Class: Raptor::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/raptor/server.rb,
sig/generated/raptor/server.rbs

Overview

High-performance HTTP server that accepts connections and dispatches them.

Server manages the main accept loop, handling incoming client connections from bound sockets. It uses IO.select for efficient polling and implements automatic load balancing by checking reactor backlog before accepting connections, providing natural backpressure based on system capacity.

Supports TCP, Unix domain, and SSL listeners transparently. TCP_NODELAY is applied only to TCP sockets, and SSL handshakes are performed synchronously before the connection is dispatched.

For HTTP/1.1 connections the first request is parsed inline on the server thread and dispatched directly to the thread pool, falling back to the reactor only when more data is needed. For HTTP/2 connections (negotiated via ALPN) the server sends initial SETTINGS and registers the connection with the reactor for frame processing through the ractor pool.

Examples:

binder = Binder.new(["tcp://0.0.0.0:3000"])
reactor = Reactor.new(thread_pool, ractor_pool, client_options: {})
request = Request.new(app, 3000)
server = Server.new(binder, reactor, thread_pool, request)
server.run
# ... later
server.shutdown

Constant Summary collapse

HTTP_SCHEME =

Returns:

  • (::String)
"http"
HTTPS_SCHEME =

Returns:

  • (::String)
"https"
H2_PROTOCOL =

Returns:

  • (::String)
"h2"

Instance Method Summary collapse

Constructor Details

#initialize(binder, reactor, thread_pool, request) ⇒ Server

Creates a new Server instance.

Parameters:

  • binder (Binder)

    the binder managing listening sockets

  • reactor (Reactor)

    the reactor for handling client connections

  • thread_pool (AtomicThreadPool)

    thread pool for application processing

  • request (Request)

    the HTTP/1.1 request handler



55
56
57
58
59
60
61
# File 'lib/raptor/server.rb', line 55

def initialize(binder, reactor, thread_pool, request)
  @binder = binder
  @reactor = reactor
  @thread_pool = thread_pool
  @request = request
  @running = AtomicBoolean.new(true)
end

Instance Method Details

#accept_connection(listener, reactor) ⇒ void

This method returns an undefined value.

Accepts a connection from the given listener and dispatches it.

For SSL connections with h2 negotiated via ALPN, the server sends initial SETTINGS and adds the connection to the reactor as an HTTP/2 connection. All other connections follow the HTTP/1.1 path.

Parameters:

  • listener (TCPServer, UNIXServer, Binder::SslListener)

    the ready listener

  • reactor (Reactor)

    the reactor to dispatch connections to



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/raptor/server.rb', line 120

def accept_connection(listener, reactor)
  tcp_client = begin
    listener.is_a?(Binder::SslListener) ? listener.tcp_server.accept_nonblock : listener.accept_nonblock
  rescue IO::WaitReadable
    return
  end

  if tcp_client.is_a?(TCPSocket)
    tcp_client.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
    remote_addr = tcp_client.remote_address.ip_address
  else
    remote_addr = "127.0.0.1"
  end

  url_scheme = HTTP_SCHEME
  client = tcp_client

  if listener.is_a?(Binder::SslListener)
    url_scheme = HTTPS_SCHEME
    begin
      ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_client, listener.ssl_context)
      ssl_socket.sync_close = true
      ssl_socket.accept
      client = ssl_socket
    rescue OpenSSL::SSL::SSLError => error
      warn "SSL handshake failed: #{error.message}"
      tcp_client.close rescue nil
      return
    end

    if ssl_socket.alpn_protocol == H2_PROTOCOL
      ssl_socket.write(Http2.build_server_settings_frame) rescue nil

      reactor.add(
        id: ssl_socket.object_id,
        socket: ssl_socket,
        remote_addr: remote_addr,
        url_scheme: HTTPS_SCHEME,
        protocol: :http2,
        writer: Http2::Writer.new
      )

      return
    end
  end

  @request.eager_accept(
    client,
    client.object_id,
    reactor,
    @thread_pool,
    remote_addr,
    url_scheme
  )
end

#runThread

Starts the server's main accept loop in a new thread.

The accept loop polls listening sockets for ready connections and accepts them when system capacity allows. It checks reactor backlog before accepting to prevent overload. This provides natural load balancing across multiple worker processes through backpressure control.

Returns:

  • (Thread)

    the thread running the accept loop



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/raptor/server.rb', line 73

def run
  Thread.new(@binder.listeners, @reactor, @running) do |server_sockets, reactor, running|
    Thread.current.name = self.class.name

    while running.true?
      begin
        ready_servers, _, _ = IO.select(server_sockets, nil, nil, 1)
      rescue IOError, Errno::EBADF
        break
      end

      next unless ready_servers
      next if @reactor.backlog >= (@thread_pool.size * 1.2).ceil

      ready_servers.each do |listener|
        accept_connection(listener, reactor)
      end
    end
  end
end

#shutdownvoid

This method returns an undefined value.

Gracefully shuts down the server.

Stops accepting new connections and closes all listening sockets. The server thread will exit after handling any in-flight accept operations.



102
103
104
105
# File 'lib/raptor/server.rb', line 102

def shutdown
  @running.make_false
  @binder.close
end