Class: ProxyMachine::ClientConnection

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

Class Method Summary collapse

Instance Method Summary collapse

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



74
75
76
77
78
79
80
81
# File 'lib/proxymachine/client_connection.rb', line 74

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}"
  @server_side = ServerConnection.request(host, port, self)
  @server_side.pending_connect_timeout = @connect_timeout
  @server_side.comm_inactivity_timeout = @inactivity_timeout
end

#establish_remote_serverObject

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

def establish_remote_server
  fail "establish_remote_server called with remote established" if @remote
  commands = ProxyMachine.router.call(@buffer.join)
  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]
    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

#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
end

#proxy_target_unboundObject

Proxy connection has been lost



132
133
134
# File 'lib/proxymachine/client_connection.rb', line 132

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.



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/proxymachine/client_connection.rb', line 98

def server_connection_failed
  @server_side = nil
  if @connected
    LOGGER.error "Connection with #{@remote.join(':')} was terminated prematurely."
    close_connection
    ProxyMachine.connect_error_callback.call(@remote.join(':'))
  elsif @tries < 10
    @tries += 1
    LOGGER.warn "Retrying connection with #{@remote.join(':')} (##{@tries})"
    EM.add_timer(0.1) { connect_to_server }
  else
    LOGGER.error "Connect #{@remote.join(':')} failed after ten attempts."
    close_connection
    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.



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

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.



119
120
121
122
123
124
# File 'lib/proxymachine/client_connection.rb', line 119

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

#unbindObject



126
127
128
129
# File 'lib/proxymachine/client_connection.rb', line 126

def unbind
  @server_side.close_connection_after_writing if @server_side
  ProxyMachine.decr
end