raptor 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/raptor/server.rb CHANGED
@@ -15,16 +15,19 @@ module Raptor
15
15
  #
16
16
  # Supports TCP, Unix domain, and SSL listeners transparently. TCP_NODELAY is
17
17
  # applied only to TCP sockets, and SSL handshakes are performed synchronously
18
- # before handing the connection to the reactor.
18
+ # before the connection is dispatched.
19
19
  #
20
- # For SSL connections, ALPN negotiation determines the protocol. HTTP/2
21
- # connections are added to the reactor with initial SETTINGS and processed
22
- # through the same ractor pool pipeline as HTTP/1.1 connections.
20
+ # For HTTP/1.1 connections the first request is parsed inline on the server
21
+ # thread and dispatched directly to the thread pool, falling back to the
22
+ # reactor only when more data is needed. For HTTP/2 connections (negotiated
23
+ # via ALPN) the server sends initial SETTINGS and registers the connection
24
+ # with the reactor for frame processing through the ractor pool.
23
25
  #
24
26
  # @example
25
27
  # binder = Binder.new(["tcp://0.0.0.0:3000"])
26
28
  # reactor = Reactor.new(thread_pool, ractor_pool, client_options: {})
27
- # server = Server.new(binder, reactor, thread_pool)
29
+ # request = Request.new(app, 3000)
30
+ # server = Server.new(binder, reactor, thread_pool, request)
28
31
  # server.run
29
32
  # # ... later
30
33
  # server.shutdown
@@ -37,6 +40,7 @@ module Raptor
37
40
  # @rbs @binder: Binder
38
41
  # @rbs @reactor: Reactor
39
42
  # @rbs @thread_pool: AtomicThreadPool
43
+ # @rbs @request: Request
40
44
  # @rbs @running: AtomicBoolean
41
45
 
42
46
  # Creates a new Server instance.
@@ -44,13 +48,15 @@ module Raptor
44
48
  # @param binder [Binder] the binder managing listening sockets
45
49
  # @param reactor [Reactor] the reactor for handling client connections
46
50
  # @param thread_pool [AtomicThreadPool] thread pool for application processing
51
+ # @param request [Request] the HTTP/1.1 request handler
47
52
  # @return [void]
48
53
  #
49
- # @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool) -> void
50
- def initialize(binder, reactor, thread_pool)
54
+ # @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Request request) -> void
55
+ def initialize(binder, reactor, thread_pool, request)
51
56
  @binder = binder
52
57
  @reactor = reactor
53
58
  @thread_pool = thread_pool
59
+ @request = request
54
60
  @running = AtomicBoolean.new(true)
55
61
  end
56
62
 
@@ -149,18 +155,21 @@ module Raptor
149
155
  socket: ssl_socket,
150
156
  remote_addr: remote_addr,
151
157
  url_scheme: HTTPS_SCHEME,
152
- protocol: :http2
158
+ protocol: :http2,
159
+ writer: Http2::Writer.new
153
160
  )
154
161
 
155
162
  return
156
163
  end
157
164
  end
158
165
 
159
- reactor.add(
160
- id: client.object_id,
161
- socket: client,
162
- remote_addr: remote_addr,
163
- url_scheme: url_scheme
166
+ @request.eager_accept(
167
+ client,
168
+ client.object_id,
169
+ reactor,
170
+ @thread_pool,
171
+ remote_addr,
172
+ url_scheme
164
173
  )
165
174
  end
166
175
  end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Raptor
5
- VERSION = "0.1.0"
5
+ VERSION = "0.3.0"
6
6
  end
data/lib/raptor.rb CHANGED
@@ -5,9 +5,9 @@ require_relative "raptor/version"
5
5
 
6
6
  # Main module for the Raptor web server.
7
7
  #
8
- # Raptor is a high-performance, multi-threaded, multi-process Ruby web server that
9
- # leverages Ractors for parallel HTTP/1.1 and HTTP/2 request processing, native C
10
- # extensions for HTTP parsing and HPACK compression, and NIO for non-blocking I/O.
8
+ # Raptor is a high-performance, preloading, multi-process, multi-threaded Ruby 4+ web server
9
+ # implementing Rack 3+, leveraging Ractors for parallel HTTP/1.1 and HTTP/2 request processing,
10
+ # native C extensions for HTTP parsing and HPACK compression, and NIO for non-blocking I/O.
11
11
  #
12
12
  module Raptor
13
13
  end
@@ -0,0 +1,40 @@
1
+ # Generated from lib/rackup/handler/raptor.rb with RBS::Inline
2
+
3
+ module Rackup
4
+ module Handler
5
+ # Rack handler for booting Raptor through Rackup, `rails server`, or any
6
+ # other host that follows the Rack handler protocol.
7
+ module Raptor
8
+ DEFAULT_OPTIONS: untyped
9
+
10
+ # Boots a Raptor cluster serving the given Rack application.
11
+ #
12
+ # @param app [#call] the Rack application to serve
13
+ # @param options [Hash] handler options provided by Rackup or the host
14
+ # @yield [cluster] the cluster instance, before it starts running
15
+ # @return [void]
16
+ #
17
+ # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, **untyped options) { (::Raptor::Cluster) -> void } -> void
18
+ def self.run: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, **untyped options) { (::Raptor::Cluster) -> void } -> void
19
+
20
+ # Returns the handler-specific options surfaced by `rackup --help`.
21
+ #
22
+ # @return [Hash{String => String}] option spec to description mapping
23
+ #
24
+ # @rbs () -> Hash[String, String]
25
+ def self.valid_options: () -> Hash[String, String]
26
+
27
+ # Builds a Raptor cluster options hash from Rack handler options.
28
+ #
29
+ # Options not explicitly supplied by the user (per the `:user_supplied_options`
30
+ # key) are treated as host-provided defaults.
31
+ #
32
+ # @param app [#call] the Rack application to serve
33
+ # @param options [Hash] handler options provided by Rackup or the host
34
+ # @return [Hash{Symbol => untyped}] cluster configuration
35
+ #
36
+ # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
37
+ def self.build_cluster_options: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
38
+ end
39
+ end
40
+ end
@@ -20,6 +20,19 @@ module Raptor
20
20
 
21
21
  DEFAULT_OPTIONS: untyped
22
22
 
23
+ # Loads a configuration file and returns the hash it evaluates to.
24
+ #
25
+ # The file is evaluated at the top level so constants like `Raptor::*` resolve
26
+ # the same as in a regular Ruby script. The final expression must be a Hash
27
+ # of cluster options (the same keys accepted by {Raptor::Cluster#initialize}).
28
+ #
29
+ # @param path [String] path to a Ruby file that evaluates to a Hash
30
+ # @return [Hash{Symbol => untyped}] cluster options
31
+ # @raise [ArgumentError] if the file does not evaluate to a Hash
32
+ #
33
+ # @rbs (String path) -> Hash[Symbol, untyped]
34
+ def self.load_config_file: (String path) -> Hash[Symbol, untyped]
35
+
23
36
  @command: Symbol
24
37
 
25
38
  @options: Hash[Symbol, untyped]
@@ -61,6 +74,30 @@ module Raptor
61
74
  # @rbs () -> void
62
75
  def run_stats: () -> void
63
76
 
77
+ # Scans argv for a `-c`/`--config` flag and returns the configured path.
78
+ #
79
+ # The pre-scan runs before the main OptionParser pass so the config file
80
+ # can be applied as a base layer that CLI args then override. All four
81
+ # OptionParser-accepted forms (`-c PATH`, `-cPATH`, `--config PATH`,
82
+ # `--config=PATH`) are recognized.
83
+ #
84
+ # @param argv [Array<String>] command-line arguments to scan
85
+ # @return [String, nil] the config path, or nil if no flag was supplied
86
+ #
87
+ # @rbs (Array[String] argv) -> String?
88
+ def extract_config_path: (Array[String] argv) -> String?
89
+
90
+ # Loads a config file and merges it into `@options` over the defaults.
91
+ #
92
+ # Top-level keys replace defaults; the nested `:client` hash is merged
93
+ # key-by-key so a config file does not need to restate every client option.
94
+ #
95
+ # @param path [String, nil] path to the config file, or nil to no-op
96
+ # @return [void]
97
+ #
98
+ # @rbs (String? path) -> void
99
+ def apply_config_file: (String? path) -> void
100
+
64
101
  # Creates the OptionParser instance with all supported command-line options.
65
102
  #
66
103
  # @return [OptionParser] configured option parser
@@ -37,7 +37,9 @@ module Raptor
37
37
  # @rbs (Hash[Symbol, untyped] options) -> void
38
38
  def self.run: (Hash[Symbol, untyped] options) -> void
39
39
 
40
- @stats_file: String?
40
+ @phased_restarting: bool
41
+
42
+ @phased_restart_requested: bool
41
43
 
42
44
  @stats: Stats
43
45
 
@@ -51,6 +53,12 @@ module Raptor
51
53
 
52
54
  @binder: Binder
53
55
 
56
+ @pidfile: String?
57
+
58
+ @stats_file: String?
59
+
60
+ @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
61
+
54
62
  @client_options: Hash[Symbol, Integer]
55
63
 
56
64
  @worker_count: Integer
@@ -70,8 +78,12 @@ module Raptor
70
78
  # @option options [Integer] :ractors number of ractors per worker process
71
79
  # @option options [Integer] :workers number of worker processes
72
80
  # @option options [Array<String>] :binds array of bind URIs
81
+ # @option options [#call] :app pre-built Rack application
73
82
  # @option options [String] :rackup path to Rack configuration file
74
- # @option options [Hash] :client client timeout configuration
83
+ # @option options [Hash] :client client configuration
84
+ # @option options [#call] :on_error callback invoked with (env, exception) when the Rack app raises
85
+ # @option options [String, nil] :stats_file path to write per-worker stats JSON, or nil to disable
86
+ # @option options [String, nil] :pidfile path to write the master PID to, or nil to disable
75
87
  # @return [void]
76
88
  #
77
89
  # @rbs (Hash[Symbol, untyped] options) -> void
@@ -81,7 +93,8 @@ module Raptor
81
93
  #
82
94
  # Forks the configured number of worker processes and monitors them,
83
95
  # automatically restarting any that exit unexpectedly. Handles graceful
84
- # shutdown via INT or TERM signals, and stats logging via USR1.
96
+ # shutdown via INT or TERM signals, stats logging via USR1, and phased
97
+ # restart via USR2.
85
98
  #
86
99
  # Each worker process includes:
87
100
  # - 1 server thread (continuously accepts connections with backpressure control)
@@ -114,6 +127,22 @@ module Raptor
114
127
  # @rbs (Integer index) -> void
115
128
  def spawn_worker: (Integer index) -> void
116
129
 
130
+ # Reaps any worker processes that have exited, respawning each one
131
+ # unless the cluster is shutting down.
132
+ #
133
+ # @return [Symbol] :no_children when there are no remaining children, otherwise :reaped
134
+ #
135
+ # @rbs () -> Symbol
136
+ def reap_workers: () -> Symbol
137
+
138
+ # Replaces each worker process one at a time, waiting for the new
139
+ # worker to boot before moving on to the next. Triggered by SIGUSR2.
140
+ #
141
+ # @return [void]
142
+ #
143
+ # @rbs () -> void
144
+ def perform_phased_restart: () -> void
145
+
117
146
  # Runs the full server stack inside a worker process.
118
147
  #
119
148
  # Sets up and coordinates the reactor, server, ractor pool, thread pool,
@@ -8,6 +8,31 @@ module Raptor
8
8
  # It integrates with the same reactor, ractor pool, and thread pool
9
9
  # pipeline used by HTTP/1.1 connections.
10
10
  class Http2
11
+ # Lock-free per-connection frame writer.
12
+ #
13
+ # Serializes concurrent socket writes from multiple stream workers
14
+ # without blocking any of them.
15
+ class Writer
16
+ IDLE: ::Symbol
17
+
18
+ @state: Atom
19
+
20
+ # Creates a new Writer.
21
+ #
22
+ # @rbs () -> void
23
+ def initialize: () -> void
24
+
25
+ # Writes frames to the socket, coordinating with concurrent writers
26
+ # so that exactly one thread is actively writing at any time.
27
+ #
28
+ # @param socket [OpenSSL::SSL::SSLSocket] the connection socket
29
+ # @param frames [Array<String>] frame bytes to write in order
30
+ # @return [void]
31
+ #
32
+ # @rbs (OpenSSL::SSL::SSLSocket socket, Array[String] frames) -> void
33
+ def write_frames: (OpenSSL::SSL::SSLSocket socket, Array[String] frames) -> void
34
+ end
35
+
11
36
  FLAG_END_STREAM: ::Integer
12
37
 
13
38
  FLAG_END_HEADERS: ::Integer
@@ -22,6 +47,8 @@ module Raptor
22
47
 
23
48
  HOP_BY_HOP_HEADERS: untyped
24
49
 
50
+ @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
51
+
25
52
  @server_port: Integer
26
53
 
27
54
  @app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
@@ -30,10 +57,11 @@ module Raptor
30
57
  #
31
58
  # @param app [#call] the Rack application to dispatch requests to
32
59
  # @param server_port [Integer] port number used to populate SERVER_PORT in the Rack env
60
+ # @param on_error [#call, nil] callback invoked with (env, exception) when the Rack app raises
33
61
  # @return [void]
34
62
  #
35
- # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port) -> void
36
- def initialize: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Integer server_port) -> void
63
+ # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port, ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
64
+ def initialize: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Integer server_port, ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
37
65
 
38
66
  # Builds the initial server SETTINGS frame to send on connection establishment.
39
67
  #
@@ -88,38 +116,38 @@ module Raptor
88
116
  # the response back as HTTP/2 frames.
89
117
  #
90
118
  # @param socket [OpenSSL::SSL::SSLSocket] the connection socket
91
- # @param mutex [Mutex] mutex for serializing writes to the connection socket
119
+ # @param writer [Writer] lock-free frame writer for the connection
92
120
  # @param stream_id [Integer] the HTTP/2 stream identifier
93
121
  # @param headers [Array<Array(String, String)>] request headers
94
122
  # @param body [String] request body
95
123
  # @param remote_addr [String] the client IP address
96
124
  # @return [void]
97
125
  #
98
- # @rbs (OpenSSL::SSL::SSLSocket socket, Mutex mutex, Integer stream_id, Array[[String, String]] headers, String body, remote_addr: String) -> void
99
- def dispatch_stream_request: (OpenSSL::SSL::SSLSocket socket, Mutex mutex, Integer stream_id, Array[[ String, String ]] headers, String body, remote_addr: String) -> void
126
+ # @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id, Array[[String, String]] headers, String body, remote_addr: String) -> void
127
+ def dispatch_stream_request: (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id, Array[[ String, String ]] headers, String body, remote_addr: String) -> void
100
128
 
101
129
  # Writes a Rack response as HTTP/2 frames to the socket.
102
130
  #
103
131
  # @param socket [OpenSSL::SSL::SSLSocket] the connection socket
104
- # @param mutex [Mutex] mutex for serializing writes to the connection socket
132
+ # @param writer [Writer] lock-free frame writer for the connection
105
133
  # @param stream_id [Integer] the HTTP/2 stream identifier
106
134
  # @param status [Integer] HTTP status code
107
135
  # @param headers [Hash] response headers from the Rack application
108
136
  # @param body [Object] response body responding to each
109
137
  # @return [void]
110
138
  #
111
- # @rbs (OpenSSL::SSL::SSLSocket socket, Mutex mutex, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> void
112
- def write_http2_response: (OpenSSL::SSL::SSLSocket socket, Mutex mutex, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> void
139
+ # @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> void
140
+ def write_http2_response: (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> void
113
141
 
114
142
  # Writes a 500 error response as HTTP/2 frames.
115
143
  #
116
144
  # @param socket [OpenSSL::SSL::SSLSocket] the connection socket
117
- # @param mutex [Mutex] mutex for serializing writes to the connection socket
145
+ # @param writer [Writer] lock-free frame writer for the connection
118
146
  # @param stream_id [Integer] the HTTP/2 stream identifier
119
147
  # @return [void]
120
148
  #
121
- # @rbs (OpenSSL::SSL::SSLSocket socket, Mutex mutex, Integer stream_id) -> void
122
- def write_http2_error_response: (OpenSSL::SSL::SSLSocket socket, Mutex mutex, Integer stream_id) -> void
149
+ # @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id) -> void
150
+ def write_http2_error_response: (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id) -> void
123
151
 
124
152
  # Builds a Rack environment hash from HTTP/2 headers and body.
125
153
  #
@@ -55,7 +55,7 @@ module Raptor
55
55
 
56
56
  TIMEOUT_RESPONSE: ::String
57
57
 
58
- @id_to_mutex: Hash[Integer, Mutex]
58
+ @id_to_writer: Hash[Integer, untyped]
59
59
 
60
60
  @id_to_timeout: Hash[Integer, TimeoutClient]
61
61
 
@@ -159,13 +159,15 @@ module Raptor
159
159
  # @rbs (Integer id) -> TCPSocket?
160
160
  def socket_for: (Integer id) -> TCPSocket?
161
161
 
162
- # Returns the mutex for a given HTTP/2 connection.
162
+ # Returns the writer object associated with a given connection, if one
163
+ # was supplied when the connection was added. Used by protocol handlers
164
+ # that need to coordinate concurrent socket writes.
163
165
  #
164
166
  # @param id [Integer] unique client identifier
165
- # @return [Mutex, nil] the mutex, if found
167
+ # @return [Object, nil] the writer, if found
166
168
  #
167
- # @rbs (Integer id) -> Mutex?
168
- def mutex_for: (Integer id) -> Mutex?
169
+ # @rbs (Integer id) -> untyped?
170
+ def writer_for: (Integer id) -> untyped?
169
171
 
170
172
  # Updates connection state for an HTTP/2 connection after frame processing.
171
173
  #
@@ -13,7 +13,7 @@ module Raptor
13
13
 
14
14
  FILE_CHUNK_SIZE: untyped
15
15
 
16
- KEEPALIVE_BUFFER_SIZE: untyped
16
+ READ_BUFFER_SIZE: untyped
17
17
 
18
18
  WRITE_TIMEOUT: ::Integer
19
19
 
@@ -33,7 +33,9 @@ module Raptor
33
33
 
34
34
  STATUS_WITH_NO_ENTITY_BODY: untyped
35
35
 
36
- ERROR_RESPONSE_500: ::String
36
+ INTERNAL_SERVER_ERROR_RESPONSE: ::String
37
+
38
+ CONTENT_TOO_LARGE_RESPONSE: ::String
37
39
 
38
40
  CONNECTION_CLOSE: ::String
39
41
 
@@ -63,6 +65,25 @@ module Raptor
63
65
  def message: () -> String
64
66
  end
65
67
 
68
+ # Decodes a chunked transfer-encoded body buffer.
69
+ #
70
+ # Returns the decoded bytes and a state symbol: `:complete` when the
71
+ # terminating zero-length chunk was found, `:too_large` when the decoded
72
+ # size would exceed `max_size`, or `:incomplete` otherwise.
73
+ #
74
+ # @param buffer [String] the raw body buffer to decode
75
+ # @param max_size [Integer, nil] maximum decoded body size, or nil for unlimited
76
+ # @return [Array(String, Symbol)] decoded body and completion state
77
+ #
78
+ # @rbs (String buffer, ?Integer? max_size) -> [String, Symbol]
79
+ def self.decode_chunked: (String buffer, ?Integer? max_size) -> [ String, Symbol ]
80
+
81
+ @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
82
+
83
+ @body_spool_threshold: Integer?
84
+
85
+ @max_body_size: Integer?
86
+
66
87
  @server_port: Integer
67
88
 
68
89
  @app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
@@ -71,10 +92,29 @@ module Raptor
71
92
  #
72
93
  # @param app [#call] the Rack application to dispatch complete requests to
73
94
  # @param server_port [Integer] port number used to populate SERVER_PORT in the Rack env
95
+ # @param client_options [Hash] client limits configuration
96
+ # @option client_options [Integer, nil] :max_body_size maximum request body size in bytes
97
+ # @option client_options [Integer, nil] :body_spool_threshold spool bodies larger than this to a tempfile
98
+ # @param on_error [#call, nil] callback invoked with (env, exception) when the Rack app raises
99
+ # @return [void]
100
+ #
101
+ # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port, ?client_options: Hash[Symbol, untyped], ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
102
+ def initialize: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Integer server_port, ?client_options: Hash[Symbol, untyped], ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
103
+
104
+ # Eagerly reads and parses the first request on a freshly accepted
105
+ # connection on the server thread, dispatching directly to the thread pool
106
+ # when complete. Falls back to the reactor when more data is needed.
107
+ #
108
+ # @param socket [TCPSocket] the freshly accepted client socket
109
+ # @param id [Integer] unique client identifier
110
+ # @param reactor [Reactor] the reactor for fallback registration
111
+ # @param thread_pool [AtomicThreadPool] thread pool for application processing
112
+ # @param remote_addr [String] client IP address
113
+ # @param url_scheme [String] "http" or "https"
74
114
  # @return [void]
75
115
  #
76
- # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port) -> void
77
- def initialize: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Integer server_port) -> void
116
+ # @rbs (TCPSocket socket, Integer id, Reactor reactor, AtomicThreadPool thread_pool, String remote_addr, String url_scheme) -> void
117
+ def eager_accept: (TCPSocket socket, Integer id, Reactor reactor, AtomicThreadPool thread_pool, String remote_addr, String url_scheme) -> void
78
118
 
79
119
  # Returns a Proc for HTTP parsing work in Ractor context.
80
120
  #
@@ -154,8 +194,12 @@ module Raptor
154
194
  # @rbs (TCPSocket socket, Integer id, Reactor reactor, AtomicThreadPool thread_pool, Integer request_count, String remote_addr, String url_scheme) -> void
155
195
  def eager_keepalive: (TCPSocket socket, Integer id, Reactor reactor, AtomicThreadPool thread_pool, Integer request_count, String remote_addr, String url_scheme) -> void
156
196
 
157
- # Re-registers a socket with the reactor for further processing
158
- # when an incomplete request is received during eager keep-alive.
197
+ # Re-registers a socket with the reactor for further processing when
198
+ # an incomplete request is received during eager accept or eager keep-alive.
199
+ #
200
+ # The persisted flag selects between persistent_data_timeout (for
201
+ # kept-alive connections awaiting the next request) and chunk_data_timeout
202
+ # (for fresh connections awaiting the rest of the first request).
159
203
  #
160
204
  # @param socket [TCPSocket] the client socket
161
205
  # @param id [Integer] unique client identifier
@@ -166,10 +210,20 @@ module Raptor
166
210
  # @param request_count [Integer] number of requests handled on this connection
167
211
  # @param remote_addr [String] client IP address
168
212
  # @param url_scheme [String] "http" or "https"
213
+ # @param persisted [Boolean] whether the connection has already completed at least one request
214
+ # @return [void]
215
+ #
216
+ # @rbs (TCPSocket socket, Integer id, String buffer, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, Reactor reactor, Integer request_count, String remote_addr, String url_scheme, persisted: bool) -> void
217
+ def fallback_to_reactor: (TCPSocket socket, Integer id, String buffer, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, Reactor reactor, Integer request_count, String remote_addr, String url_scheme, persisted: bool) -> void
218
+
219
+ # Writes a 413 response and closes the socket. Used when a request body
220
+ # exceeds the configured maximum size.
221
+ #
222
+ # @param socket [TCPSocket] the client socket
169
223
  # @return [void]
170
224
  #
171
- # @rbs (TCPSocket socket, Integer id, String buffer, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, Reactor reactor, Integer request_count, String remote_addr, String url_scheme) -> void
172
- def fallback_to_reactor: (TCPSocket socket, Integer id, String buffer, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, Reactor reactor, Integer request_count, String remote_addr, String url_scheme) -> void
225
+ # @rbs (TCPSocket socket) -> void
226
+ def reject_oversized: (TCPSocket socket) -> void
173
227
 
174
228
  # Builds a Rack environment hash from parsed HTTP request data.
175
229
  #
@@ -187,6 +241,16 @@ module Raptor
187
241
  # @rbs (Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, TCPSocket socket, ?remote_addr: String, ?url_scheme: String) -> Hash[String, untyped]
188
242
  def build_rack_env: (Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, TCPSocket socket, ?remote_addr: String, ?url_scheme: String) -> Hash[String, untyped]
189
243
 
244
+ # Builds the `rack.input` IO object for the request body. Returns an
245
+ # in-memory StringIO for bodies up to the spool threshold, or a Tempfile
246
+ # for larger bodies to bound per-worker memory.
247
+ #
248
+ # @param body [String, nil] decoded request body
249
+ # @return [IO] an IO-like object positioned at the start of the body
250
+ #
251
+ # @rbs (String? body) -> IO
252
+ def build_rack_input: (String? body) -> IO
253
+
190
254
  # Determines whether the connection should be kept alive after the response.
191
255
  #
192
256
  # Returns false if the request limit has been reached. For HTTP/1.1, keep-alive
@@ -10,16 +10,19 @@ module Raptor
10
10
  #
11
11
  # Supports TCP, Unix domain, and SSL listeners transparently. TCP_NODELAY is
12
12
  # applied only to TCP sockets, and SSL handshakes are performed synchronously
13
- # before handing the connection to the reactor.
13
+ # before the connection is dispatched.
14
14
  #
15
- # For SSL connections, ALPN negotiation determines the protocol. HTTP/2
16
- # connections are added to the reactor with initial SETTINGS and processed
17
- # through the same ractor pool pipeline as HTTP/1.1 connections.
15
+ # For HTTP/1.1 connections the first request is parsed inline on the server
16
+ # thread and dispatched directly to the thread pool, falling back to the
17
+ # reactor only when more data is needed. For HTTP/2 connections (negotiated
18
+ # via ALPN) the server sends initial SETTINGS and registers the connection
19
+ # with the reactor for frame processing through the ractor pool.
18
20
  #
19
21
  # @example
20
22
  # binder = Binder.new(["tcp://0.0.0.0:3000"])
21
23
  # reactor = Reactor.new(thread_pool, ractor_pool, client_options: {})
22
- # server = Server.new(binder, reactor, thread_pool)
24
+ # request = Request.new(app, 3000)
25
+ # server = Server.new(binder, reactor, thread_pool, request)
23
26
  # server.run
24
27
  # # ... later
25
28
  # server.shutdown
@@ -36,6 +39,8 @@ module Raptor
36
39
 
37
40
  @thread_pool: AtomicThreadPool
38
41
 
42
+ @request: Request
43
+
39
44
  @running: AtomicBoolean
40
45
 
41
46
  # Creates a new Server instance.
@@ -43,10 +48,11 @@ module Raptor
43
48
  # @param binder [Binder] the binder managing listening sockets
44
49
  # @param reactor [Reactor] the reactor for handling client connections
45
50
  # @param thread_pool [AtomicThreadPool] thread pool for application processing
51
+ # @param request [Request] the HTTP/1.1 request handler
46
52
  # @return [void]
47
53
  #
48
- # @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool) -> void
49
- def initialize: (Binder binder, Reactor reactor, AtomicThreadPool thread_pool) -> void
54
+ # @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Request request) -> void
55
+ def initialize: (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Request request) -> void
50
56
 
51
57
  # Starts the server's main accept loop in a new thread.
52
58
  #
@@ -2,8 +2,8 @@
2
2
 
3
3
  # Main module for the Raptor web server.
4
4
  #
5
- # Raptor is a high-performance, multi-threaded, multi-process Ruby web server that
6
- # leverages Ractors for parallel HTTP/1.1 and HTTP/2 request processing, native C
7
- # extensions for HTTP parsing and HPACK compression, and NIO for non-blocking I/O.
5
+ # Raptor is a high-performance, preloading, multi-process, multi-threaded Ruby 4+ web server
6
+ # implementing Rack 3+, leveraging Ractors for parallel HTTP/1.1 and HTTP/2 request processing,
7
+ # native C extensions for HTTP parsing and HPACK compression, and NIO for non-blocking I/O.
8
8
  module Raptor
9
9
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: raptor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Young
@@ -114,6 +114,7 @@ files:
114
114
  - ext/raptor_http2/extconf.rb
115
115
  - ext/raptor_http2/huffman_table.h
116
116
  - ext/raptor_http2/raptor_http2.c
117
+ - lib/rackup/handler/raptor.rb
117
118
  - lib/raptor.rb
118
119
  - lib/raptor/binder.rb
119
120
  - lib/raptor/cli.rb
@@ -124,6 +125,7 @@ files:
124
125
  - lib/raptor/server.rb
125
126
  - lib/raptor/stats.rb
126
127
  - lib/raptor/version.rb
128
+ - sig/generated/rackup/handler/raptor.rbs
127
129
  - sig/generated/raptor.rbs
128
130
  - sig/generated/raptor/binder.rbs
129
131
  - sig/generated/raptor/cli.rbs