Class: TunnelRb::Server::ControlServer
- Inherits:
-
Object
- Object
- TunnelRb::Server::ControlServer
- Defined in:
- lib/tunnel_rb/server/control_server.rb
Overview
Owns the control plane: port 7777 listener, handshake handler, the IO.select-based read loop that consumes pong/disconnect events, and the ping loop that evicts unresponsive clients.
Constant Summary collapse
- PING_INTERVAL =
seconds; keeps NAT mappings alive
30- PONG_MISSES =
3- HANDSHAKE_POOL_SIZE =
64- HANDSHAKE_QUEUE_SIZE =
64- READ_CHUNK =
4096- LINE_MAX =
drop clients that send oversized lines without n
16 * 1024
- SELECT_TIMEOUT =
1.0- IDLE_SLEEP =
when no clients are connected
0.5
Instance Method Summary collapse
-
#initialize(port:, domain:, registry:, token_store:, pending_connections:, logger:, url_port: nil, tls_context: nil) ⇒ ControlServer
constructor
A new instance of ControlServer.
- #ping_loop ⇒ Object
-
#read_loop ⇒ Object
IO.select over all registered control sockets.
-
#start ⇒ Object
Blocking accept loop.
- #stop ⇒ Object
- #tls? ⇒ Boolean
Constructor Details
#initialize(port:, domain:, registry:, token_store:, pending_connections:, logger:, url_port: nil, tls_context: nil) ⇒ ControlServer
Returns a new instance of ControlServer.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/tunnel_rb/server/control_server.rb', line 25 def initialize(port:, domain:, registry:, token_store:, pending_connections:, logger:, url_port: nil, tls_context: nil) @port = port @domain = domain @url_port = url_port @registry = registry @token_store = token_store @pending_connections = pending_connections @tls_context = tls_context @logger = logger @pool = ThreadPool.new(HANDSHAKE_POOL_SIZE, max_queue: HANDSHAKE_QUEUE_SIZE) @stopping = false @server = nil end |
Instance Method Details
#ping_loop ⇒ Object
84 85 86 87 88 |
# File 'lib/tunnel_rb/server/control_server.rb', line 84 def ping_loop while interruptible_sleep(PING_INTERVAL) tick_pings end end |
#read_loop ⇒ Object
IO.select over all registered control sockets. One thread serves all clients, so the count of long-lived ping/pong loops doesn’t grow with the number of registered tunnels.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/tunnel_rb/server/control_server.rb', line 63 def read_loop until @stopping sockets = @registry.sockets if sockets.empty? sleep IDLE_SLEEP next end begin readable, = IO.select(sockets, nil, nil, SELECT_TIMEOUT) rescue IOError, Errno::EBADF # A socket was closed (e.g. shutdown) while we were waiting on it. # Drop it from our snapshot and re-loop to rebuild the set. next end next unless readable readable.each { |socket| handle_readable(socket) } end end |
#start ⇒ Object
Blocking accept loop. Returns when stop is called.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/tunnel_rb/server/control_server.rb', line 45 def start tcp_server = TCPServer.new("0.0.0.0", @port) @server = tls? ? TLS.wrap_listener(tcp_server, @tls_context) : tcp_server @logger.info "🎧 Control server listening for clients on #{@port}" loop do socket = @server.accept @pool.submit { handle_connection(socket) } rescue IOError, Errno::EBADF break if @stopping raise end end |
#stop ⇒ Object
90 91 92 93 94 95 |
# File 'lib/tunnel_rb/server/control_server.rb', line 90 def stop @stopping = true @server&.close rescue nil @pool.shutdown @registry.sockets.each { |s| s.close rescue nil } end |
#tls? ⇒ Boolean
40 41 42 |
# File 'lib/tunnel_rb/server/control_server.rb', line 40 def tls? !@tls_context.nil? end |