raptor 0.12.0 → 0.13.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.
@@ -103,27 +103,41 @@ module Raptor
103
103
  # @rbs (String buffer, ?Integer? max_size) -> [String, Symbol]
104
104
  def self.decode_chunked: (String buffer, ?Integer? max_size) -> [ String, Symbol ]
105
105
 
106
- @app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
106
+ # Advances an HTTP/1.x request parse from the state hash's buffered
107
+ # bytes. A complete well-formed request returns with `:complete` set
108
+ # plus populated `:env` and `:body`; malformed or oversized input flips
109
+ # `:malformed` or `:too_large`; incomplete input returns the state
110
+ # so the reactor can wait for more.
111
+ #
112
+ # @param data [Hash] the current parse state
113
+ # @param env_template [Hash] the Rack env template to seed the request with
114
+ # @param max_body_size [Integer, nil] byte limit for the request body, or nil for no limit
115
+ # @return [Hash] the updated parse state, made shareable for cross-Ractor return
116
+ #
117
+ # @rbs (Hash[Symbol, untyped] data, Hash[String, untyped] env_template, Integer? max_body_size) -> Hash[Symbol, untyped]
118
+ def self.parse: (Hash[Symbol, untyped] data, Hash[String, untyped] env_template, Integer? max_body_size) -> Hash[Symbol, untyped]
107
119
 
108
- @server_port: Integer
120
+ @env_template: Hash[String, untyped]
109
121
 
110
- @server_port_string: String
122
+ @running: AtomicBoolean
111
123
 
112
- @write_timeout: Integer
124
+ @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
113
125
 
114
- @max_body_size: Integer?
126
+ @access_log_io: IO?
127
+
128
+ @max_keepalive_requests: Integer
115
129
 
116
130
  @body_spool_threshold: Integer?
117
131
 
118
- @max_keepalive_requests: Integer
132
+ @max_body_size: Integer?
119
133
 
120
- @access_log_io: IO?
134
+ @write_timeout: Integer
121
135
 
122
- @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
136
+ @server_port_string: String
123
137
 
124
- @running: AtomicBoolean
138
+ @server_port: Integer
125
139
 
126
- @env_template: Hash[String, untyped]
140
+ @app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
127
141
 
128
142
  # Creates a new Http1 handler.
129
143
  #
@@ -142,6 +156,14 @@ module Raptor
142
156
  # @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
143
157
  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
144
158
 
159
+ # Instance-level wrapper around {Http.parser_worker} that binds this
160
+ # handler's env template and body-size limit into the worker proc.
161
+ #
162
+ # @return [Proc]
163
+ #
164
+ # @rbs () -> ^(Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
165
+ def parser_worker: () -> ^(Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
166
+
145
167
  # Instance-level wrapper around {Http.socket_write} that applies the
146
168
  # configured `write_timeout`.
147
169
  #
@@ -187,21 +209,8 @@ module Raptor
187
209
  # @rbs (TCPSocket socket, Integer id, Reactor reactor, AtomicThreadPool thread_pool, String remote_addr, String url_scheme) -> void
188
210
  def eager_accept: (TCPSocket socket, Integer id, Reactor reactor, AtomicThreadPool thread_pool, String remote_addr, String url_scheme) -> void
189
211
 
190
- # Returns a Proc for HTTP parsing work in Ractor context.
191
- #
192
- # The returned Proc processes raw socket data through the appropriate
193
- # HTTP parser and returns either a complete request state (ready for
194
- # app processing) or incomplete request state (needs more data).
195
- #
196
- # @return [Proc] a Ractor-safe proc that accepts a state hash and returns an updated state hash
197
- #
198
- # @rbs () -> ^(Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
199
- def http_parser_worker: () -> ^(Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
200
-
201
- # Handles a parsed HTTP request by either continuing parsing or dispatching to the Rack app.
202
- #
203
- # For incomplete requests, updates reactor state and re-registers for more I/O.
204
- # For complete requests, removes from reactor, builds Rack env, and dispatches to thread pool.
212
+ # Dispatches a parsed HTTP request to the thread pool when complete,
213
+ # or hands it back to the reactor for more I/O when incomplete.
205
214
  #
206
215
  # @param parsed_request [Hash] the parsed request state from the ractor pool
207
216
  # @param reactor [Reactor] the reactor managing the client connection
@@ -213,6 +222,42 @@ module Raptor
213
222
 
214
223
  private
215
224
 
225
+ # Reads pending bytes off `socket` into the thread-local read buffer,
226
+ # draining any additional SSL-buffered bytes so `pending` is empty on
227
+ # return. Raises `IO::WaitReadable`, `EOFError`, or `IOError` like the
228
+ # underlying `read_nonblock` does.
229
+ #
230
+ # @param socket [TCPSocket] the socket to read from
231
+ # @return [String] the thread-local buffer, freshly populated
232
+ #
233
+ # @rbs (TCPSocket socket) -> String
234
+ def read_into_thread_buffer: (TCPSocket socket) -> String
235
+
236
+ # Runs a fresh HTTP/1.x parse against `buffer`, returning
237
+ # `[env, parse_data, nread, parser]`. Raises `HttpParserError` on
238
+ # malformed input.
239
+ #
240
+ # @param buffer [String] the raw request bytes
241
+ # @return [Array(Hash, Hash, Integer, HttpParser)]
242
+ #
243
+ # @rbs (String buffer) -> [Hash[String, untyped], Hash[Symbol, untyped], Integer, HttpParser]
244
+ def parse_next_request: (String buffer) -> [ Hash[String, untyped], Hash[Symbol, untyped], Integer, HttpParser ]
245
+
246
+ # Resolves the request body for a finished parse. Returns the body
247
+ # `String` (or `nil` when the request has no body), or one of
248
+ # `:incomplete`, `:too_large`, `:malformed` when the caller must
249
+ # fall back or reject.
250
+ #
251
+ # @param buffer [String] the raw request bytes
252
+ # @param env [Hash] the Rack environment being built
253
+ # @param parser [HttpParser] the parser holding the finished parse state
254
+ # @param nread [Integer] the byte offset where the body begins in `buffer`
255
+ # @param decode_chunked [Boolean] whether to decode chunked bodies inline; when false chunked bodies signal `:incomplete`
256
+ # @return [String, nil, Symbol]
257
+ #
258
+ # @rbs (String buffer, Hash[String, untyped] env, HttpParser parser, Integer nread, decode_chunked: bool) -> (String | Symbol)?
259
+ def extract_body: (String buffer, Hash[String, untyped] env, HttpParser parser, Integer nread, decode_chunked: bool) -> (String | Symbol)?
260
+
216
261
  # Returns true if the request expects a 100 Continue response per
217
262
  # RFC 7231 section 5.1.1.
218
263
  #
@@ -222,11 +267,9 @@ module Raptor
222
267
  # @rbs (Hash[String, untyped] env) -> bool
223
268
  def expects_100_continue?: (Hash[String, untyped] env) -> bool
224
269
 
225
- # Sends an HTTP 100 Continue response when an HTTP/1.1 client requested
226
- # `Expect: 100-continue` and the request body has not yet been received.
227
- #
228
- # Returns the state hash with `:continued` set when the response has been
229
- # written. A write failure is silently ignored.
270
+ # Sends an HTTP 100 Continue response when the client requested
271
+ # `Expect: 100-continue`, returning the state hash with `:continued`
272
+ # set once written. A write failure is silently ignored.
230
273
  #
231
274
  # @param state [Hash] the partially-parsed connection state
232
275
  # @param reactor [Reactor] the reactor holding the connection's socket
@@ -253,9 +296,9 @@ module Raptor
253
296
  # @rbs (TCPSocket socket, Integer id, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, Reactor reactor, AtomicThreadPool thread_pool, Integer request_count, String remote_addr, String url_scheme) -> void
254
297
  def process_client: (TCPSocket socket, Integer id, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, Reactor reactor, AtomicThreadPool thread_pool, Integer request_count, String remote_addr, String url_scheme) -> void
255
298
 
256
- # Builds the Rack env, calls the application, and writes the response.
257
- # Returns true if the connection should be kept alive for further
258
- # requests, false otherwise (including hijack and error cases).
299
+ # Processes a single request. Builds the Rack env, calls the app,
300
+ # writes the response, and returns whether the connection stays open
301
+ # for another request.
259
302
  #
260
303
  # @param socket [TCPSocket] the client socket
261
304
  # @param env [Hash] partial env hash from the HTTP parser
@@ -269,11 +312,26 @@ module Raptor
269
312
  # @rbs (TCPSocket socket, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, Integer request_count, String remote_addr, String url_scheme) -> bool
270
313
  def process_request: (TCPSocket socket, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, Integer request_count, String remote_addr, String url_scheme) -> bool
271
314
 
272
- # Attempts to read and process subsequent requests inline on a
273
- # kept-alive connection. Blocks briefly for the next request to avoid
274
- # a full reactor round-trip. Falls back to the reactor when no data
275
- # arrives within the timeout, when the thread pool has queued work
276
- # (deprioritization), or when the request is incomplete.
315
+ # Handles an exception raised while processing a request. Fires the
316
+ # `rack.response_finished` callbacks with the error, writes a 500
317
+ # response when no bytes have gone to the socket yet, and routes the
318
+ # exception through the configured `on_error` handler (or re-raises).
319
+ #
320
+ # @param socket [TCPSocket] the client socket
321
+ # @param rack_env [Hash, nil] the Rack environment, if it was built
322
+ # @param status [Integer, nil] the status returned by the app, if any
323
+ # @param headers [Hash, nil] the headers returned by the app, if any
324
+ # @param error [Exception] the exception raised
325
+ # @param response_started [Boolean] whether any response bytes have been written
326
+ # @param hijacked [Boolean] whether the app took over the socket
327
+ # @return [void]
328
+ #
329
+ # @rbs (TCPSocket socket, Hash[String, untyped]? rack_env, Integer? status, Hash[String, String | Array[String]]? headers, Exception error, response_started: bool, hijacked: bool) -> void
330
+ def handle_app_error: (TCPSocket socket, Hash[String, untyped]? rack_env, Integer? status, Hash[String, String | Array[String]]? headers, Exception error, response_started: bool, hijacked: bool) -> void
331
+
332
+ # Reads and processes subsequent requests inline on a kept-alive
333
+ # connection. Falls back to the reactor when no data arrives within the
334
+ # timeout, the thread pool is saturated, or the request is incomplete.
277
335
  #
278
336
  # @param socket [TCPSocket] the client socket
279
337
  # @param id [Integer] unique client identifier
@@ -288,11 +346,7 @@ module Raptor
288
346
  def eager_keepalive: (TCPSocket socket, Integer id, Reactor reactor, AtomicThreadPool thread_pool, Integer request_count, String remote_addr, String url_scheme) -> void
289
347
 
290
348
  # Re-registers a socket with the reactor for further processing when
291
- # an incomplete request is received during eager accept or eager keep-alive.
292
- #
293
- # The persisted flag selects between persistent_data_timeout (for
294
- # kept-alive connections awaiting the next request) and chunk_data_timeout
295
- # (for fresh connections awaiting the rest of the first request).
349
+ # an incomplete request is received on the fast path.
296
350
  #
297
351
  # @param socket [TCPSocket] the client socket
298
352
  # @param id [Integer] unique client identifier
@@ -309,8 +363,7 @@ module Raptor
309
363
  # @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
310
364
  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
311
365
 
312
- # Writes a 413 response and closes the socket. Used when a request body
313
- # exceeds the configured maximum size.
366
+ # Writes a 413 response and closes the socket.
314
367
  #
315
368
  # @param socket [TCPSocket] the client socket
316
369
  # @return [void]
@@ -318,8 +371,7 @@ module Raptor
318
371
  # @rbs (TCPSocket socket) -> void
319
372
  def reject_oversized: (TCPSocket socket) -> void
320
373
 
321
- # Writes a 400 response and closes the socket. Used when the HTTP parser
322
- # rejects the request line or headers.
374
+ # Writes a 400 response and closes the socket.
323
375
  #
324
376
  # @param socket [TCPSocket] the client socket
325
377
  # @return [void]
@@ -329,9 +381,6 @@ module Raptor
329
381
 
330
382
  # Builds a Rack environment hash from parsed HTTP request data.
331
383
  #
332
- # Populates all required Rack env keys including rack.* keys, REMOTE_ADDR,
333
- # SERVER_NAME, SERVER_PORT, and hijack support.
334
- #
335
384
  # @param env [Hash] partial env hash from the HTTP parser
336
385
  # @param parse_data [Hash] metadata from the parsing pass, including content_length
337
386
  # @param body [String, nil] decoded request body, or nil if no body
@@ -363,11 +412,8 @@ module Raptor
363
412
  # @rbs (Hash[String, untyped] env) -> bool
364
413
  def forwarded_https?: (Hash[String, untyped] env) -> bool
365
414
 
366
- # Determines whether the connection should be kept alive after the response.
367
- #
368
- # Returns false if the request limit has been reached. For HTTP/1.1, keep-alive
369
- # is the default unless the client sent Connection: close. For HTTP/1.0,
370
- # keep-alive must be explicitly requested.
415
+ # Returns true when the connection should be kept alive after the
416
+ # current response.
371
417
  #
372
418
  # @param env [Hash] the Rack environment
373
419
  # @param request_count [Integer] number of requests handled on this connection
@@ -376,9 +422,8 @@ module Raptor
376
422
  # @rbs (Hash[String, untyped] env, Integer request_count) -> bool
377
423
  def keep_alive?: (Hash[String, untyped] env, Integer request_count) -> bool
378
424
 
379
- # Sends an HTTP 103 Early Hints response to the client.
380
- #
381
- # Skips any hints with illegal header keys or values. No-ops if hints is empty.
425
+ # Sends an HTTP 103 Early Hints response, skipping any entries with
426
+ # illegal header keys or values. No-ops when `hints` is empty.
382
427
  #
383
428
  # @param socket [TCPSocket] the client socket to write to
384
429
  # @param hints [Hash] header name to value (or array of values) pairs
@@ -389,9 +434,6 @@ module Raptor
389
434
 
390
435
  # Writes a complete HTTP response to the socket.
391
436
  #
392
- # Handles header normalization, validation, connection management, TCP corking,
393
- # and dispatches to the appropriate body write strategy.
394
- #
395
437
  # @param socket [TCPSocket] the client socket to write to
396
438
  # @param env [Hash] the Rack environment
397
439
  # @param status [Integer] HTTP status code
@@ -413,10 +455,8 @@ module Raptor
413
455
  # @rbs (Integer status) -> void
414
456
  def validate_status: (Integer status) -> void
415
457
 
416
- # Normalizes response headers by downcasing keys and filtering invalid entries.
417
- #
418
- # Removes headers with illegal keys, rack.* prefixed headers, and "status" headers.
419
- # Raises if headers is not a Hash or contains non-String keys.
458
+ # Returns a normalised copy of the response headers with lowercased
459
+ # keys and illegal/`rack.*`/`status` entries dropped.
420
460
  #
421
461
  # @param headers [Hash] raw headers from the Rack application
422
462
  # @return [Hash] normalized headers with lowercased string keys
@@ -425,10 +465,8 @@ module Raptor
425
465
  # @rbs (Hash[String, String | Array[String]] headers) -> Hash[String, String | Array[String]]
426
466
  def normalize_headers: (Hash[String, String | Array[String]] headers) -> Hash[String, String | Array[String]]
427
467
 
428
- # Validates that headers are appropriate for the given status code.
429
- #
430
- # Raises if content-type or content-length are present for status codes
431
- # that must not have an entity body (204, 304, 1xx).
468
+ # Raises when the headers include entries forbidden for the response
469
+ # status (`content-type` or `content-length` on a 204, 304, or 1xx).
432
470
  #
433
471
  # @param headers [Hash] normalized response headers
434
472
  # @param status [Integer] HTTP status code
@@ -438,8 +476,7 @@ module Raptor
438
476
  # @rbs (Hash[String, String | Array[String]] headers, Integer status) -> void
439
477
  def validate_headers: (Hash[String, String | Array[String]] headers, Integer status) -> void
440
478
 
441
- # Returns the HTTP status line for `status`. Callers must not retain the
442
- # returned string across further calls on the same thread.
479
+ # Returns the HTTP status line for `status`.
443
480
  #
444
481
  # @param http_version [String] "HTTP/1.1" or "HTTP/1.0"
445
482
  # @param status [Integer] HTTP status code
@@ -448,10 +485,8 @@ module Raptor
448
485
  # @rbs (String http_version, Integer status) -> String
449
486
  def build_status_line: (String http_version, Integer status) -> String
450
487
 
451
- # Writes response headers and delegates body writing to the hijack callback.
452
- #
453
- # Uncorks the socket before calling the hijack so the app has full control
454
- # of the raw connection.
488
+ # Writes the response headers, uncorks the socket, and hands the raw
489
+ # socket to the hijack callback.
455
490
  #
456
491
  # @param socket [TCPSocket] the client socket
457
492
  # @param response [String] the status line accumulated so far
@@ -462,11 +497,9 @@ module Raptor
462
497
  # @rbs (TCPSocket socket, String response, Hash[String, String | Array[String]] headers, ^(TCPSocket) -> void response_hijack) -> void
463
498
  def write_hijacked_response: (TCPSocket socket, String response, Hash[String, String | Array[String]] headers, ^(TCPSocket) -> void response_hijack) -> void
464
499
 
465
- # Writes a response with no entity body.
466
- #
467
- # Used for HEAD requests and status codes that must not carry a body
468
- # (204, 304, 1xx). Adds a zero content-length for non-no-body statuses
469
- # that did not supply one.
500
+ # Writes a response with no entity body, adding a zero
501
+ # `Content-Length` when the status may carry a body but none was
502
+ # supplied.
470
503
  #
471
504
  # @param socket [TCPSocket] the client socket
472
505
  # @param response [String] the status line accumulated so far
@@ -477,12 +510,9 @@ module Raptor
477
510
  # @rbs (TCPSocket socket, String response, Hash[String, String | Array[String]] headers, Integer status) -> void
478
511
  def write_no_body_response: (TCPSocket socket, String response, Hash[String, String | Array[String]] headers, Integer status) -> void
479
512
 
480
- # Writes a complete response with a body.
481
- #
482
- # Selects the appropriate write strategy based on body type: callable (streaming),
483
- # file (zero-copy), array, or generic enumerable. Automatically determines
484
- # content-length where possible, falling back to chunked transfer encoding
485
- # for HTTP/1.1 when the length cannot be determined upfront.
513
+ # Writes a complete response with a body. Emits a `Content-Length`
514
+ # when the total size is known upfront, otherwise chunked encoding on
515
+ # HTTP/1.1.
486
516
  #
487
517
  # @param socket [TCPSocket] the client socket
488
518
  # @param response [String] the status line accumulated so far
@@ -494,9 +524,8 @@ module Raptor
494
524
  # @rbs (TCPSocket socket, String response, Hash[String, String | Array[String]] headers, untyped body, String http_version) -> void
495
525
  def write_full_response: (TCPSocket socket, String response, Hash[String, String | Array[String]] headers, untyped body, String http_version) -> void
496
526
 
497
- # Calculates content length from an array or file body without consuming it.
498
- #
499
- # Returns nil for enumerable bodies whose length cannot be determined upfront.
527
+ # Returns the byte length of the body when it can be determined
528
+ # upfront (array or file), otherwise nil.
500
529
  #
501
530
  # @param body [Object] the response body
502
531
  # @return [Integer, nil] the byte length, or nil if it cannot be determined
@@ -506,9 +535,6 @@ module Raptor
506
535
 
507
536
  # Writes a file body to the socket.
508
537
  #
509
- # Uses zero-copy IO.copy_stream for large files, direct buffering for small ones,
510
- # and chunked encoding when required.
511
- #
512
538
  # @param socket [TCPSocket] the client socket
513
539
  # @param response [String] headers already serialized, to be written before the body
514
540
  # @param path [String] filesystem path of the file to send
@@ -521,8 +547,6 @@ module Raptor
521
547
 
522
548
  # Writes an array body to the socket.
523
549
  #
524
- # Dispatches to the single-chunk or multi-chunk path based on array length.
525
- #
526
550
  # @param socket [TCPSocket] the client socket
527
551
  # @param response [String] headers already serialized, to be written before the body
528
552
  # @param body_array [Array<String>] the response body chunks
@@ -584,10 +608,8 @@ module Raptor
584
608
  # @rbs (String value) -> bool
585
609
  def illegal_header_value?: (String value) -> bool
586
610
 
587
- # Formats a headers hash into an HTTP header string.
588
- #
589
- # Skips entries with illegal keys or values. Array values are written
590
- # as separate header lines.
611
+ # Appends normalised header lines to `result`. Skips entries with
612
+ # illegal keys or values. Array values are written as separate lines.
591
613
  #
592
614
  # @param headers [Hash] normalized response headers
593
615
  # @return [String] formatted header lines, each ending with CRLF
@@ -595,9 +617,9 @@ module Raptor
595
617
  # @rbs (String result, Hash[String, String | Array[String]] headers) -> void
596
618
  def format_headers: (String result, Hash[String, String | Array[String]] headers) -> void
597
619
 
598
- # Appends one or more `name: value` header lines to `result`. Newline-
599
- # separated values are emitted as separate lines; empty values and values
600
- # with illegal characters are skipped silently.
620
+ # Appends one or more `name: value` header lines to `result`, splitting
621
+ # newline-joined values across separate lines and skipping empty or
622
+ # illegal values.
601
623
  #
602
624
  # @param result [String] the buffer to append to
603
625
  # @param name [String] the header name
@@ -607,10 +629,8 @@ module Raptor
607
629
  # @rbs (String result, String name, untyped value) -> void
608
630
  def append_header_value: (String result, String name, untyped value) -> void
609
631
 
610
- # Calls all rack.response_finished callbacks registered in the environment.
611
- #
612
- # Callbacks are called in reverse registration order. Individual callback
613
- # failures are rescued so all callbacks are always attempted.
632
+ # Calls every `rack.response_finished` callback in reverse
633
+ # registration order, rescuing any that raise.
614
634
  #
615
635
  # @param env [Hash, nil] the Rack environment
616
636
  # @param status [Integer, nil] the response status code
@@ -644,10 +664,8 @@ module Raptor
644
664
  # @rbs (Hash[String, String | Array[String]] headers, untyped body) -> String
645
665
  def response_size: (Hash[String, String | Array[String]] headers, untyped body) -> String
646
666
 
647
- # Enables TCP_CORK on the socket to batch outgoing packets into fewer segments.
648
- #
649
- # Only applies to TCP sockets. No-op on non-TCP sockets.
650
- # Available on Linux only; this method is not defined on other platforms.
667
+ # Enables `TCP_CORK` on the socket to batch outgoing packets into
668
+ # fewer segments. Linux-only; a no-op elsewhere.
651
669
  #
652
670
  # @param socket [TCPSocket] the socket to cork
653
671
  # @return [void]
@@ -655,10 +673,8 @@ module Raptor
655
673
  # @rbs (TCPSocket socket) -> void
656
674
  def cork_socket: (TCPSocket socket) -> void
657
675
 
658
- # Disables TCP_CORK on the socket, flushing any buffered packets.
659
- #
660
- # Only applies to TCP sockets. No-op on non-TCP sockets.
661
- # Available on Linux only; this method is not defined on other platforms.
676
+ # Disables `TCP_CORK` on the socket, flushing any buffered packets.
677
+ # Linux-only; a no-op elsewhere.
662
678
  #
663
679
  # @param socket [TCPSocket] the socket to uncork
664
680
  # @return [void]
@@ -2,16 +2,9 @@
2
2
 
3
3
  module Raptor
4
4
  # Handles HTTP/2 request processing and Rack application integration.
5
- #
6
- # Http2 manages the HTTP/2 protocol lifecycle including frame processing,
7
- # HPACK header compression, stream management, and response writing.
8
- # It integrates with the same reactor, ractor pool, and thread pool
9
- # pipeline used by HTTP/1.1 connections.
10
5
  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.
6
+ # Serialises concurrent frame writes on a single HTTP/2 connection so
7
+ # exactly one thread is writing at any moment.
15
8
  class Writer
16
9
  IDLE: ::Symbol
17
10
 
@@ -38,14 +31,8 @@ module Raptor
38
31
  def write_frames: (OpenSSL::SSL::SSLSocket socket, Array[String] frames) -> void
39
32
  end
40
33
 
41
- # Per-connection outbound flow-control accounting.
42
- #
43
34
  # Tracks the peer's connection-level and per-stream receive windows so
44
- # outbound DATA frames respect RFC 7540 section 5.2. Threads dispatching stream
45
- # responses call `acquire` to reserve send capacity; threads applying
46
- # inbound WINDOW_UPDATE or SETTINGS frames call the mutating methods to
47
- # replenish it. The connection window and per-stream windows live in
48
- # separate `Atom`s so the common fast path skips per-stream tracking.
35
+ # outbound `DATA` frames respect RFC 7540 section 5.2.
49
36
  class FlowControl
50
37
  ACQUIRE_POLL_INTERVAL: ::Float
51
38
 
@@ -57,6 +44,8 @@ module Raptor
57
44
 
58
45
  # Creates a new FlowControl with the spec-default windows.
59
46
  #
47
+ # @return [void]
48
+ #
60
49
  # @rbs () -> void
61
50
  def initialize: () -> void
62
51
 
@@ -64,12 +53,6 @@ module Raptor
64
53
  # least one byte is available on both the connection and stream
65
54
  # windows. The returned size is capped at `MAX_FRAME_SIZE`.
66
55
  #
67
- # When `end_stream` is true, `max_bytes` fits within the peer's
68
- # initial stream window, and no per-stream override has been
69
- # recorded, only the connection window is consulted. The stream
70
- # closes on this frame, so its remaining send window will not be
71
- # consulted again and need not be tracked.
72
- #
73
56
  # @param stream_id [Integer] the HTTP/2 stream identifier
74
57
  # @param max_bytes [Integer] the largest size the caller would like to send
75
58
  # @param end_stream [Boolean] true when this is the final frame on the stream
@@ -78,8 +61,7 @@ module Raptor
78
61
  # @rbs (Integer stream_id, Integer max_bytes, ?end_stream: bool) -> Integer
79
62
  def acquire: (Integer stream_id, Integer max_bytes, ?end_stream: bool) -> Integer
80
63
 
81
- # Increments the connection-level send window. Called when the peer
82
- # sends a WINDOW_UPDATE on stream 0.
64
+ # Increments the connection-level send window by `increment` bytes.
83
65
  #
84
66
  # @param increment [Integer] the byte count to add
85
67
  # @return [void]
@@ -87,8 +69,7 @@ module Raptor
87
69
  # @rbs (Integer increment) -> void
88
70
  def add_connection_window: (Integer increment) -> void
89
71
 
90
- # Increments the per-stream send window. Called when the peer sends
91
- # a WINDOW_UPDATE on a specific stream.
72
+ # Increments the send window for the given stream by `increment` bytes.
92
73
  #
93
74
  # @param stream_id [Integer] the HTTP/2 stream identifier
94
75
  # @param increment [Integer] the byte count to add
@@ -106,15 +87,25 @@ module Raptor
106
87
  # @rbs (Integer new_size) -> void
107
88
  def set_initial_stream_window: (Integer new_size) -> void
108
89
 
109
- # Discards any per-stream tracking for the given stream. Called
110
- # after a stream closes so `@stream_windows` does not grow without
111
- # bound across the lifetime of a connection.
90
+ # Discards any per-stream tracking for the given stream.
112
91
  #
113
92
  # @param stream_id [Integer] the HTTP/2 stream identifier
114
93
  # @return [void]
115
94
  #
116
95
  # @rbs (Integer stream_id) -> void
117
96
  def discard_stream: (Integer stream_id) -> void
97
+
98
+ private
99
+
100
+ # Reserves up to `capped` bytes from the connection window,
101
+ # returning the number of bytes granted (0 when the window is
102
+ # exhausted).
103
+ #
104
+ # @param capped [Integer] the largest reservation the caller will accept
105
+ # @return [Integer]
106
+ #
107
+ # @rbs (Integer capped) -> Integer
108
+ def reserve_connection: (Integer capped) -> Integer
118
109
  end
119
110
 
120
111
  EAGER_READ_TIMEOUT: ::Float
@@ -173,9 +164,10 @@ module Raptor
173
164
 
174
165
  @app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
175
166
 
176
- # The initial server SETTINGS frame sent on every new HTTP/2 connection.
167
+ # Returns the initial server SETTINGS frame to send on every new
168
+ # HTTP/2 connection.
177
169
  #
178
- # @return [String] the encoded SETTINGS frame
170
+ # @return [String]
179
171
  attr_reader initial_settings_frame: untyped
180
172
 
181
173
  # Creates a new Http2 handler.
@@ -200,11 +192,9 @@ module Raptor
200
192
  # @rbs () -> Writer
201
193
  def create_writer: () -> Writer
202
194
 
203
- # Processes HTTP/2 frames from the connection buffer.
204
- #
205
- # Parses frames, handles HPACK decoding, tracks stream state, and returns
195
+ # Advances HTTP/2 frame parsing from the connection buffer, returning
206
196
  # updated connection state along with any outgoing protocol frames and
207
- # completed stream requests. Ractor-safe.
197
+ # completed stream requests.
208
198
  #
209
199
  # @param data [Hash] the connection state including buffer and HPACK table
210
200
  # @return [Hash] updated state with outgoing_frames and completed_requests
@@ -245,12 +235,10 @@ module Raptor
245
235
  # @rbs (Hash[Symbol, untyped] data, String buffer, Array[untyped] hpack_table, Hash[Integer, Hash[Symbol, untyped]] streams, Array[String] outgoing_frames, Array[Hash[Symbol, untyped]] completed_requests, Array[[Integer, Integer]] window_updates, Integer? peer_initial_window_size, Integer connection_window, bool preface_received, Integer last_client_stream_id, Hash[Symbol, untyped]? pending_headers, bool close_connection) -> Hash[Symbol, untyped]
246
236
  def self.build_result: (Hash[Symbol, untyped] data, String buffer, Array[untyped] hpack_table, Hash[Integer, Hash[Symbol, untyped]] streams, Array[String] outgoing_frames, Array[Hash[Symbol, untyped]] completed_requests, Array[[ Integer, Integer ]] window_updates, Integer? peer_initial_window_size, Integer connection_window, bool preface_received, Integer last_client_stream_id, Hash[Symbol, untyped]? pending_headers, bool close_connection) -> Hash[Symbol, untyped]
247
237
 
248
- # Handles a parsed HTTP/2 request from the ractor pool.
249
- #
250
- # Writes outgoing protocol frames to the socket, updates reactor state,
251
- # and dispatches completed stream requests to the thread pool. Eagerly
252
- # consumes subsequent frame batches that are already buffered, skipping
253
- # the reactor and ractor pool hops while the connection is hot.
238
+ # Handles a parsed HTTP/2 result. Writes outgoing frames, dispatches
239
+ # completed stream requests to the thread pool, and eagerly consumes
240
+ # further buffered frame batches before returning control to the
241
+ # reactor.
254
242
  #
255
243
  # @param result [Hash] the parsed result from the ractor pool
256
244
  # @param reactor [Reactor] the reactor managing the connection
@@ -296,10 +284,8 @@ module Raptor
296
284
  # @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, FlowControl flow_control, Integer stream_id, Array[[String, String]] headers, String body, remote_addr: String) -> void
297
285
  def dispatch_stream_request: (OpenSSL::SSL::SSLSocket socket, Writer writer, FlowControl flow_control, Integer stream_id, Array[[ String, String ]] headers, String body, remote_addr: String) -> void
298
286
 
299
- # Writes a Rack response as HTTP/2 frames to the socket.
300
- #
301
- # DATA frames are partitioned through `flow_control` so each write fits
302
- # within the peer's per-stream and connection windows.
287
+ # Writes a Rack response as HTTP/2 frames to the socket, partitioning
288
+ # DATA frames through `flow_control` to fit within the peer's windows.
303
289
  #
304
290
  # @param socket [OpenSSL::SSL::SSLSocket] the connection socket
305
291
  # @param writer [Writer] lock-free frame writer for the connection
@@ -337,9 +323,6 @@ module Raptor
337
323
 
338
324
  # Builds a Rack environment hash from HTTP/2 headers and body.
339
325
  #
340
- # Translates HTTP/2 pseudo-headers into Rack-compatible environment keys
341
- # and populates all required Rack env entries.
342
- #
343
326
  # @param headers [Array<Array(String, String)>] HTTP/2 header pairs
344
327
  # @param body [String] the request body
345
328
  # @param remote_addr [String] the client IP address