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