raptor 0.6.0 → 0.8.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.
@@ -1,18 +1,18 @@
1
- # Generated from lib/raptor/request.rb with RBS::Inline
1
+ # Generated from lib/raptor/http1.rb with RBS::Inline
2
2
 
3
3
  module Raptor
4
4
  # Parses HTTP/1.x requests and dispatches them to the Rack
5
5
  # application. Coordinates with the Ractor pool for parsing and
6
6
  # with the reactor for requests that need more data before they
7
7
  # can be handled.
8
- class Request
8
+ class Http1
9
9
  BODY_BUFFER_THRESHOLD: untyped
10
10
 
11
11
  FILE_CHUNK_SIZE: untyped
12
12
 
13
- READ_BUFFER_SIZE: untyped
13
+ MAX_CHUNK_OVERHEAD: untyped
14
14
 
15
- WRITE_TIMEOUT: ::Integer
15
+ READ_BUFFER_SIZE: untyped
16
16
 
17
17
  KEEPALIVE_READ_TIMEOUT: ::Float
18
18
 
@@ -28,20 +28,26 @@ module Raptor
28
28
 
29
29
  STATUS_WITH_NO_ENTITY_BODY: untyped
30
30
 
31
- BAD_REQUEST_RESPONSE: ::String
31
+ CONTINUE_RESPONSE: ::String
32
32
 
33
- INTERNAL_SERVER_ERROR_RESPONSE: ::String
33
+ BAD_REQUEST_RESPONSE: ::String
34
34
 
35
35
  CONTENT_TOO_LARGE_RESPONSE: ::String
36
36
 
37
+ INTERNAL_SERVER_ERROR_RESPONSE: ::String
38
+
37
39
  CONNECTION_CLOSE: ::String
38
40
 
39
41
  CONNECTION_KEEPALIVE: ::String
40
42
 
43
+ EXPECT_100_CONTINUE: ::String
44
+
41
45
  TRANSFER_ENCODING_CHUNKED: ::String
42
46
 
43
47
  HTTP_CONNECTION: ::String
44
48
 
49
+ HTTP_EXPECT: ::String
50
+
45
51
  HTTP_TRANSFER_ENCODING: ::String
46
52
 
47
53
  RACK_HEADER_PREFIX: ::String
@@ -54,19 +60,24 @@ module Raptor
54
60
 
55
61
  ILLEGAL_HEADER_VALUE_REGEX: ::Regexp
56
62
 
57
- class Error < StandardError
58
- end
59
-
60
- class WriteError < Error
61
- # @rbs () -> String
62
- def message: () -> String
63
- end
63
+ # Returns true when the message framing shows a request-smuggling vector
64
+ # per RFC 9112 section 6.3: a `Transfer-Encoding` where `chunked` is
65
+ # missing, not the final encoding, or duplicated; a `Transfer-Encoding`
66
+ # paired with a `Content-Length`; or a `Content-Length` containing any
67
+ # non-digit character.
68
+ #
69
+ # @param env [Hash] the Rack environment after header parsing
70
+ # @return [Boolean]
71
+ #
72
+ # @rbs (Hash[String, untyped] env) -> bool
73
+ def self.request_smuggling?: (Hash[String, untyped] env) -> bool
64
74
 
65
75
  # Decodes a chunked transfer-encoded body buffer.
66
76
  #
67
77
  # Returns the decoded bytes and a state symbol: `:complete` when the
68
78
  # terminating zero-length chunk was found, `:too_large` when the decoded
69
- # size would exceed `max_size`, or `:incomplete` otherwise.
79
+ # size would exceed `max_size`, `:malformed` when chunk framing overhead
80
+ # exceeds `MAX_CHUNK_OVERHEAD`, or `:incomplete` otherwise.
70
81
  #
71
82
  # @param buffer [String] the raw body buffer to decode
72
83
  # @param max_size [Integer, nil] maximum decoded body size, or nil for unlimited
@@ -75,41 +86,51 @@ module Raptor
75
86
  # @rbs (String buffer, ?Integer? max_size) -> [String, Symbol]
76
87
  def self.decode_chunked: (String buffer, ?Integer? max_size) -> [ String, Symbol ]
77
88
 
78
- # Writes `string` in full, retrying on partial writes. Bounded by
79
- # `WRITE_TIMEOUT` so a slow client can't pin the writing thread.
80
- #
81
- # @param socket [TCPSocket] the socket to write to
82
- # @param string [String] the data to write
83
- # @return [void]
84
- # @raise [WriteError] if the socket is not writable within the timeout or raises IOError
85
- #
86
- # @rbs (TCPSocket socket, String string) -> void
87
- def self.socket_write: (TCPSocket socket, String string) -> void
88
-
89
89
  @running: AtomicBoolean
90
90
 
91
91
  @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
92
92
 
93
+ @access_log_io: IO?
94
+
95
+ @max_keepalive_requests: Integer
96
+
93
97
  @body_spool_threshold: Integer?
94
98
 
95
99
  @max_body_size: Integer?
96
100
 
101
+ @write_timeout: Integer
102
+
97
103
  @server_port: Integer
98
104
 
99
105
  @app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
100
106
 
101
- # Creates a new Request handler.
107
+ # Creates a new Http1 handler.
102
108
  #
103
109
  # @param app [#call] the Rack application to dispatch complete requests to
104
110
  # @param server_port [Integer] port number used to populate SERVER_PORT in the Rack env
105
- # @param client_options [Hash] client limits configuration
106
- # @option client_options [Integer, nil] :max_body_size maximum request body size in bytes
107
- # @option client_options [Integer, nil] :body_spool_threshold spool bodies larger than this to a tempfile
111
+ # @param connection_options [Hash] per-connection settings shared across protocols
112
+ # @option connection_options [Integer] :write_timeout per-write socket timeout in seconds
113
+ # @option connection_options [Integer, nil] :max_body_size maximum request body size in bytes
114
+ # @option connection_options [Integer, nil] :body_spool_threshold spool bodies larger than this to a tempfile
115
+ # @param http1_options [Hash] HTTP/1.1-specific settings
116
+ # @option http1_options [Integer] :max_keepalive_requests maximum requests per HTTP/1.1 keep-alive connection
117
+ # @param access_log_io [IO, nil] IO to write Common Log Format access entries to, or nil to disable
108
118
  # @param on_error [#call, nil] callback invoked with (env, exception) when the Rack app raises
109
119
  # @return [void]
110
120
  #
111
- # @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
112
- 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
121
+ # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port, ?connection_options: Hash[Symbol, untyped], ?http1_options: Hash[Symbol, untyped], ?access_log_io: IO?, ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
122
+ def initialize: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Integer server_port, ?connection_options: Hash[Symbol, untyped], ?http1_options: Hash[Symbol, untyped], ?access_log_io: IO?, ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
123
+
124
+ # Instance-level wrapper around {Http.socket_write} that applies the
125
+ # configured `write_timeout`.
126
+ #
127
+ # @param socket [TCPSocket] the socket to write to
128
+ # @param string [String] the data to write
129
+ # @return [void]
130
+ # @raise [Http::WriteError] if the socket is not writable within the timeout or raises IOError
131
+ #
132
+ # @rbs (TCPSocket socket, String string) -> void
133
+ def socket_write: (TCPSocket socket, String string) -> void
113
134
 
114
135
  # Signals eager keep-alive loops to stop processing further requests on
115
136
  # their connections. In-flight requests complete normally.
@@ -160,6 +181,28 @@ module Raptor
160
181
 
161
182
  private
162
183
 
184
+ # Returns true if the request expects a 100 Continue response per
185
+ # RFC 7231 section 5.1.1.
186
+ #
187
+ # @param env [Hash] the parsed Rack environment (possibly incomplete)
188
+ # @return [Boolean]
189
+ #
190
+ # @rbs (Hash[String, untyped] env) -> bool
191
+ def expects_100_continue?: (Hash[String, untyped] env) -> bool
192
+
193
+ # Sends an HTTP 100 Continue response when an HTTP/1.1 client requested
194
+ # `Expect: 100-continue` and the request body has not yet been received.
195
+ #
196
+ # Returns the state hash with `:continued` set when the response has been
197
+ # written. A write failure is silently ignored.
198
+ #
199
+ # @param state [Hash] the partially-parsed connection state
200
+ # @param reactor [Reactor] the reactor holding the connection's socket
201
+ # @return [Hash] the state, with `:continued` set if 100 was written
202
+ #
203
+ # @rbs (Hash[Symbol, untyped] state, Reactor reactor) -> Hash[Symbol, untyped]
204
+ def send_continue_if_expected: (Hash[Symbol, untyped] state, Reactor reactor) -> Hash[Symbol, untyped]
205
+
163
206
  # Processes a client connection by handling the current request and,
164
207
  # if keep-alive, eagerly reading subsequent requests inline.
165
208
  #
@@ -278,6 +321,16 @@ module Raptor
278
321
  # @rbs (String? body) -> IO
279
322
  def build_rack_input: (String? body) -> IO
280
323
 
324
+ # Returns true when an upstream proxy signals that it terminated TLS for
325
+ # this request via `X-Forwarded-Proto`, `X-Forwarded-Scheme`, or
326
+ # `X-Forwarded-Ssl`. Only the first comma-separated value is consulted.
327
+ #
328
+ # @param env [Hash] the Rack environment
329
+ # @return [Boolean]
330
+ #
331
+ # @rbs (Hash[String, untyped] env) -> bool
332
+ def forwarded_https?: (Hash[String, untyped] env) -> bool
333
+
281
334
  # Determines whether the connection should be kept alive after the response.
282
335
  #
283
336
  # Returns false if the request limit has been reached. For HTTP/1.1, keep-alive
@@ -526,6 +579,29 @@ module Raptor
526
579
  # @rbs (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
527
580
  def call_response_finished: (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
528
581
 
582
+ # Instance-level wrapper around {Http.write_access_log} that routes to
583
+ # the configured `@access_log_io`.
584
+ #
585
+ # @param env [Hash] the Rack environment
586
+ # @param status [Integer] the response status code
587
+ # @param size [String] the response body size in bytes, or `-` if unknown
588
+ # @param remote_addr [String] the client IP address
589
+ # @return [void]
590
+ #
591
+ # @rbs (Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
592
+ def write_access_log: (Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
593
+
594
+ # Returns the response body size as a String for the access log, taken
595
+ # from the `content-length` header when set, computed from the body
596
+ # otherwise, or `-` when the size cannot be determined upfront.
597
+ #
598
+ # @param headers [Hash] the response headers
599
+ # @param body [Object] the response body
600
+ # @return [String]
601
+ #
602
+ # @rbs (Hash[String, String | Array[String]] headers, untyped body) -> String
603
+ def response_size: (Hash[String, String | Array[String]] headers, untyped body) -> String
604
+
529
605
  # Enables TCP_CORK on the socket to batch outgoing packets into fewer segments.
530
606
  #
531
607
  # Only applies to TCP sockets. No-op on non-TCP sockets.
@@ -17,10 +17,15 @@ module Raptor
17
17
 
18
18
  @state: Atom
19
19
 
20
+ @write_timeout: Integer
21
+
20
22
  # Creates a new Writer.
21
23
  #
22
- # @rbs () -> void
23
- def initialize: () -> void
24
+ # @param write_timeout [Integer] per-write socket timeout passed through to {Http.socket_write}
25
+ # @return [void]
26
+ #
27
+ # @rbs (write_timeout: Integer) -> void
28
+ def initialize: (write_timeout: Integer) -> void
24
29
 
25
30
  # Writes frames to the socket, coordinating with concurrent writers
26
31
  # so that exactly one thread is actively writing at any time.
@@ -36,7 +41,7 @@ module Raptor
36
41
  # Per-connection outbound flow-control accounting.
37
42
  #
38
43
  # Tracks the peer's connection-level and per-stream receive windows so
39
- # outbound DATA frames respect RFC 7540 §5.2. Threads dispatching stream
44
+ # outbound DATA frames respect RFC 7540 section 5.2. Threads dispatching stream
40
45
  # responses call `acquire` to reserve send capacity; threads applying
41
46
  # inbound WINDOW_UPDATE or SETTINGS frames call the mutating methods to
42
47
  # replenish it. The connection window and per-stream windows live in
@@ -93,7 +98,7 @@ module Raptor
93
98
  def add_stream_window: (Integer stream_id, Integer increment) -> void
94
99
 
95
100
  # Updates the peer's `SETTINGS_INITIAL_WINDOW_SIZE`. Shifts every
96
- # existing stream window by the delta as required by RFC 7540 §6.9.2.
101
+ # existing stream window by the delta as required by RFC 7540 section 6.9.2.
97
102
  #
98
103
  # @param new_size [Integer] the peer's new initial window size
99
104
  # @return [void]
@@ -112,6 +117,12 @@ module Raptor
112
117
  def discard_stream: (Integer stream_id) -> void
113
118
  end
114
119
 
120
+ EAGER_READ_TIMEOUT: ::Float
121
+
122
+ EAGER_READ_BUFFER_SIZE: untyped
123
+
124
+ EAGER_MAX_ROUNDS: ::Integer
125
+
115
126
  FLAG_END_STREAM: ::Integer
116
127
 
117
128
  FLAG_END_HEADERS: ::Integer
@@ -134,28 +145,44 @@ module Raptor
134
145
 
135
146
  HOP_BY_HOP_HEADERS: untyped
136
147
 
148
+ @initial_settings_frame: String
149
+
137
150
  @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
138
151
 
152
+ @access_log_io: IO?
153
+
154
+ @write_timeout: Integer
155
+
139
156
  @server_port: Integer
140
157
 
141
158
  @app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
142
159
 
160
+ # The initial server SETTINGS frame sent on every new HTTP/2 connection.
161
+ #
162
+ # @return [String] the encoded SETTINGS frame
163
+ attr_reader initial_settings_frame: untyped
164
+
143
165
  # Creates a new Http2 handler.
144
166
  #
145
167
  # @param app [#call] the Rack application to dispatch requests to
146
168
  # @param server_port [Integer] port number used to populate SERVER_PORT in the Rack env
169
+ # @param connection_options [Hash] per-connection settings shared across protocols
170
+ # @option connection_options [Integer] :write_timeout per-write socket timeout in seconds
171
+ # @param http2_options [Hash] HTTP/2-specific settings
172
+ # @option http2_options [Integer] :max_concurrent_streams maximum HTTP/2 concurrent streams per connection
173
+ # @param access_log_io [IO, nil] IO to write Common Log Format access entries to, or nil to disable
147
174
  # @param on_error [#call, nil] callback invoked with (env, exception) when the Rack app raises
148
175
  # @return [void]
149
176
  #
150
- # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port, ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
151
- 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
177
+ # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port, ?connection_options: Hash[Symbol, untyped], ?http2_options: Hash[Symbol, untyped], ?access_log_io: IO?, ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
178
+ def initialize: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Integer server_port, ?connection_options: Hash[Symbol, untyped], ?http2_options: Hash[Symbol, untyped], ?access_log_io: IO?, ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
152
179
 
153
- # Builds the initial server SETTINGS frame to send on connection establishment.
180
+ # Creates a per-connection {Writer} configured with the handler's write timeout.
154
181
  #
155
- # @return [String] the encoded SETTINGS frame
182
+ # @return [Writer] a new per-connection frame writer
156
183
  #
157
- # @rbs () -> String
158
- def self.build_server_settings_frame: () -> String
184
+ # @rbs () -> Writer
185
+ def create_writer: () -> Writer
159
186
 
160
187
  # Processes HTTP/2 frames from the connection buffer.
161
188
  #
@@ -205,7 +232,9 @@ module Raptor
205
232
  # Handles a parsed HTTP/2 request from the ractor pool.
206
233
  #
207
234
  # Writes outgoing protocol frames to the socket, updates reactor state,
208
- # and dispatches completed stream requests to the thread pool.
235
+ # and dispatches completed stream requests to the thread pool. Eagerly
236
+ # consumes subsequent frame batches that are already buffered, skipping
237
+ # the reactor and ractor pool hops while the connection is hot.
209
238
  #
210
239
  # @param result [Hash] the parsed result from the ractor pool
211
240
  # @param reactor [Reactor] the reactor managing the connection
@@ -227,6 +256,15 @@ module Raptor
227
256
  # @rbs (FlowControl flow_control, Hash[Symbol, untyped] result) -> void
228
257
  def apply_flow_control_updates: (FlowControl flow_control, Hash[Symbol, untyped] result) -> void
229
258
 
259
+ # Reads the next frame batch from `socket` within a short window, or
260
+ # returns nil if nothing arrives in time.
261
+ #
262
+ # @param socket [OpenSSL::SSL::SSLSocket] the connection socket
263
+ # @return [String, nil] the bytes read, or nil if nothing was available
264
+ #
265
+ # @rbs (OpenSSL::SSL::SSLSocket socket) -> String?
266
+ def eager_read_next_batch: (OpenSSL::SSL::SSLSocket socket) -> String?
267
+
230
268
  # Dispatches a completed stream request to the Rack app and writes
231
269
  # the response back as HTTP/2 frames.
232
270
  #
@@ -254,10 +292,10 @@ module Raptor
254
292
  # @param status [Integer] HTTP status code
255
293
  # @param headers [Hash] response headers from the Rack application
256
294
  # @param body [Object] response body responding to each
257
- # @return [void]
295
+ # @return [String] the response body size in bytes
258
296
  #
259
- # @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, FlowControl flow_control, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> void
260
- def write_http2_response: (OpenSSL::SSL::SSLSocket socket, Writer writer, FlowControl flow_control, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> void
297
+ # @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, FlowControl flow_control, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> String
298
+ def write_http2_response: (OpenSSL::SSL::SSLSocket socket, Writer writer, FlowControl flow_control, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> String
261
299
 
262
300
  # Writes a 500 error response as HTTP/2 frames.
263
301
  #
@@ -269,6 +307,18 @@ module Raptor
269
307
  # @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id) -> void
270
308
  def write_http2_error_response: (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id) -> void
271
309
 
310
+ # Instance-level wrapper around {Http.write_access_log} that routes to
311
+ # the configured `@access_log_io`.
312
+ #
313
+ # @param env [Hash] the Rack environment
314
+ # @param status [Integer] the response status code
315
+ # @param size [String] the response body size in bytes, or `-` if unknown
316
+ # @param remote_addr [String] the client IP address
317
+ # @return [void]
318
+ #
319
+ # @rbs (Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
320
+ def write_access_log: (Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
321
+
272
322
  # Builds a Rack environment hash from HTTP/2 headers and body.
273
323
  #
274
324
  # Translates HTTP/2 pseudo-headers into Rack-compatible environment keys
@@ -10,10 +10,12 @@ module Raptor
10
10
  # the server uses for backpressure control to prevent overload.
11
11
  #
12
12
  # @example
13
- # reactor = Reactor.new(ractor_pool, thread_pool, client_options: {
14
- # first_data_timeout: 30,
15
- # chunk_data_timeout: 10
16
- # })
13
+ # reactor = Reactor.new(
14
+ # ractor_pool,
15
+ # thread_pool,
16
+ # connection_options: { first_data_timeout: 30, chunk_data_timeout: 10 },
17
+ # http1_options: { persistent_data_timeout: 65 }
18
+ # )
17
19
  # reactor.run
18
20
  # reactor.add(id: client.object_id, socket: client)
19
21
  # # ... later
@@ -71,7 +73,11 @@ module Raptor
71
73
 
72
74
  @selector: NIO::Selector
73
75
 
74
- @client_options: Hash[Symbol, Integer]
76
+ @persistent_data_timeout: Integer
77
+
78
+ @chunk_data_timeout: Integer
79
+
80
+ @first_data_timeout: Integer
75
81
 
76
82
  @ractor_pool: untyped
77
83
 
@@ -81,14 +87,15 @@ module Raptor
81
87
  #
82
88
  # @param ractor_pool [RactorPool] ractor pool for HTTP parsing
83
89
  # @param thread_pool [AtomicThreadPool] thread pool for application processing
84
- # @param client_options [Hash] timeout configuration options
85
- # @option client_options [Integer] :first_data_timeout timeout for initial data
86
- # @option client_options [Integer] :chunk_data_timeout timeout for subsequent chunks
87
- # @option client_options [Integer] :persistent_data_timeout timeout for keep-alive connections
90
+ # @param connection_options [Hash] per-connection timeout configuration
91
+ # @option connection_options [Integer] :first_data_timeout timeout for initial data
92
+ # @option connection_options [Integer] :chunk_data_timeout timeout for subsequent chunks
93
+ # @param http1_options [Hash] HTTP/1.1-specific configuration
94
+ # @option http1_options [Integer] :persistent_data_timeout timeout for keep-alive idle connections
88
95
  # @return [void]
89
96
  #
90
- # @rbs (untyped ractor_pool, untyped thread_pool, client_options: Hash[Symbol, Integer]) -> void
91
- def initialize: (untyped ractor_pool, untyped thread_pool, client_options: Hash[Symbol, Integer]) -> void
97
+ # @rbs (untyped ractor_pool, untyped thread_pool, connection_options: Hash[Symbol, untyped], http1_options: Hash[Symbol, untyped]) -> void
98
+ def initialize: (untyped ractor_pool, untyped thread_pool, connection_options: Hash[Symbol, untyped], http1_options: Hash[Symbol, untyped]) -> void
92
99
 
93
100
  # Starts the reactor's main event loop in a new thread.
94
101
  #
@@ -14,9 +14,10 @@ module Raptor
14
14
  #
15
15
  # @example
16
16
  # binder = Binder.new(["tcp://0.0.0.0:3000"])
17
- # reactor = Reactor.new(ractor_pool, thread_pool, client_options: {})
18
- # request = Request.new(app, 3000)
19
- # server = Server.new(binder, reactor, thread_pool, request, client_options: { first_data_timeout: 30 })
17
+ # reactor = Reactor.new(ractor_pool, thread_pool, connection_options: {}, http1_options: {})
18
+ # http1 = Http1.new(app, 3000)
19
+ # http2 = Http2.new(app, 3000)
20
+ # server = Server.new(binder, reactor, thread_pool, http1, http2, connection_options: { first_data_timeout: 30 })
20
21
  # server.run
21
22
  # # ... later
22
23
  # server.shutdown
@@ -35,9 +36,13 @@ module Raptor
35
36
 
36
37
  @running: AtomicBoolean
37
38
 
38
- @client_options: Hash[Symbol, untyped]
39
+ @drain_accept_queue: bool
39
40
 
40
- @request: Request
41
+ @first_data_timeout: Integer
42
+
43
+ @http2: Http2
44
+
45
+ @http1: Http1
41
46
 
42
47
  @thread_pool: AtomicThreadPool
43
48
 
@@ -50,12 +55,14 @@ module Raptor
50
55
  # @param binder [Binder] the binder managing listening sockets
51
56
  # @param reactor [Reactor] the reactor for handling client connections
52
57
  # @param thread_pool [AtomicThreadPool] thread pool for application processing
53
- # @param request [Request] the HTTP/1.1 request handler
54
- # @param client_options [Hash] client timeout configuration, used to bound TLS handshakes
58
+ # @param http1 [Http1] the HTTP/1.1 handler
59
+ # @param http2 [Http2] the HTTP/2 handler (provides the initial SETTINGS frame)
60
+ # @param connection_options [Hash] per-connection timeout configuration, used to bound TLS handshakes
61
+ # @param drain_accept_queue [Boolean] whether to drain the kernel accept queue on shutdown
55
62
  # @return [void]
56
63
  #
57
- # @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Request request, client_options: Hash[Symbol, untyped]) -> void
58
- def initialize: (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Request request, client_options: Hash[Symbol, untyped]) -> void
64
+ # @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Http1 http1, Http2 http2, connection_options: Hash[Symbol, untyped], ?drain_accept_queue: bool) -> void
65
+ def initialize: (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Http1 http1, Http2 http2, connection_options: Hash[Symbol, untyped], ?drain_accept_queue: bool) -> void
59
66
 
60
67
  # Starts the server's main accept loop in a new thread.
61
68
  #
@@ -71,8 +78,9 @@ module Raptor
71
78
 
72
79
  # Gracefully shuts down the server.
73
80
  #
74
- # Stops accepting new connections and closes all listening sockets.
75
- # The server thread will exit after handling any in-flight accept operations.
81
+ # Stops accepting new connections and closes all listening sockets. When
82
+ # `drain_accept_queue` is enabled, dispatches every connection already in
83
+ # the kernel accept queue before closing the listeners.
76
84
  #
77
85
  # @return [void]
78
86
  #
@@ -81,6 +89,14 @@ module Raptor
81
89
 
82
90
  private
83
91
 
92
+ # Dispatches every connection already in the kernel accept queue for each
93
+ # listener until all are drained.
94
+ #
95
+ # @return [void]
96
+ #
97
+ # @rbs () -> void
98
+ def drain_accept_queue: () -> void
99
+
84
100
  # Accepts a connection from the given listener and dispatches it.
85
101
  #
86
102
  # For SSL listeners the TLS handshake is offloaded to the thread pool so
@@ -90,11 +106,10 @@ module Raptor
90
106
  # follow the HTTP/1.1 path.
91
107
  #
92
108
  # @param listener [TCPServer, UNIXServer, Binder::SslListener] the ready listener
93
- # @param reactor [Reactor] the reactor to dispatch connections to
94
- # @return [void]
109
+ # @return [Boolean] true if a connection was accepted, false if the listener had nothing to dispatch
95
110
  #
96
- # @rbs (TCPServer | UNIXServer | Binder::SslListener listener, Reactor reactor) -> void
97
- def accept_connection: (TCPServer | UNIXServer | Binder::SslListener listener, Reactor reactor) -> void
111
+ # @rbs (TCPServer | UNIXServer | Binder::SslListener listener) -> bool
112
+ def accept_connection: (TCPServer | UNIXServer | Binder::SslListener listener) -> bool
98
113
 
99
114
  # Performs the TLS handshake for an accepted SSL connection and dispatches
100
115
  # it through the HTTP/2 or HTTP/1.1 path. The handshake is bounded by
@@ -103,11 +118,10 @@ module Raptor
103
118
  # @param listener [Binder::SslListener] the SSL listener that accepted the connection
104
119
  # @param tcp_client [TCPSocket] the accepted TCP socket
105
120
  # @param remote_addr [String] the client's IP address
106
- # @param reactor [Reactor] the reactor to dispatch the connection to
107
121
  # @return [void]
108
122
  #
109
- # @rbs (Binder::SslListener listener, TCPSocket tcp_client, String remote_addr, Reactor reactor) -> void
110
- def dispatch_ssl_connection: (Binder::SslListener listener, TCPSocket tcp_client, String remote_addr, Reactor reactor) -> void
123
+ # @rbs (Binder::SslListener listener, TCPSocket tcp_client, String remote_addr) -> void
124
+ def dispatch_ssl_connection: (Binder::SslListener listener, TCPSocket tcp_client, String remote_addr) -> void
111
125
 
112
126
  # Drives a non-blocking SSL handshake to completion, bounded by the
113
127
  # configured first-data timeout. Returns true on success, false on
@@ -0,0 +1,42 @@
1
+ # Generated from lib/raptor/systemd.rb with RBS::Inline
2
+
3
+ module Raptor
4
+ # Integration with systemd's service notification protocol and
5
+ # socket-activation file descriptors.
6
+ module Systemd
7
+ LISTEN_FDS_START: ::Integer
8
+
9
+ LISTEN_FDNAMES_ENV: ::String
10
+
11
+ LISTEN_FDS_ENV: ::String
12
+
13
+ LISTEN_PID_ENV: ::String
14
+
15
+ NOTIFY_SOCKET_ENV: ::String
16
+
17
+ # Sends `message` to the systemd notification socket, returning true on
18
+ # success and false when the socket is unset or the send fails.
19
+ #
20
+ # @param message [String] notify protocol payload, e.g. "READY=1"
21
+ # @return [Boolean]
22
+ #
23
+ # @rbs (String message) -> bool
24
+ def self.notify: (String message) -> bool
25
+
26
+ # Returns the file descriptors passed in via socket activation, or an
27
+ # empty array when systemd has not exported any.
28
+ #
29
+ # @return [Array<Integer>]
30
+ #
31
+ # @rbs () -> Array[Integer]
32
+ def self.listen_fds: () -> Array[Integer]
33
+
34
+ # Clears the socket-activation environment variables so children don't
35
+ # act on stale values.
36
+ #
37
+ # @return [void]
38
+ #
39
+ # @rbs () -> void
40
+ def self.clear_listen_env: () -> void
41
+ end
42
+ 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.6.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Young
@@ -121,24 +121,28 @@ files:
121
121
  - lib/raptor/binder.rb
122
122
  - lib/raptor/cli.rb
123
123
  - lib/raptor/cluster.rb
124
+ - lib/raptor/http.rb
125
+ - lib/raptor/http1.rb
124
126
  - lib/raptor/http2.rb
125
127
  - lib/raptor/log.rb
126
128
  - lib/raptor/reactor.rb
127
- - lib/raptor/request.rb
128
129
  - lib/raptor/server.rb
129
130
  - lib/raptor/stats.rb
131
+ - lib/raptor/systemd.rb
130
132
  - lib/raptor/version.rb
131
133
  - sig/generated/rackup/handler/raptor.rbs
132
134
  - sig/generated/raptor.rbs
133
135
  - sig/generated/raptor/binder.rbs
134
136
  - sig/generated/raptor/cli.rbs
135
137
  - sig/generated/raptor/cluster.rbs
138
+ - sig/generated/raptor/http.rbs
139
+ - sig/generated/raptor/http1.rbs
136
140
  - sig/generated/raptor/http2.rbs
137
141
  - sig/generated/raptor/log.rbs
138
142
  - sig/generated/raptor/reactor.rbs
139
- - sig/generated/raptor/request.rbs
140
143
  - sig/generated/raptor/server.rbs
141
144
  - sig/generated/raptor/stats.rbs
145
+ - sig/generated/raptor/systemd.rbs
142
146
  - sig/generated/raptor/version.rbs
143
147
  homepage: https://github.com/joshuay03/raptor
144
148
  licenses: