Class: TCPProxy

Inherits:
Object
  • Object
show all
Defined in:
lib/tcp_proxy.rb

Overview

Proxies tcp connections on a machine from one port to the next. This is used for debugging and writing specifications for cod. This is not officially part of cod’s API.

Defined Under Namespace

Classes: Connection

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host, from_port, to_port) ⇒ TCPProxy

Returns a new instance of TCPProxy.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/tcp_proxy.rb', line 9

def initialize(host, from_port, to_port)
  @ins  = TCPServer.new(host, from_port)

  @host = host
  @from_port, @to_port = from_port, to_port

  # Active connections and mutex to protect access to it.
  @connections = []
  @connections_m = Mutex.new
  
  # Are we currently accepting new connections?
  @accept_new = true
  
  @thread = Thread.start(&method(:thread_main))
end

Instance Attribute Details

#connectionsObject (readonly)

Returns the value of attribute connections.



7
8
9
# File 'lib/tcp_proxy.rb', line 7

def connections
  @connections
end

Instance Method Details

#accept_connectionsObject



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/tcp_proxy.rb', line 136

def accept_connections
  loop do
    in_sock = @ins.accept_nonblock
    out_sock = TCPSocket.new(@host, @to_port)
    
    @connections_m.synchronize {
      @connections << Connection.new(in_sock, out_sock) }
  end
rescue Errno::EAGAIN
  # No more connections pending, stop accepting new connections
end

#allowObject

Allows new connections to be made. This is the default, so you only need this to reenable connections after a #block.



52
53
54
55
# File 'lib/tcp_proxy.rb', line 52

def allow
  @ins  = TCPServer.new(@host, @from_port)
  @accept_new = true
end

#blockObject

Disallows new connections.



43
44
45
46
47
# File 'lib/tcp_proxy.rb', line 43

def block
  @accept_new = false
  @ins.close
  @ins = nil
end

#closeObject

Closes the proxy, disrupting every connection made to it.



27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/tcp_proxy.rb', line 27

def close
  @shutdown = true
  
  @thread.join
  @thread = nil
  
  # Since the thread is stopped now, we can be sure no new connections are
  # accepted. This is why we access the collection without locking.
  @connections.each do |connection|
    connection.close
  end
  @ins.close if @ins
end

#drop_allObject

Drops all established connections.



59
60
61
62
63
64
65
66
67
68
# File 'lib/tcp_proxy.rb', line 59

def drop_all
  # Copy the connections and then empty the collection
  connections = @connections_m.synchronize {
    @connections.tap { 
      @connections = [] } }
  
  connections.each do |conn|
    conn.close
  end
end

#forward_dataObject



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/tcp_proxy.rb', line 149

def forward_data
  connections = @connections_m.synchronize { @connections.dup }
  remove_list = []
  connections.each do |conn|
    begin
      conn.pump_synchronized
    rescue EOFError
      # Socket was closed, remove from the collection.
      remove_list << conn
    end
  end

  @connections_m.synchronize {
    @connections -= remove_list
  }
end

#thread_mainObject

Internal thread that pumps messages from and to ports.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/tcp_proxy.rb', line 74

def thread_main
  loop do
    accept_connections if @accept_new
    
    forward_data
    
    break if @shutdown
  end
rescue Exception => ex
  p [:uncaught, ex]
  ex.backtrace.each do |line|
    puts line
  end
  raise
end