Class: Hyperion::Server
- Inherits:
-
Object
- Object
- Hyperion::Server
- Defined in:
- lib/hyperion/server.rb
Overview
Phase 2a server: bind a TCPServer, accept connections, schedule each on its own fiber via Async. Multiple in-flight requests run concurrently on a single OS thread. Keep-alive is still off — connection closes after one request (Phase 2b will add keep-alive).
Phase 7 (scoped): when ‘tls:` is supplied, wrap the listener in an OpenSSL::SSL::SSLServer with ALPN advertising `h2` + `http/1.1`. After the handshake, dispatch on the negotiated protocol — http/1.1 goes through Connection (real path); h2 goes to Http2Handler (505 stub until Phase 8).
Constant Summary collapse
- DEFAULT_READ_TIMEOUT_SECONDS =
30- DEFAULT_THREAD_COUNT =
5
Instance Attribute Summary collapse
-
#host ⇒ Object
readonly
Returns the value of attribute host.
-
#port ⇒ Object
readonly
Returns the value of attribute port.
Instance Method Summary collapse
-
#adopt_listener(sock) ⇒ Object
Phase 3: workers pass in a pre-bound, SO_REUSEPORT-set socket built by Hyperion::Worker.
-
#initialize(app:, host: '127.0.0.1', port: 9292, read_timeout: DEFAULT_READ_TIMEOUT_SECONDS, tls: nil, thread_count: DEFAULT_THREAD_COUNT) ⇒ Server
constructor
A new instance of Server.
- #listen ⇒ Object
- #run_one ⇒ Object
- #start ⇒ Object
- #stop ⇒ Object
Constructor Details
#initialize(app:, host: '127.0.0.1', port: 9292, read_timeout: DEFAULT_READ_TIMEOUT_SECONDS, tls: nil, thread_count: DEFAULT_THREAD_COUNT) ⇒ Server
Returns a new instance of Server.
25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/hyperion/server.rb', line 25 def initialize(app:, host: '127.0.0.1', port: 9292, read_timeout: DEFAULT_READ_TIMEOUT_SECONDS, tls: nil, thread_count: DEFAULT_THREAD_COUNT) @host = host @port = port @app = app @read_timeout = read_timeout @tls = tls @thread_count = thread_count @thread_pool = nil @stopped = false end |
Instance Attribute Details
#host ⇒ Object (readonly)
Returns the value of attribute host.
23 24 25 |
# File 'lib/hyperion/server.rb', line 23 def host @host end |
#port ⇒ Object (readonly)
Returns the value of attribute port.
23 24 25 |
# File 'lib/hyperion/server.rb', line 23 def port @port end |
Instance Method Details
#adopt_listener(sock) ⇒ Object
Phase 3: workers pass in a pre-bound, SO_REUSEPORT-set socket built by Hyperion::Worker. Bypasses #listen but keeps the rest of the accept loop intact since Socket and TCPServer both quack #accept_nonblock.
Phase 8: when ‘tls:` was supplied to the constructor, also build the SSL context here so the accept loop can wrap incoming connections. Each worker builds its own context — they don’t share state.
61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/hyperion/server.rb', line 61 def adopt_listener(sock) @server = sock @tcp_server = sock @port = case sock when ::TCPServer sock.addr[1] else sock.local_address.ip_port end @ssl_ctx = TLS.context(cert: @tls[:cert], key: @tls[:key]) if @tls self end |
#listen ⇒ Object
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/hyperion/server.rb', line 37 def listen tcp = ::TCPServer.new(@host, @port) @port = tcp.addr[1] if @tls @ssl_ctx = TLS.context(cert: @tls[:cert], key: @tls[:key]) ssl_server = ::OpenSSL::SSL::SSLServer.new(tcp, @ssl_ctx) ssl_server.start_immediately = false @server = ssl_server @tcp_server = tcp else @server = tcp @tcp_server = tcp end self end |
#run_one ⇒ Object
74 75 76 77 78 79 80 81 82 |
# File 'lib/hyperion/server.rb', line 74 def run_one Async do socket = blocking_accept next unless socket apply_timeout(socket) dispatch(socket) end.wait end |
#start ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/hyperion/server.rb', line 84 def start listen unless @server @thread_pool = ThreadPool.new(size: @thread_count) if @thread_count.positive? Async do |task| until @stopped socket = accept_or_nil next unless socket apply_timeout(socket) # Plain HTTP/1.1 with a pool: submit straight to the worker — no # fiber wrap needed (submit_connection returns immediately and the # worker thread owns the connection for its lifetime). # TLS still goes through a fiber: ALPN negotiation determines h2 # vs http/1.1, and h2 needs the fiber because each stream is its # own fiber inside Http2Handler. if @thread_pool && !@tls @thread_pool.submit_connection(socket, @app) else task.async { dispatch(socket) } end end end ensure @thread_pool&.shutdown end |
#stop ⇒ Object
111 112 113 114 115 116 |
# File 'lib/hyperion/server.rb', line 111 def stop @stopped = true @server&.close @server = nil @tcp_server = nil end |