Class: Bridge::TCPSocket

Inherits:
TCPSocket
  • Object
show all
Defined in:
lib/bridge/tcp_server.rb

Overview

This is the class returned by TCPServer.accept. It is a TCPSocket with a couple of extra features.

Defined Under Namespace

Classes: RetryError

Instance Method Summary collapse

Constructor Details

#initialize(cloud_host, cloud_port, listen_hosts, listen_keys) ⇒ TCPSocket

Returns a new instance of TCPSocket.



14
15
16
17
18
19
20
21
# File 'lib/bridge/tcp_server.rb', line 14

def initialize(cloud_host, cloud_port, listen_hosts, listen_keys)
  @cloud_host = cloud_host
  @cloud_port = cloud_port
  @listen_hosts = listen_hosts
  @listen_keys = listen_keys
  
  super(@cloud_host, @cloud_port)
end

Instance Method Details

#send_bridge_requestObject



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/bridge/tcp_server.rb', line 23

def send_bridge_request()
			write("BRIDGE / HTTP/1.1\r\n")
			write("Expect: 100-continue\r\n")
			@listen_hosts.each {|host|
write("Host: #{host}\r\n")
			}
			@listen_keys.each {|key|
write("Host-Key: #{key}\r\n")
			}
			write("\r\n")      
end

#setupObject

This does the full setup process on the request, returning only when the connection is actually available.



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
# File 'lib/bridge/tcp_server.rb', line 63

def setup()
    send_bridge_request
    code = nil
	name = nil
	headers = []
	while (line = gets())
		line = line.strip
		if (line == "")
			case code.to_i
			when 100 # 100 Continue, just a ping. Ignore.
				code = name = nil
				headers = []
				next
			when 101 # 101 Upgrade, successfuly got a connection.
				write("HTTP/1.1 100 Continue\r\n\r\n") # let the server know we're still here.
				return self
			when 401 # 401 Access Denied, key wasn't right.
			  close()
			  raise "HTTP BRIDGE error #{code}: host key was invalid or missing, but required."
			when 503, 504 # 503 Service Unavailable or 504 Gateway Timeout, just retry.
				close()
				sleep_time = headers.find {|header| header["Retry-After"] } || 5
				raise RetryError.new("BRIDGE server timed out or is overloaded, wait #{sleep_time}s to try again.", sleep_time)
			else
				raise "HTTP BRIDGE error #{code}: #{name} waiting for connection."
			end
		end

		if (!code && !name) # This is the initial response line
			if (match = line.match(%r{^HTTP/1\.[01] ([0-9]{3,3}) (.*)$}))
				code = match[1]
				name = match[2]
				next
			else
				raise "Parse error in BRIDGE request reply."
			end
		else
			if (match = line.match(%r{^(.+?):\s+(.+)$}))
				headers.push({match[1] => match[2]})
			else
				raise "Parse error in BRIDGE request reply's headers."
			end
		end
	end
	return nil
end

#verifyObject

This just tries to determine if the server will honor requests as specified above so that the TCPServer initializer can error out early if it won’t.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/bridge/tcp_server.rb', line 38

def verify()
  send_bridge_request()
  begin
    line = gets()
    match = line.match(%r{^HTTP/1\.[01] ([0-9]{3,3}) (.*)$})
    if (!match)
      raise "HTTP BRIDGE error: bridge server sent incorrect reply to bridge request."
    end
    case code = match[1].to_i
    when 100, 101
      return true
	when 401 # 401 Access Denied, key wasn't right.
	  raise "HTTP BRIDGE error #{code}: host key was invalid or missing, but required."
  when 503, 504 # 503 Service Unavailable or 504 Gateway Timeout
    raise "HTTP BRIDGE error #{code}: could not verify server can handle requests because it's overloaded."
   else
     raise "HTTP BRIDGE error #{code}: #{match[2]} unknown error connecting to bridge server."
    end 
  ensure
    close() # once we do this, we just assume the connection is useless.
  end
end