raptor 0.2.0 → 0.4.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 +23 -0
- data/README.md +16 -16
- data/ext/raptor_http2/raptor_http2.c +1 -0
- data/lib/rackup/handler/raptor.rb +24 -11
- data/lib/raptor/binder.rb +1 -0
- data/lib/raptor/cli.rb +100 -1
- data/lib/raptor/cluster.rb +96 -26
- data/lib/raptor/http2.rb +333 -44
- data/lib/raptor/reactor.rb +67 -27
- data/lib/raptor/request.rb +196 -69
- data/lib/raptor/server.rb +112 -36
- data/lib/raptor/version.rb +1 -1
- data/lib/raptor.rb +3 -3
- data/sig/generated/raptor/cli.rbs +51 -0
- data/sig/generated/raptor/cluster.rbs +31 -3
- data/sig/generated/raptor/http2.rbs +130 -8
- data/sig/generated/raptor/reactor.rbs +22 -0
- data/sig/generated/raptor/request.rbs +75 -22
- data/sig/generated/raptor/server.rbs +51 -12
- data/sig/generated/raptor.rbs +3 -3
- metadata +1 -1
|
@@ -33,7 +33,11 @@ module Raptor
|
|
|
33
33
|
|
|
34
34
|
STATUS_WITH_NO_ENTITY_BODY: untyped
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
BAD_REQUEST_RESPONSE: ::String
|
|
37
|
+
|
|
38
|
+
INTERNAL_SERVER_ERROR_RESPONSE: ::String
|
|
39
|
+
|
|
40
|
+
CONTENT_TOO_LARGE_RESPONSE: ::String
|
|
37
41
|
|
|
38
42
|
CONNECTION_CLOSE: ::String
|
|
39
43
|
|
|
@@ -65,15 +69,37 @@ module Raptor
|
|
|
65
69
|
|
|
66
70
|
# Decodes a chunked transfer-encoded body buffer.
|
|
67
71
|
#
|
|
68
|
-
# Returns the decoded bytes and a
|
|
69
|
-
# zero-length chunk was found
|
|
70
|
-
#
|
|
72
|
+
# Returns the decoded bytes and a state symbol: `:complete` when the
|
|
73
|
+
# terminating zero-length chunk was found, `:too_large` when the decoded
|
|
74
|
+
# size would exceed `max_size`, or `:incomplete` otherwise.
|
|
71
75
|
#
|
|
72
76
|
# @param buffer [String] the raw body buffer to decode
|
|
73
|
-
# @
|
|
77
|
+
# @param max_size [Integer, nil] maximum decoded body size, or nil for unlimited
|
|
78
|
+
# @return [Array(String, Symbol)] decoded body and completion state
|
|
79
|
+
#
|
|
80
|
+
# @rbs (String buffer, ?Integer? max_size) -> [String, Symbol]
|
|
81
|
+
def self.decode_chunked: (String buffer, ?Integer? max_size) -> [ String, Symbol ]
|
|
82
|
+
|
|
83
|
+
# Writes a string to the socket, retrying on partial writes and flow control blocks.
|
|
84
|
+
#
|
|
85
|
+
# Uses write_nonblock with `WRITE_TIMEOUT` to avoid blocking the thread
|
|
86
|
+
# indefinitely on slow clients.
|
|
74
87
|
#
|
|
75
|
-
# @
|
|
76
|
-
|
|
88
|
+
# @param socket [TCPSocket] the socket to write to
|
|
89
|
+
# @param string [String] the data to write
|
|
90
|
+
# @return [void]
|
|
91
|
+
# @raise [WriteError] if the socket is not writable within the timeout or raises IOError
|
|
92
|
+
#
|
|
93
|
+
# @rbs (TCPSocket socket, String string) -> void
|
|
94
|
+
def self.socket_write: (TCPSocket socket, String string) -> void
|
|
95
|
+
|
|
96
|
+
@running: AtomicBoolean
|
|
97
|
+
|
|
98
|
+
@on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
|
|
99
|
+
|
|
100
|
+
@body_spool_threshold: Integer?
|
|
101
|
+
|
|
102
|
+
@max_body_size: Integer?
|
|
77
103
|
|
|
78
104
|
@server_port: Integer
|
|
79
105
|
|
|
@@ -83,10 +109,22 @@ module Raptor
|
|
|
83
109
|
#
|
|
84
110
|
# @param app [#call] the Rack application to dispatch complete requests to
|
|
85
111
|
# @param server_port [Integer] port number used to populate SERVER_PORT in the Rack env
|
|
112
|
+
# @param client_options [Hash] client limits configuration
|
|
113
|
+
# @option client_options [Integer, nil] :max_body_size maximum request body size in bytes
|
|
114
|
+
# @option client_options [Integer, nil] :body_spool_threshold spool bodies larger than this to a tempfile
|
|
115
|
+
# @param on_error [#call, nil] callback invoked with (env, exception) when the Rack app raises
|
|
86
116
|
# @return [void]
|
|
87
117
|
#
|
|
88
|
-
# @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port) -> void
|
|
89
|
-
def initialize: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Integer server_port) -> void
|
|
118
|
+
# @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
|
|
119
|
+
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
|
|
120
|
+
|
|
121
|
+
# Signals eager keep-alive loops to stop processing further requests on
|
|
122
|
+
# their connections. In-flight requests complete normally.
|
|
123
|
+
#
|
|
124
|
+
# @return [void]
|
|
125
|
+
#
|
|
126
|
+
# @rbs () -> void
|
|
127
|
+
def shutdown: () -> void
|
|
90
128
|
|
|
91
129
|
# Eagerly reads and parses the first request on a freshly accepted
|
|
92
130
|
# connection on the server thread, dispatching directly to the thread pool
|
|
@@ -203,6 +241,24 @@ module Raptor
|
|
|
203
241
|
# @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
|
|
204
242
|
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
|
|
205
243
|
|
|
244
|
+
# Writes a 413 response and closes the socket. Used when a request body
|
|
245
|
+
# exceeds the configured maximum size.
|
|
246
|
+
#
|
|
247
|
+
# @param socket [TCPSocket] the client socket
|
|
248
|
+
# @return [void]
|
|
249
|
+
#
|
|
250
|
+
# @rbs (TCPSocket socket) -> void
|
|
251
|
+
def reject_oversized: (TCPSocket socket) -> void
|
|
252
|
+
|
|
253
|
+
# Writes a 400 response and closes the socket. Used when the HTTP parser
|
|
254
|
+
# rejects the request line or headers.
|
|
255
|
+
#
|
|
256
|
+
# @param socket [TCPSocket] the client socket
|
|
257
|
+
# @return [void]
|
|
258
|
+
#
|
|
259
|
+
# @rbs (TCPSocket socket) -> void
|
|
260
|
+
def reject_malformed: (TCPSocket socket) -> void
|
|
261
|
+
|
|
206
262
|
# Builds a Rack environment hash from parsed HTTP request data.
|
|
207
263
|
#
|
|
208
264
|
# Populates all required Rack env keys including rack.* keys, REMOTE_ADDR,
|
|
@@ -219,6 +275,16 @@ module Raptor
|
|
|
219
275
|
# @rbs (Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, TCPSocket socket, ?remote_addr: String, ?url_scheme: String) -> Hash[String, untyped]
|
|
220
276
|
def build_rack_env: (Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, TCPSocket socket, ?remote_addr: String, ?url_scheme: String) -> Hash[String, untyped]
|
|
221
277
|
|
|
278
|
+
# Builds the `rack.input` IO object for the request body. Returns an
|
|
279
|
+
# in-memory StringIO for bodies up to the spool threshold, or a Tempfile
|
|
280
|
+
# for larger bodies to bound per-worker memory.
|
|
281
|
+
#
|
|
282
|
+
# @param body [String, nil] decoded request body
|
|
283
|
+
# @return [IO] an IO-like object positioned at the start of the body
|
|
284
|
+
#
|
|
285
|
+
# @rbs (String? body) -> IO
|
|
286
|
+
def build_rack_input: (String? body) -> IO
|
|
287
|
+
|
|
222
288
|
# Determines whether the connection should be kept alive after the response.
|
|
223
289
|
#
|
|
224
290
|
# Returns false if the request limit has been reached. For HTTP/1.1, keep-alive
|
|
@@ -467,19 +533,6 @@ module Raptor
|
|
|
467
533
|
# @rbs (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
|
|
468
534
|
def call_response_finished: (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
|
|
469
535
|
|
|
470
|
-
# Writes a string to the socket, retrying on partial writes and flow control blocks.
|
|
471
|
-
#
|
|
472
|
-
# Uses write_nonblock with a 5-second writable timeout to avoid blocking the
|
|
473
|
-
# thread indefinitely on slow clients.
|
|
474
|
-
#
|
|
475
|
-
# @param socket [TCPSocket] the socket to write to
|
|
476
|
-
# @param string [String] the data to write
|
|
477
|
-
# @return [void]
|
|
478
|
-
# @raise [WriteError] if the socket is not writable within the timeout or raises IOError
|
|
479
|
-
#
|
|
480
|
-
# @rbs (TCPSocket socket, String string) -> void
|
|
481
|
-
def socket_write: (TCPSocket socket, String string) -> void
|
|
482
|
-
|
|
483
536
|
# Enables TCP_CORK on the socket to batch outgoing packets into fewer segments.
|
|
484
537
|
#
|
|
485
538
|
# Only applies to TCP sockets. No-op on non-TCP sockets.
|
|
@@ -9,8 +9,8 @@ module Raptor
|
|
|
9
9
|
# providing natural backpressure based on system capacity.
|
|
10
10
|
#
|
|
11
11
|
# Supports TCP, Unix domain, and SSL listeners transparently. TCP_NODELAY is
|
|
12
|
-
# applied only to TCP sockets, and SSL handshakes are
|
|
13
|
-
#
|
|
12
|
+
# applied only to TCP sockets, and SSL handshakes are offloaded to the thread
|
|
13
|
+
# pool so a slow client cannot block the server thread.
|
|
14
14
|
#
|
|
15
15
|
# For HTTP/1.1 connections the first request is parsed inline on the server
|
|
16
16
|
# thread and dispatched directly to the thread pool, falling back to the
|
|
@@ -22,7 +22,7 @@ module Raptor
|
|
|
22
22
|
# binder = Binder.new(["tcp://0.0.0.0:3000"])
|
|
23
23
|
# reactor = Reactor.new(thread_pool, ractor_pool, client_options: {})
|
|
24
24
|
# request = Request.new(app, 3000)
|
|
25
|
-
# server = Server.new(binder, reactor, thread_pool, request)
|
|
25
|
+
# server = Server.new(binder, reactor, thread_pool, request, client_options: { first_data_timeout: 30 })
|
|
26
26
|
# server.run
|
|
27
27
|
# # ... later
|
|
28
28
|
# server.shutdown
|
|
@@ -33,15 +33,17 @@ module Raptor
|
|
|
33
33
|
|
|
34
34
|
H2_PROTOCOL: ::String
|
|
35
35
|
|
|
36
|
-
@
|
|
36
|
+
@running: AtomicBoolean
|
|
37
37
|
|
|
38
|
-
@
|
|
38
|
+
@client_options: Hash[Symbol, untyped]
|
|
39
|
+
|
|
40
|
+
@request: Request
|
|
39
41
|
|
|
40
42
|
@thread_pool: AtomicThreadPool
|
|
41
43
|
|
|
42
|
-
@
|
|
44
|
+
@reactor: Reactor
|
|
43
45
|
|
|
44
|
-
@
|
|
46
|
+
@binder: Binder
|
|
45
47
|
|
|
46
48
|
# Creates a new Server instance.
|
|
47
49
|
#
|
|
@@ -49,10 +51,11 @@ module Raptor
|
|
|
49
51
|
# @param reactor [Reactor] the reactor for handling client connections
|
|
50
52
|
# @param thread_pool [AtomicThreadPool] thread pool for application processing
|
|
51
53
|
# @param request [Request] the HTTP/1.1 request handler
|
|
54
|
+
# @param client_options [Hash] client timeout configuration, used to bound TLS handshakes
|
|
52
55
|
# @return [void]
|
|
53
56
|
#
|
|
54
|
-
# @rbs (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Request request) -> void
|
|
55
|
-
def initialize: (Binder binder, Reactor reactor, AtomicThreadPool thread_pool, Request request) -> void
|
|
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
|
|
56
59
|
|
|
57
60
|
# Starts the server's main accept loop in a new thread.
|
|
58
61
|
#
|
|
@@ -80,9 +83,11 @@ module Raptor
|
|
|
80
83
|
|
|
81
84
|
# Accepts a connection from the given listener and dispatches it.
|
|
82
85
|
#
|
|
83
|
-
# For SSL
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
+
# For SSL listeners the TLS handshake is offloaded to the thread pool so
|
|
87
|
+
# a slow client cannot block the server thread. For SSL connections with
|
|
88
|
+
# h2 negotiated via ALPN, the server sends initial SETTINGS and adds the
|
|
89
|
+
# connection to the reactor as an HTTP/2 connection. All other connections
|
|
90
|
+
# follow the HTTP/1.1 path.
|
|
86
91
|
#
|
|
87
92
|
# @param listener [TCPServer, UNIXServer, Binder::SslListener] the ready listener
|
|
88
93
|
# @param reactor [Reactor] the reactor to dispatch connections to
|
|
@@ -90,5 +95,39 @@ module Raptor
|
|
|
90
95
|
#
|
|
91
96
|
# @rbs (TCPServer | UNIXServer | Binder::SslListener listener, Reactor reactor) -> void
|
|
92
97
|
def accept_connection: (TCPServer | UNIXServer | Binder::SslListener listener, Reactor reactor) -> void
|
|
98
|
+
|
|
99
|
+
# Performs the TLS handshake for an accepted SSL connection and dispatches
|
|
100
|
+
# it through the HTTP/2 or HTTP/1.1 path. The handshake is bounded by
|
|
101
|
+
# `:first_data_timeout` so a slow client cannot pin a worker thread.
|
|
102
|
+
#
|
|
103
|
+
# @param listener [Binder::SslListener] the SSL listener that accepted the connection
|
|
104
|
+
# @param tcp_client [TCPSocket] the accepted TCP socket
|
|
105
|
+
# @param remote_addr [String] the client's IP address
|
|
106
|
+
# @param reactor [Reactor] the reactor to dispatch the connection to
|
|
107
|
+
# @return [void]
|
|
108
|
+
#
|
|
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
|
|
111
|
+
|
|
112
|
+
# Drives a non-blocking SSL handshake to completion, bounded by the
|
|
113
|
+
# configured first-data timeout. Returns true on success, false on
|
|
114
|
+
# timeout or SSL error.
|
|
115
|
+
#
|
|
116
|
+
# @param ssl_socket [OpenSSL::SSL::SSLSocket] the SSL socket to hand-shake
|
|
117
|
+
# @return [Boolean] true if the handshake completed
|
|
118
|
+
#
|
|
119
|
+
# @rbs (OpenSSL::SSL::SSLSocket ssl_socket) -> bool
|
|
120
|
+
def perform_ssl_handshake: (OpenSSL::SSL::SSLSocket ssl_socket) -> bool
|
|
121
|
+
|
|
122
|
+
# Waits up to `deadline` for the socket to become ready for the next step
|
|
123
|
+
# of the SSL handshake. Closes the socket and returns false on timeout.
|
|
124
|
+
#
|
|
125
|
+
# @param ssl_socket [OpenSSL::SSL::SSLSocket] the SSL socket
|
|
126
|
+
# @param deadline [Float] absolute monotonic deadline
|
|
127
|
+
# @param direction [Symbol] either `:read` or `:write`
|
|
128
|
+
# @return [Boolean] true if the socket became ready before the deadline
|
|
129
|
+
#
|
|
130
|
+
# @rbs (OpenSSL::SSL::SSLSocket ssl_socket, Float deadline, Symbol direction) -> bool
|
|
131
|
+
def wait_for_handshake: (OpenSSL::SSL::SSLSocket ssl_socket, Float deadline, Symbol direction) -> bool
|
|
93
132
|
end
|
|
94
133
|
end
|
data/sig/generated/raptor.rbs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# Main module for the Raptor web server.
|
|
4
4
|
#
|
|
5
|
-
# Raptor is a high-performance, multi-
|
|
6
|
-
#
|
|
7
|
-
# extensions for HTTP parsing and HPACK compression, and NIO for non-blocking I/O.
|
|
5
|
+
# Raptor is a high-performance, preloading, multi-process, multi-threaded Ruby 4+ web server
|
|
6
|
+
# implementing Rack 3+, leveraging Ractors for parallel HTTP/1.1 and HTTP/2 request processing,
|
|
7
|
+
# native C extensions for HTTP parsing and HPACK compression, and NIO for non-blocking I/O.
|
|
8
8
|
module Raptor
|
|
9
9
|
end
|