Class: Rack::Tunnel

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

Defined Under Namespace

Classes: Error

Constant Summary collapse

VERSION =
'0.1.1'
HEADER =
'X-Tunnel-Uri'

Instance Method Summary collapse

Constructor Details

#initialize(app, uri) ⇒ Tunnel

Returns a new instance of Tunnel.



10
11
12
13
14
15
16
# File 'lib/rack/tunnel.rb', line 10

def initialize(app, uri)
  @app, @uri = app, URI.parse(uri)

  if @uri.port <= 1024 && @uri.user != 'root'
    raise Error, "root user required for port #{@uri.port}"
  end
end

Instance Method Details

#call(env) ⇒ Object



18
19
20
21
# File 'lib/rack/tunnel.rb', line 18

def call(env)
  establish_tunnel(env['SERVER_PORT']) unless @tunnel_established
  @app.call(env.merge(HEADER => @uri.to_s))
end

#command(local_port) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/rack/tunnel.rb', line 35

def command(local_port)
  cmd = %w"ssh"
  cmd << "-R" << "0.0.0.0:#{@uri.port}:localhost:#{local_port}"
  cmd << @uri.host
  cmd << "-l" << @uri.user
  cmd << "-o" << "TCPKeepAlive=yes"
  cmd << "-o" << "ServerAliveInterval=30"
  cmd << "-o" << "ServerAliveCountMax=10"
  cmd << "-o" << "GatewayPorts=yes"
  cmd << "-N"

  cmd
end

#establish_tunnel(local_port) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/rack/tunnel.rb', line 23

def establish_tunnel(local_port)
  pid, _ = Open4.popen4(*command(local_port))

  at_exit do
    Process.kill(9, pid)
    Process.wait
  end

      wait_for_tunnel
  @tunnel_established = true
end

#wait_for_tunnelObject



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/rack/tunnel.rb', line 49

def wait_for_tunnel
  Timeout::timeout(30) do
    begin
      return TCPSocket.new(@uri.host, @uri.port)
    rescue Errno::ECONNREFUSED
      retry
    rescue => e
      raise Error, e.message
    end
  end
end