Class: Kontena::LocalhostWebServer

Inherits:
Object
  • Object
show all
Defined in:
lib/kontena/cli/localhost_web_server.rb

Constant Summary collapse

DEFAULT_ERROR_MESSAGE =
"Bad request"
SUCCESS_URL =
"https://cloud.kontena.io/terminal-success"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(success_response: nil, error_response: nil, port: nil) ⇒ LocalhostWebServer

Get new server instance

Parameters:

  • success_response (String) (defaults to: nil)

    Returned for successful callback

  • error_response (String) (defaults to: nil)

    Returned for unsuccessful callback



26
27
28
29
30
31
# File 'lib/kontena/cli/localhost_web_server.rb', line 26

def initialize(success_response: nil, error_response: nil, port: nil)
  @success_response = success_response
  @error_response   = error_response   || DEFAULT_ERROR_MESSAGE
  @server = TCPServer.new('localhost', port || 0)
  @port = @server.addr[1]
end

Instance Attribute Details

#error_responseObject

Serves one request to localhost:<random_port>/cb

Used for local webserver browser authentication flow.

Examples:

server = LocalhostWebServer.new
server.url
 => "http://localhost:1234/cb"
response = server.serve_one
# <visit server.url?foo=bar&bar=123 on browser>
 => { "foo" => "bar", "bar" => 123 }  # (it converts integers!)


17
18
19
# File 'lib/kontena/cli/localhost_web_server.rb', line 17

def error_response
  @error_response
end

#portObject

Serves one request to localhost:<random_port>/cb

Used for local webserver browser authentication flow.

Examples:

server = LocalhostWebServer.new
server.url
 => "http://localhost:1234/cb"
response = server.serve_one
# <visit server.url?foo=bar&bar=123 on browser>
 => { "foo" => "bar", "bar" => 123 }  # (it converts integers!)


17
18
19
# File 'lib/kontena/cli/localhost_web_server.rb', line 17

def port
  @port
end

#serverObject

Serves one request to localhost:<random_port>/cb

Used for local webserver browser authentication flow.

Examples:

server = LocalhostWebServer.new
server.url
 => "http://localhost:1234/cb"
response = server.serve_one
# <visit server.url?foo=bar&bar=123 on browser>
 => { "foo" => "bar", "bar" => 123 }  # (it converts integers!)


17
18
19
# File 'lib/kontena/cli/localhost_web_server.rb', line 17

def server
  @server
end

#success_responseObject

Serves one request to localhost:<random_port>/cb

Used for local webserver browser authentication flow.

Examples:

server = LocalhostWebServer.new
server.url
 => "http://localhost:1234/cb"
response = server.serve_one
# <visit server.url?foo=bar&bar=123 on browser>
 => { "foo" => "bar", "bar" => 123 }  # (it converts integers!)


17
18
19
# File 'lib/kontena/cli/localhost_web_server.rb', line 17

def success_response
  @success_response
end

Instance Method Details

#serve_oneHash

Serve one request and return query params.

Returns:

  • (Hash)

    query_params



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/kontena/cli/localhost_web_server.rb', line 41

def serve_one
  Kontena.logger.debug("LHWS") { "Waiting for connection on port #{port}.." }
  socket = server.accept

  content = socket.recvfrom(2048).first.split(/(?:\r)?\n/)

  request = content.shift

  headers = {}
  while line = content.shift
    break if line.nil?
    break if line == ''
    header, value = line.chomp.split(/:\s{0,}/, 2)
    headers[header] = value
  end

  body = content.join("\n")

  Kontena.logger.debug("LHWS") { "Got request: \"#{request.inspect}\n  Headers: #{headers.inspect}\n  Body: #{body}\"" }

  get_request = request[/GET (\/cb.+?) HTTP/, 1]
  if get_request
    if success_response
      socket.print [
        'HTTP/1.1 200 OK',
        'Content-Type: text/html',
        "Content-Length: #{success_response.bytesize}",
        "Connection: close",
        '',
        success_response
      ].join("\r\n")
    else
      socket.print [
        'HTTP/1.1 302 Found',
        "Location: #{SUCCESS_URL}",
        "Referrer-Policy: no-referrer",
        "Connection: close",
        ''
      ].join("\r\n")
    end

    socket.close
    server.close
    uri = URI.parse("http://localhost#{get_request}")
    Kontena.logger.debug("LHWS") { "  * Parsing params: \"#{uri.query}\"" }
    params = {}
    URI.decode_www_form(uri.query).each do |key, value|
      if value.to_s == ''
        next
      elsif value.to_s =~ /\A\d+\z/
        params[key] = value.to_i
      else
        params[key] = value
      end
    end
    params
  else
    # Unless it's a query to /cb, send an error message and keep listening,
    # it might have been something funny like fetching favicon.ico
    socket.print [
      'HTTP/1.1 400 Bad request',
      'Content-Type: text/plain',
      "Content-Length: #{error_response.bytesize}",
      'Connection: close',
      '',
      error_response
    ].join("\r\n")
    socket.close
    serve_one # serve more, this one was not proper.
  end
end

#urlObject

The url to this service, send this as redirect_uri to auth provider.



34
35
36
# File 'lib/kontena/cli/localhost_web_server.rb', line 34

def url
  "http://localhost:#{port}/cb"
end