Class: ProxyMachine::ClientConnection

Inherits:
EventMachine::Connection
  • Object
show all
Defined in:
lib/proxymachine/client_connection.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#inactivity_warning_callbackObject (readonly)

Returns the value of attribute inactivity_warning_callback.



48
49
50
# File 'lib/proxymachine/client_connection.rb', line 48

def inactivity_warning_callback
  @inactivity_warning_callback
end

#inactivity_warning_timeoutObject (readonly)

Returns the value of attribute inactivity_warning_timeout.



48
49
50
# File 'lib/proxymachine/client_connection.rb', line 48

def inactivity_warning_timeout
  @inactivity_warning_timeout
end

Class Method Details

.start(host, port) ⇒ Object



3
4
5
6
7
8
# File 'lib/proxymachine/client_connection.rb', line 3

def self.start(host, port)
  $server = EM.start_server(host, port, self)
  $logger.info "Listening on #{host}:#{port}"
  $logger.info "Send QUIT to quit after waiting for all connections to finish."
  $logger.info "Send TERM or INT to quit after waiting for up to 10 seconds for connections to finish."
end

Instance Method Details

#connect_to_serverObject

Connect to the remote server



85
86
87
88
89
90
91
92
93
94
95
# File 'lib/proxymachine/client_connection.rb', line 85

def connect_to_server
  fail "connect_server called without remote established" if @remote.nil?
  host, port = @remote
  $logger.info "Establishing new connection with #{host}:#{port}"
  cb = @commands[:callback]
  klass = cb ? CallbackServerConnection : WarningServerConnection
  @server_side = klass.request(host, port, self)
  @server_side.callback = cb if cb
  @server_side.pending_connect_timeout = @connect_timeout
  @server_side.comm_inactivity_timeout = @inactivity_timeout
end

#establish_remote_server(routes = nil) ⇒ Object

Called when new data is available from the client but no remote server has been established. If a remote can be established, an attempt is made to connect and proxy to the remote server.



42
43
44
45
46
# File 'lib/proxymachine/client_connection.rb', line 42

def establish_remote_server(routes = nil)
  fail "establish_remote_server called with remote established" if @remote
  @routes = [routes || ProxyMachine.router.call(@buffer.join, self) || {}].flatten
  try_connect
end

#inactivity_warning_triggeredObject



108
109
110
111
112
# File 'lib/proxymachine/client_connection.rb', line 108

def inactivity_warning_triggered
  proc {
    (@inactivity_warning_callback || ProxyMachine.inactivity_warning_callback).call(@remote.join(':'))
  }
end

#peerObject



21
22
23
24
25
26
27
# File 'lib/proxymachine/client_connection.rb', line 21

def peer
  @peer ||=
  begin
    port, ip = Socket.unpack_sockaddr_in(get_peername)
    "#{ip}:#{port}"
  end
end

#post_initObject



10
11
12
13
14
15
16
17
18
19
# File 'lib/proxymachine/client_connection.rb', line 10

def post_init
  $logger.info "Accepted #{peer}"
  @buffer = []
  @remote = nil
  @tries = 0
  @connected = false
  @connect_timeout = nil
  @inactivity_timeout = nil
  ProxyMachine.incr(self)
end

#proxy_target_unboundObject

Proxy connection has been lost



152
153
154
# File 'lib/proxymachine/client_connection.rb', line 152

def proxy_target_unbound
  @server_side = nil
end

#receive_data(data) ⇒ Object



29
30
31
32
33
34
35
36
37
# File 'lib/proxymachine/client_connection.rb', line 29

def receive_data(data)
  if !@connected
    @buffer << data
    establish_remote_server if @remote.nil?
  end
rescue => e
  close_connection
  $logger.error "#{e.class} - #{e.message}"
end

#server_connection_failedObject

Called by the server side when a connection could not be established, either due to a hard connection failure or to a connection timeout. Leave the client connection open and retry the server connection up to 10 times.



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/proxymachine/client_connection.rb', line 118

def server_connection_failed
  @server_side = nil
  if @connected
    $logger.error "Connection with #{@remote.join(':')} was terminated prematurely."
    close_connection
    (@connect_error_callback || ProxyMachine.connect_error_callback).call(@remote.join(':'))
  elsif @routes.size > 0
    @tries += 1
    $logger.warn "Retrying connection with #{@remote.join(':')} (##{@tries})"
    EM.add_timer(0.1) { try_connect }
  else
    $logger.error "Connect #{@remote.join(':')} failed after exhausting failovers."
    close_connection
    (@connect_error_callback || ProxyMachine.connect_error_callback).call(@remote.join(':'))
  end
end

#server_connection_successObject

Called by the server side immediately after the server connection was successfully established. Send any buffer we’ve accumulated and start raw proxying.



100
101
102
103
104
105
106
# File 'lib/proxymachine/client_connection.rb', line 100

def server_connection_success
  $logger.info "Successful connection to #{@remote.join(':')}"
  @connected = true
  @buffer.each { |data| @server_side.send_data(data) }
  @buffer = []
  proxy_incoming_to(@server_side, 10240)
end

#server_inactivity_timeout(timeout, elapsed) ⇒ Object

Called by the server when an inactivity timeout is detected. The timeout argument is the configured inactivity timeout in seconds as a float; the elapsed argument is the amount of time that actually elapsed since connecting but not receiving any data.



139
140
141
142
143
144
# File 'lib/proxymachine/client_connection.rb', line 139

def server_inactivity_timeout(timeout, elapsed)
  $logger.error "Disconnecting #{@remote.join(':')} after #{elapsed}s of inactivity (> #{timeout.inspect})"
  @server_side = nil
  close_connection
  (@inactivity_error_callback || ProxyMachine.inactivity_error_callback).call(@remote.join(':'))
end

#try_connectObject



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
# File 'lib/proxymachine/client_connection.rb', line 50

def try_connect
  @commands = @routes.shift
  $logger.info "#{peer} #{@commands.inspect}"
  close_connection unless @commands.instance_of?(Hash)
  if remote = @commands[:remote]
    m, host, port = *remote.match(/^(.+):(.+)$/)
    @remote = [host, port]
    if data = @commands[:data]
      @buffer = [data]
    end
    if reply = @commands[:reply]
      send_data(reply)
    end
    @connect_timeout = @commands[:connect_timeout]
    @inactivity_timeout = @commands[:inactivity_timeout]
    @inactivity_warning_timeout = @commands[:inactivity_warning_timeout]
    @connect_error_callback = @commands[:connect_error_callback]
    @inactivity_error_callback = @commands[:inactivity_error_callback]
    @inactivity_warning_callback = @commands[:inactivity_warning_callback]
    connect_to_server
  elsif close = @commands[:close]
    if close == true
      close_connection
    else
      send_data(close)
      close_connection_after_writing
    end
  elsif @commands[:noop]
    # do nothing
  else
    close_connection
  end
end

#unbindObject



146
147
148
149
# File 'lib/proxymachine/client_connection.rb', line 146

def unbind
  @server_side.close_connection_after_writing if @server_side
  ProxyMachine.decr(self)
end