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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +10 -10
- data/docs/raptor-vs-puma.md +29 -29
- data/ext/raptor_bpf/reuseport_select.bpf.c +8 -4
- data/lib/raptor/binder.rb +37 -87
- data/lib/raptor/cli.rb +9 -25
- data/lib/raptor/cluster.rb +17 -53
- data/lib/raptor/http.rb +18 -0
- data/lib/raptor/http1.rb +244 -233
- data/lib/raptor/http2.rb +42 -63
- data/lib/raptor/reactor.rb +21 -52
- data/lib/raptor/reuseport_bpf.rb +14 -5
- data/lib/raptor/server.rb +10 -36
- data/lib/raptor/stats.rb +3 -8
- data/lib/raptor/systemd.rb +1 -1
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/binder.rbs +34 -80
- data/sig/generated/raptor/cli.rbs +9 -25
- data/sig/generated/raptor/cluster.rbs +10 -51
- data/sig/generated/raptor/http.rbs +10 -0
- data/sig/generated/raptor/http1.rbs +129 -113
- data/sig/generated/raptor/http2.rbs +31 -48
- data/sig/generated/raptor/reactor.rbs +21 -52
- data/sig/generated/raptor/reuseport_bpf.rbs +11 -2
- data/sig/generated/raptor/server.rbs +9 -35
- data/sig/generated/raptor/stats.rbs +3 -8
- metadata +1 -1
|
@@ -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
|
-
|
|
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
|
-
@
|
|
120
|
+
@env_template: Hash[String, untyped]
|
|
109
121
|
|
|
110
|
-
@
|
|
122
|
+
@running: AtomicBoolean
|
|
111
123
|
|
|
112
|
-
@
|
|
124
|
+
@on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
|
|
113
125
|
|
|
114
|
-
@
|
|
126
|
+
@access_log_io: IO?
|
|
127
|
+
|
|
128
|
+
@max_keepalive_requests: Integer
|
|
115
129
|
|
|
116
130
|
@body_spool_threshold: Integer?
|
|
117
131
|
|
|
118
|
-
@
|
|
132
|
+
@max_body_size: Integer?
|
|
119
133
|
|
|
120
|
-
@
|
|
134
|
+
@write_timeout: Integer
|
|
121
135
|
|
|
122
|
-
@
|
|
136
|
+
@server_port_string: String
|
|
123
137
|
|
|
124
|
-
@
|
|
138
|
+
@server_port: Integer
|
|
125
139
|
|
|
126
|
-
@
|
|
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
|
-
#
|
|
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
|
|
226
|
-
# `Expect: 100-continue
|
|
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
|
|
257
|
-
#
|
|
258
|
-
#
|
|
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
|
-
#
|
|
273
|
-
#
|
|
274
|
-
#
|
|
275
|
-
#
|
|
276
|
-
#
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
#
|
|
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
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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`.
|
|
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
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
|
599
|
-
#
|
|
600
|
-
#
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
#
|
|
167
|
+
# Returns the initial server SETTINGS frame to send on every new
|
|
168
|
+
# HTTP/2 connection.
|
|
177
169
|
#
|
|
178
|
-
# @return [String]
|
|
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
|
-
#
|
|
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.
|
|
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
|
|
249
|
-
#
|
|
250
|
-
#
|
|
251
|
-
#
|
|
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
|