Class: TCPProxy

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

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.



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/tcp_proxy.rb', line 4

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.



2
3
4
# File 'lib/tcp_proxy.rb', line 2

def connections
  @connections
end

Instance Method Details

#accept_connectionsObject



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/tcp_proxy.rb', line 114

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



37
38
39
# File 'lib/tcp_proxy.rb', line 37

def allow
  @accept_new = true
end

#blockObject



34
35
36
# File 'lib/tcp_proxy.rb', line 34

def block
  @accept_new = false
end

#closeObject



20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/tcp_proxy.rb', line 20

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
end

#drop_allObject



41
42
43
44
45
46
47
48
49
50
# File 'lib/tcp_proxy.rb', line 41

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



126
127
128
129
130
131
# File 'lib/tcp_proxy.rb', line 126

def forward_data
  connections = @connections_m.synchronize { @connections.dup }
  connections.each do |conn|
    conn.pump_synchronized
  end
end

#thread_mainObject

Inside the background thread —————————————-



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

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