raptor 0.7.0 → 0.9.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/.buildkite/pipeline.yml +90 -32
- data/.dockerignore +5 -0
- data/CHANGELOG.md +30 -0
- data/Dockerfile +28 -0
- data/README.md +159 -27
- data/Rakefile +19 -1
- data/docs/raptor-vs-puma.md +557 -0
- data/ext/raptor_bpf/reuseport_select.bpf.c +63 -0
- data/ext/raptor_http/extconf.rb +1 -1
- data/ext/raptor_native/extconf.rb +7 -0
- data/ext/raptor_native/raptor_native.c +99 -0
- data/lib/rackup/handler/raptor.rb +12 -2
- data/lib/raptor/binder.rb +139 -31
- data/lib/raptor/cli.rb +83 -22
- data/lib/raptor/cluster.rb +214 -33
- data/lib/raptor/http.rb +116 -0
- data/lib/raptor/{request.rb → http1.rb} +228 -99
- data/lib/raptor/http2.rb +85 -40
- data/lib/raptor/reactor.rb +22 -15
- data/lib/raptor/reuseport_bpf.rb +108 -0
- data/lib/raptor/server.rb +92 -43
- data/lib/raptor/stats.rb +1 -1
- data/lib/raptor/systemd.rb +69 -0
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/binder.rbs +82 -5
- data/sig/generated/raptor/cli.rbs +2 -3
- data/sig/generated/raptor/cluster.rbs +91 -13
- data/sig/generated/raptor/http.rbs +65 -0
- data/sig/generated/raptor/{request.rbs → http1.rbs} +122 -35
- data/sig/generated/raptor/http2.rbs +46 -13
- data/sig/generated/raptor/reactor.rbs +18 -11
- data/sig/generated/raptor/reuseport_bpf.rbs +56 -0
- data/sig/generated/raptor/server.rbs +49 -21
- data/sig/generated/raptor/systemd.rbs +42 -0
- metadata +30 -3
|
@@ -26,10 +26,12 @@ module Raptor
|
|
|
26
26
|
# workers: 4, ractors: 2, threads: 8,
|
|
27
27
|
# binds: ["tcp://0.0.0.0:3000"],
|
|
28
28
|
# rackup: "config.ru",
|
|
29
|
-
#
|
|
29
|
+
# connection: { first_data_timeout: 30, chunk_data_timeout: 10 }
|
|
30
30
|
# }
|
|
31
31
|
# Cluster.run(options)
|
|
32
32
|
class Cluster
|
|
33
|
+
INHERITED_FDS_ENV: ::String
|
|
34
|
+
|
|
33
35
|
# Convenience method to create and run a cluster with the given options.
|
|
34
36
|
#
|
|
35
37
|
# @param options [Hash] cluster configuration options
|
|
@@ -38,13 +40,13 @@ module Raptor
|
|
|
38
40
|
# @rbs (Hash[Symbol, untyped] options) -> void
|
|
39
41
|
def self.run: (Hash[Symbol, untyped] options) -> void
|
|
40
42
|
|
|
41
|
-
@
|
|
43
|
+
@http2_options: Hash[Symbol, untyped]
|
|
42
44
|
|
|
43
|
-
@
|
|
45
|
+
@worker_boot_timeout: Integer
|
|
44
46
|
|
|
45
47
|
@worker_timeout: Integer
|
|
46
48
|
|
|
47
|
-
@
|
|
49
|
+
@worker_drain_timeout: Integer
|
|
48
50
|
|
|
49
51
|
@worker_shutdown_timeout: Integer
|
|
50
52
|
|
|
@@ -52,6 +54,18 @@ module Raptor
|
|
|
52
54
|
|
|
53
55
|
@pid_file: String?
|
|
54
56
|
|
|
57
|
+
@stdout_file: String?
|
|
58
|
+
|
|
59
|
+
@stderr_file: String?
|
|
60
|
+
|
|
61
|
+
@access_log_file: String?
|
|
62
|
+
|
|
63
|
+
@access_log_io: IO?
|
|
64
|
+
|
|
65
|
+
@launch_command: String?
|
|
66
|
+
|
|
67
|
+
@launch_argv: Array[String]?
|
|
68
|
+
|
|
55
69
|
@on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
|
|
56
70
|
|
|
57
71
|
@binder: Binder
|
|
@@ -74,10 +88,24 @@ module Raptor
|
|
|
74
88
|
|
|
75
89
|
@phased_restarting: bool
|
|
76
90
|
|
|
91
|
+
@hot_restart_requested: bool
|
|
92
|
+
|
|
93
|
+
@bpf_active: bool
|
|
94
|
+
|
|
95
|
+
@http1_options: Hash[Symbol, untyped]
|
|
96
|
+
|
|
97
|
+
@connection_options: Hash[Symbol, untyped]
|
|
98
|
+
|
|
99
|
+
@environment: String
|
|
100
|
+
|
|
101
|
+
@thread_count: Integer
|
|
102
|
+
|
|
77
103
|
@ractor_count: Integer
|
|
78
104
|
|
|
79
105
|
@worker_count: Integer
|
|
80
106
|
|
|
107
|
+
@drain_accept_queue: bool
|
|
108
|
+
|
|
81
109
|
# Creates a new Cluster with the specified configuration.
|
|
82
110
|
#
|
|
83
111
|
# Initializes the cluster with worker, ractor, and thread counts,
|
|
@@ -86,18 +114,30 @@ module Raptor
|
|
|
86
114
|
#
|
|
87
115
|
# @param options [Hash] cluster configuration options
|
|
88
116
|
# @option options [Array<String>] :binds array of bind URIs
|
|
117
|
+
# @option options [Integer] :socket_backlog kernel listen() queue depth for TCP/SSL listeners
|
|
118
|
+
# @option options [Boolean] :drain_accept_queue whether to drain the kernel accept queue on shutdown
|
|
89
119
|
# @option options [Integer] :workers number of worker processes
|
|
90
120
|
# @option options [Integer] :ractors number of ractors per worker process
|
|
91
121
|
# @option options [Integer] :threads number of threads per worker process
|
|
92
122
|
# @option options [#call] :app pre-built Rack application
|
|
93
123
|
# @option options [String] :rackup path to Rack configuration file
|
|
94
|
-
# @option options [
|
|
95
|
-
# @option options [
|
|
124
|
+
# @option options [String, nil] :chdir directory to change to before loading the Rack application, or nil to leave the working directory unchanged
|
|
125
|
+
# @option options [String, nil] :environment Raptor's application environment label; falls back to `$RAILS_ENV`, then `$RACK_ENV`, then `"development"`
|
|
126
|
+
# @option options [Hash] :connection per-connection settings shared across protocols
|
|
127
|
+
# @option options [Hash] :http1 HTTP/1.1-specific settings
|
|
128
|
+
# @option options [Hash] :http2 HTTP/2-specific settings
|
|
96
129
|
# @option options [Integer] :worker_boot_timeout seconds to wait for a worker to finish booting before killing it
|
|
130
|
+
# @option options [Integer] :worker_timeout seconds to wait for a booted worker to check in before killing it
|
|
131
|
+
# @option options [Integer] :worker_drain_timeout seconds a worker waits for in-flight requests during shutdown before force-killing app threads
|
|
97
132
|
# @option options [Integer] :worker_shutdown_timeout seconds to wait for graceful worker exit before force-killing
|
|
98
133
|
# @option options [String, nil] :stats_file path to write per-worker stats JSON, or nil to disable
|
|
99
134
|
# @option options [String, nil] :pid_file path to write the master PID to, or nil to disable
|
|
100
|
-
# @option options [
|
|
135
|
+
# @option options [String, nil] :stdout_file path to redirect stdout to, reopened on SIGHUP, or nil to disable
|
|
136
|
+
# @option options [String, nil] :stderr_file path to redirect stderr to, reopened on SIGHUP, or nil to disable
|
|
137
|
+
# @option options [String, nil] :access_log_file path to write Common Log Format access logs to, reopened on SIGHUP, or nil to disable
|
|
138
|
+
# @option options [String, nil] :launch_command path of the program to re-exec on hot restart, or nil to disable
|
|
139
|
+
# @option options [Array<String>, nil] :launch_argv command-line arguments for the hot-restart exec, or nil to disable
|
|
140
|
+
# @option options [#call, nil] :on_error callback invoked with (env, exception) when the Rack app raises
|
|
101
141
|
# @return [void]
|
|
102
142
|
#
|
|
103
143
|
# @rbs (Hash[Symbol, untyped] options) -> void
|
|
@@ -107,8 +147,8 @@ module Raptor
|
|
|
107
147
|
#
|
|
108
148
|
# Forks the configured number of worker processes and monitors them,
|
|
109
149
|
# restarting any that exit unexpectedly or stop checking in. Handles
|
|
110
|
-
# graceful shutdown via INT or TERM signals,
|
|
111
|
-
# and
|
|
150
|
+
# graceful shutdown via INT or TERM signals, phased restart via USR1,
|
|
151
|
+
# and hot restart via USR2.
|
|
112
152
|
#
|
|
113
153
|
# Each worker process includes:
|
|
114
154
|
# - 1 server thread (continuously accepts connections with backpressure control)
|
|
@@ -133,6 +173,18 @@ module Raptor
|
|
|
133
173
|
|
|
134
174
|
private
|
|
135
175
|
|
|
176
|
+
# Returns the inherited-FDs hash for a systemd socket-activation handoff,
|
|
177
|
+
# pairing each bind URI with the FD systemd passed at the same index.
|
|
178
|
+
# The activation is skipped (with a warning) when the FD count doesn't
|
|
179
|
+
# match the number of bind URIs.
|
|
180
|
+
#
|
|
181
|
+
# @param bind_uris [Array<String>] the configured bind URIs
|
|
182
|
+
# @param filenos [Array<Integer>] file descriptors passed by systemd
|
|
183
|
+
# @return [Hash{String => Array<Integer>}]
|
|
184
|
+
#
|
|
185
|
+
# @rbs (Array[String] bind_uris, Array[Integer] filenos) -> Hash[String, Array[Integer]]
|
|
186
|
+
def pair_systemd_fds: (Array[String] bind_uris, Array[Integer] filenos) -> Hash[String, Array[Integer]]
|
|
187
|
+
|
|
136
188
|
# Forks a new worker process and registers it at the given index.
|
|
137
189
|
# The worker inherits the cluster's current phase.
|
|
138
190
|
#
|
|
@@ -170,13 +222,22 @@ module Raptor
|
|
|
170
222
|
def timeout_hung_workers: () -> void
|
|
171
223
|
|
|
172
224
|
# Replaces each worker process one at a time, waiting for the new
|
|
173
|
-
# worker to boot before moving on to the next.
|
|
225
|
+
# worker to boot before moving on to the next.
|
|
174
226
|
#
|
|
175
227
|
# @return [void]
|
|
176
228
|
#
|
|
177
229
|
# @rbs () -> void
|
|
178
230
|
def perform_phased_restart: () -> void
|
|
179
231
|
|
|
232
|
+
# Re-execs the master process with a fresh boot of the same Raptor
|
|
233
|
+
# invocation, handing the new master its listening sockets so accepted
|
|
234
|
+
# connections continue to be served across the swap.
|
|
235
|
+
#
|
|
236
|
+
# @return [void]
|
|
237
|
+
#
|
|
238
|
+
# @rbs () -> void
|
|
239
|
+
def perform_hot_restart: () -> void
|
|
240
|
+
|
|
180
241
|
# Runs the full server stack inside a worker process.
|
|
181
242
|
#
|
|
182
243
|
# Sets up and coordinates the reactor, server, ractor pool, thread pool,
|
|
@@ -190,6 +251,16 @@ module Raptor
|
|
|
190
251
|
# @rbs (Integer index, Integer phase) -> void
|
|
191
252
|
def run_worker: (Integer index, Integer phase) -> void
|
|
192
253
|
|
|
254
|
+
# Shuts down the worker's application thread pool, force-killing the
|
|
255
|
+
# underlying threads if in-flight requests have not finished within
|
|
256
|
+
# `worker_drain_timeout` seconds.
|
|
257
|
+
#
|
|
258
|
+
# @param thread_pool [AtomicThreadPool] the worker's thread pool
|
|
259
|
+
# @return [void]
|
|
260
|
+
#
|
|
261
|
+
# @rbs (AtomicThreadPool thread_pool) -> void
|
|
262
|
+
def drain_thread_pool: (AtomicThreadPool thread_pool) -> void
|
|
263
|
+
|
|
193
264
|
# Returns a human-readable description of how a process exited.
|
|
194
265
|
#
|
|
195
266
|
# @param status [Process::Status] the exit status of the process
|
|
@@ -213,14 +284,21 @@ module Raptor
|
|
|
213
284
|
# @rbs () -> void
|
|
214
285
|
def log_initialization: () -> void
|
|
215
286
|
|
|
216
|
-
#
|
|
287
|
+
# Redirects `$stdout`, `$stderr`, and the access log to their configured
|
|
288
|
+
# paths. No-op for any stream whose target path is nil.
|
|
217
289
|
#
|
|
218
|
-
#
|
|
290
|
+
# @return [void]
|
|
291
|
+
#
|
|
292
|
+
# @rbs () -> void
|
|
293
|
+
def reopen_logs: () -> void
|
|
294
|
+
|
|
295
|
+
# Reopens the master's log files and forwards SIGHUP to each worker so
|
|
296
|
+
# they reopen their own inherited file descriptors.
|
|
219
297
|
#
|
|
220
298
|
# @return [void]
|
|
221
299
|
#
|
|
222
300
|
# @rbs () -> void
|
|
223
|
-
def
|
|
301
|
+
def reopen_logs_and_signal_workers: () -> void
|
|
224
302
|
|
|
225
303
|
# Writes the stats file on a 1-second interval until shutdown.
|
|
226
304
|
#
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Generated from lib/raptor/http.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module Raptor
|
|
4
|
+
# Shared HTTP utilities used by both the HTTP/1.x and HTTP/2 handlers:
|
|
5
|
+
# Rack env keys that aren't provided by Rack itself, low-level socket
|
|
6
|
+
# writing, and Common Log Format access-log formatting.
|
|
7
|
+
module Http
|
|
8
|
+
WRITE_TIMEOUT: ::Integer
|
|
9
|
+
|
|
10
|
+
CONTENT_LENGTH: ::String
|
|
11
|
+
|
|
12
|
+
CONTENT_TYPE: ::String
|
|
13
|
+
|
|
14
|
+
HTTP_VERSION: ::String
|
|
15
|
+
|
|
16
|
+
REMOTE_ADDR: ::String
|
|
17
|
+
|
|
18
|
+
SERVER_SOFTWARE: ::String
|
|
19
|
+
|
|
20
|
+
SERVER_SOFTWARE_VALUE: untyped
|
|
21
|
+
|
|
22
|
+
class WriteError < StandardError
|
|
23
|
+
# @rbs () -> String
|
|
24
|
+
def message: () -> String
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Writes `string` in full, retrying on partial writes. Bounded by
|
|
28
|
+
# `timeout` so a slow client can't pin the writing thread.
|
|
29
|
+
#
|
|
30
|
+
# @param socket [TCPSocket] the socket to write to
|
|
31
|
+
# @param string [String] the data to write
|
|
32
|
+
# @param timeout [Integer] seconds to wait for the socket to become writable on each partial write
|
|
33
|
+
# @return [void]
|
|
34
|
+
# @raise [WriteError] if the socket is not writable within the timeout or raises IOError
|
|
35
|
+
#
|
|
36
|
+
# @rbs (TCPSocket socket, String string, ?timeout: Integer) -> void
|
|
37
|
+
def self.socket_write: (TCPSocket socket, String string, ?timeout: Integer) -> void
|
|
38
|
+
|
|
39
|
+
# Writes `strings` in full via a single `writev(2)` syscall when possible,
|
|
40
|
+
# falling back to per-string writes on partial results. Bounded by
|
|
41
|
+
# `timeout` so a slow client can't pin the writing thread.
|
|
42
|
+
#
|
|
43
|
+
# @param socket [TCPSocket] the socket to write to
|
|
44
|
+
# @param strings [Array<String>] the buffers to write in order
|
|
45
|
+
# @param timeout [Integer] seconds to wait for the socket to become writable on each retry
|
|
46
|
+
# @return [void]
|
|
47
|
+
# @raise [WriteError] if the socket is not writable within the timeout or raises IOError
|
|
48
|
+
#
|
|
49
|
+
# @rbs (TCPSocket socket, Array[String] strings, ?timeout: Integer) -> void
|
|
50
|
+
def self.socket_writev: (TCPSocket socket, Array[String] strings, ?timeout: Integer) -> void
|
|
51
|
+
|
|
52
|
+
# Writes a Common Log Format entry to `io`. Write failures are silently
|
|
53
|
+
# ignored.
|
|
54
|
+
#
|
|
55
|
+
# @param io [IO] the destination IO
|
|
56
|
+
# @param env [Hash] the Rack environment
|
|
57
|
+
# @param status [Integer] the response status code
|
|
58
|
+
# @param size [String] the response body size in bytes, or `-` if unknown
|
|
59
|
+
# @param remote_addr [String] the client IP address
|
|
60
|
+
# @return [void]
|
|
61
|
+
#
|
|
62
|
+
# @rbs (IO io, Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
|
|
63
|
+
def self.write_access_log: (IO io, Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
# Generated from lib/raptor/
|
|
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
|
|
8
|
+
class Http1
|
|
9
9
|
BODY_BUFFER_THRESHOLD: untyped
|
|
10
10
|
|
|
11
11
|
FILE_CHUNK_SIZE: untyped
|
|
12
12
|
|
|
13
|
+
MAX_CHUNK_OVERHEAD: untyped
|
|
14
|
+
|
|
13
15
|
READ_BUFFER_SIZE: untyped
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
RESPONSE_BUFFER_CAPACITY: untyped
|
|
16
18
|
|
|
17
19
|
KEEPALIVE_READ_TIMEOUT: ::Float
|
|
18
20
|
|
|
@@ -28,20 +30,26 @@ module Raptor
|
|
|
28
30
|
|
|
29
31
|
STATUS_WITH_NO_ENTITY_BODY: untyped
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
CONTINUE_RESPONSE: ::String
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
BAD_REQUEST_RESPONSE: ::String
|
|
34
36
|
|
|
35
37
|
CONTENT_TOO_LARGE_RESPONSE: ::String
|
|
36
38
|
|
|
39
|
+
INTERNAL_SERVER_ERROR_RESPONSE: ::String
|
|
40
|
+
|
|
37
41
|
CONNECTION_CLOSE: ::String
|
|
38
42
|
|
|
39
43
|
CONNECTION_KEEPALIVE: ::String
|
|
40
44
|
|
|
45
|
+
EXPECT_100_CONTINUE: ::String
|
|
46
|
+
|
|
41
47
|
TRANSFER_ENCODING_CHUNKED: ::String
|
|
42
48
|
|
|
43
49
|
HTTP_CONNECTION: ::String
|
|
44
50
|
|
|
51
|
+
HTTP_EXPECT: ::String
|
|
52
|
+
|
|
45
53
|
HTTP_TRANSFER_ENCODING: ::String
|
|
46
54
|
|
|
47
55
|
RACK_HEADER_PREFIX: ::String
|
|
@@ -54,19 +62,24 @@ module Raptor
|
|
|
54
62
|
|
|
55
63
|
ILLEGAL_HEADER_VALUE_REGEX: ::Regexp
|
|
56
64
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
# Returns true when the message framing shows a request-smuggling vector
|
|
66
|
+
# per RFC 9112 section 6.3: a `Transfer-Encoding` where `chunked` is
|
|
67
|
+
# missing, not the final encoding, or duplicated; a `Transfer-Encoding`
|
|
68
|
+
# paired with a `Content-Length`; or a `Content-Length` containing any
|
|
69
|
+
# non-digit character.
|
|
70
|
+
#
|
|
71
|
+
# @param env [Hash] the Rack environment after header parsing
|
|
72
|
+
# @return [Boolean]
|
|
73
|
+
#
|
|
74
|
+
# @rbs (Hash[String, untyped] env) -> bool
|
|
75
|
+
def self.request_smuggling?: (Hash[String, untyped] env) -> bool
|
|
64
76
|
|
|
65
77
|
# Decodes a chunked transfer-encoded body buffer.
|
|
66
78
|
#
|
|
67
79
|
# Returns the decoded bytes and a state symbol: `:complete` when the
|
|
68
80
|
# terminating zero-length chunk was found, `:too_large` when the decoded
|
|
69
|
-
# size would exceed `max_size`,
|
|
81
|
+
# size would exceed `max_size`, `:malformed` when chunk framing overhead
|
|
82
|
+
# exceeds `MAX_CHUNK_OVERHEAD`, or `:incomplete` otherwise.
|
|
70
83
|
#
|
|
71
84
|
# @param buffer [String] the raw body buffer to decode
|
|
72
85
|
# @param max_size [Integer, nil] maximum decoded body size, or nil for unlimited
|
|
@@ -75,41 +88,62 @@ module Raptor
|
|
|
75
88
|
# @rbs (String buffer, ?Integer? max_size) -> [String, Symbol]
|
|
76
89
|
def self.decode_chunked: (String buffer, ?Integer? max_size) -> [ String, Symbol ]
|
|
77
90
|
|
|
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
91
|
@running: AtomicBoolean
|
|
90
92
|
|
|
91
93
|
@on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
|
|
92
94
|
|
|
95
|
+
@access_log_io: IO?
|
|
96
|
+
|
|
97
|
+
@max_keepalive_requests: Integer
|
|
98
|
+
|
|
93
99
|
@body_spool_threshold: Integer?
|
|
94
100
|
|
|
95
101
|
@max_body_size: Integer?
|
|
96
102
|
|
|
103
|
+
@write_timeout: Integer
|
|
104
|
+
|
|
97
105
|
@server_port: Integer
|
|
98
106
|
|
|
99
107
|
@app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
|
|
100
108
|
|
|
101
|
-
# Creates a new
|
|
109
|
+
# Creates a new Http1 handler.
|
|
102
110
|
#
|
|
103
111
|
# @param app [#call] the Rack application to dispatch complete requests to
|
|
104
112
|
# @param server_port [Integer] port number used to populate SERVER_PORT in the Rack env
|
|
105
|
-
# @param
|
|
106
|
-
# @option
|
|
107
|
-
# @option
|
|
113
|
+
# @param connection_options [Hash] per-connection settings shared across protocols
|
|
114
|
+
# @option connection_options [Integer] :write_timeout per-write socket timeout in seconds
|
|
115
|
+
# @option connection_options [Integer, nil] :max_body_size maximum request body size in bytes
|
|
116
|
+
# @option connection_options [Integer, nil] :body_spool_threshold spool bodies larger than this to a tempfile
|
|
117
|
+
# @param http1_options [Hash] HTTP/1.1-specific settings
|
|
118
|
+
# @option http1_options [Integer] :max_keepalive_requests maximum requests per HTTP/1.1 keep-alive connection
|
|
119
|
+
# @param access_log_io [IO, nil] IO to write Common Log Format access entries to, or nil to disable
|
|
108
120
|
# @param on_error [#call, nil] callback invoked with (env, exception) when the Rack app raises
|
|
109
121
|
# @return [void]
|
|
110
122
|
#
|
|
111
|
-
# @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port, ?
|
|
112
|
-
def initialize: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Integer server_port, ?
|
|
123
|
+
# @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
|
|
124
|
+
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
|
|
125
|
+
|
|
126
|
+
# Instance-level wrapper around {Http.socket_write} that applies the
|
|
127
|
+
# configured `write_timeout`.
|
|
128
|
+
#
|
|
129
|
+
# @param socket [TCPSocket] the socket to write to
|
|
130
|
+
# @param string [String] the data to write
|
|
131
|
+
# @return [void]
|
|
132
|
+
# @raise [Http::WriteError] if the socket is not writable within the timeout or raises IOError
|
|
133
|
+
#
|
|
134
|
+
# @rbs (TCPSocket socket, String string) -> void
|
|
135
|
+
def socket_write: (TCPSocket socket, String string) -> void
|
|
136
|
+
|
|
137
|
+
# Instance-level wrapper around {Http.socket_writev} that applies the
|
|
138
|
+
# configured `write_timeout`.
|
|
139
|
+
#
|
|
140
|
+
# @param socket [TCPSocket] the socket to write to
|
|
141
|
+
# @param strings [Array<String>] the buffers to write in order
|
|
142
|
+
# @return [void]
|
|
143
|
+
# @raise [Http::WriteError] if the socket is not writable within the timeout or raises IOError
|
|
144
|
+
#
|
|
145
|
+
# @rbs (TCPSocket socket, Array[String] strings) -> void
|
|
146
|
+
def socket_writev: (TCPSocket socket, Array[String] strings) -> void
|
|
113
147
|
|
|
114
148
|
# Signals eager keep-alive loops to stop processing further requests on
|
|
115
149
|
# their connections. In-flight requests complete normally.
|
|
@@ -160,6 +194,28 @@ module Raptor
|
|
|
160
194
|
|
|
161
195
|
private
|
|
162
196
|
|
|
197
|
+
# Returns true if the request expects a 100 Continue response per
|
|
198
|
+
# RFC 7231 section 5.1.1.
|
|
199
|
+
#
|
|
200
|
+
# @param env [Hash] the parsed Rack environment (possibly incomplete)
|
|
201
|
+
# @return [Boolean]
|
|
202
|
+
#
|
|
203
|
+
# @rbs (Hash[String, untyped] env) -> bool
|
|
204
|
+
def expects_100_continue?: (Hash[String, untyped] env) -> bool
|
|
205
|
+
|
|
206
|
+
# Sends an HTTP 100 Continue response when an HTTP/1.1 client requested
|
|
207
|
+
# `Expect: 100-continue` and the request body has not yet been received.
|
|
208
|
+
#
|
|
209
|
+
# Returns the state hash with `:continued` set when the response has been
|
|
210
|
+
# written. A write failure is silently ignored.
|
|
211
|
+
#
|
|
212
|
+
# @param state [Hash] the partially-parsed connection state
|
|
213
|
+
# @param reactor [Reactor] the reactor holding the connection's socket
|
|
214
|
+
# @return [Hash] the state, with `:continued` set if 100 was written
|
|
215
|
+
#
|
|
216
|
+
# @rbs (Hash[Symbol, untyped] state, Reactor reactor) -> Hash[Symbol, untyped]
|
|
217
|
+
def send_continue_if_expected: (Hash[Symbol, untyped] state, Reactor reactor) -> Hash[Symbol, untyped]
|
|
218
|
+
|
|
163
219
|
# Processes a client connection by handling the current request and,
|
|
164
220
|
# if keep-alive, eagerly reading subsequent requests inline.
|
|
165
221
|
#
|
|
@@ -278,6 +334,16 @@ module Raptor
|
|
|
278
334
|
# @rbs (String? body) -> IO
|
|
279
335
|
def build_rack_input: (String? body) -> IO
|
|
280
336
|
|
|
337
|
+
# Returns true when an upstream proxy signals that it terminated TLS for
|
|
338
|
+
# this request via `X-Forwarded-Proto`, `X-Forwarded-Scheme`, or
|
|
339
|
+
# `X-Forwarded-Ssl`. Only the first comma-separated value is consulted.
|
|
340
|
+
#
|
|
341
|
+
# @param env [Hash] the Rack environment
|
|
342
|
+
# @return [Boolean]
|
|
343
|
+
#
|
|
344
|
+
# @rbs (Hash[String, untyped] env) -> bool
|
|
345
|
+
def forwarded_https?: (Hash[String, untyped] env) -> bool
|
|
346
|
+
|
|
281
347
|
# Determines whether the connection should be kept alive after the response.
|
|
282
348
|
#
|
|
283
349
|
# Returns false if the request limit has been reached. For HTTP/1.1, keep-alive
|
|
@@ -353,7 +419,8 @@ module Raptor
|
|
|
353
419
|
# @rbs (Hash[String, String | Array[String]] headers, Integer status) -> void
|
|
354
420
|
def validate_headers: (Hash[String, String | Array[String]] headers, Integer status) -> void
|
|
355
421
|
|
|
356
|
-
#
|
|
422
|
+
# Returns the HTTP status line for `status`. Callers must not retain the
|
|
423
|
+
# returned string across further calls on the same thread.
|
|
357
424
|
#
|
|
358
425
|
# @param http_version [String] "HTTP/1.1" or "HTTP/1.0"
|
|
359
426
|
# @param status [Integer] HTTP status code
|
|
@@ -446,10 +513,7 @@ module Raptor
|
|
|
446
513
|
# @rbs (TCPSocket socket, String response, Array[String] body_array, bool use_chunked) -> void
|
|
447
514
|
def write_array_body: (TCPSocket socket, String response, Array[String] body_array, bool use_chunked) -> void
|
|
448
515
|
|
|
449
|
-
# Writes a single-element array body
|
|
450
|
-
#
|
|
451
|
-
# Small bodies are concatenated with the headers into one write to reduce
|
|
452
|
-
# system call overhead.
|
|
516
|
+
# Writes a single-element array body to the socket.
|
|
453
517
|
#
|
|
454
518
|
# @param socket [TCPSocket] the client socket
|
|
455
519
|
# @param response [String] headers already serialized, to be written before the body
|
|
@@ -526,6 +590,29 @@ module Raptor
|
|
|
526
590
|
# @rbs (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
|
|
527
591
|
def call_response_finished: (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
|
|
528
592
|
|
|
593
|
+
# Instance-level wrapper around {Http.write_access_log} that routes to
|
|
594
|
+
# the configured `@access_log_io`.
|
|
595
|
+
#
|
|
596
|
+
# @param env [Hash] the Rack environment
|
|
597
|
+
# @param status [Integer] the response status code
|
|
598
|
+
# @param size [String] the response body size in bytes, or `-` if unknown
|
|
599
|
+
# @param remote_addr [String] the client IP address
|
|
600
|
+
# @return [void]
|
|
601
|
+
#
|
|
602
|
+
# @rbs (Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
|
|
603
|
+
def write_access_log: (Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
|
|
604
|
+
|
|
605
|
+
# Returns the response body size as a String for the access log, taken
|
|
606
|
+
# from the `content-length` header when set, computed from the body
|
|
607
|
+
# otherwise, or `-` when the size cannot be determined upfront.
|
|
608
|
+
#
|
|
609
|
+
# @param headers [Hash] the response headers
|
|
610
|
+
# @param body [Object] the response body
|
|
611
|
+
# @return [String]
|
|
612
|
+
#
|
|
613
|
+
# @rbs (Hash[String, String | Array[String]] headers, untyped body) -> String
|
|
614
|
+
def response_size: (Hash[String, String | Array[String]] headers, untyped body) -> String
|
|
615
|
+
|
|
529
616
|
# Enables TCP_CORK on the socket to batch outgoing packets into fewer segments.
|
|
530
617
|
#
|
|
531
618
|
# 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
|
-
# @
|
|
23
|
-
|
|
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
|
|
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
|
|
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]
|
|
@@ -140,28 +145,44 @@ module Raptor
|
|
|
140
145
|
|
|
141
146
|
HOP_BY_HOP_HEADERS: untyped
|
|
142
147
|
|
|
148
|
+
@initial_settings_frame: String
|
|
149
|
+
|
|
143
150
|
@on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
|
|
144
151
|
|
|
152
|
+
@access_log_io: IO?
|
|
153
|
+
|
|
154
|
+
@write_timeout: Integer
|
|
155
|
+
|
|
145
156
|
@server_port: Integer
|
|
146
157
|
|
|
147
158
|
@app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
|
|
148
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
|
+
|
|
149
165
|
# Creates a new Http2 handler.
|
|
150
166
|
#
|
|
151
167
|
# @param app [#call] the Rack application to dispatch requests to
|
|
152
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
|
|
153
174
|
# @param on_error [#call, nil] callback invoked with (env, exception) when the Rack app raises
|
|
154
175
|
# @return [void]
|
|
155
176
|
#
|
|
156
|
-
# @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port, ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
|
|
157
|
-
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
|
|
158
179
|
|
|
159
|
-
#
|
|
180
|
+
# Creates a per-connection {Writer} configured with the handler's write timeout.
|
|
160
181
|
#
|
|
161
|
-
# @return [
|
|
182
|
+
# @return [Writer] a new per-connection frame writer
|
|
162
183
|
#
|
|
163
|
-
# @rbs () ->
|
|
164
|
-
def
|
|
184
|
+
# @rbs () -> Writer
|
|
185
|
+
def create_writer: () -> Writer
|
|
165
186
|
|
|
166
187
|
# Processes HTTP/2 frames from the connection buffer.
|
|
167
188
|
#
|
|
@@ -271,10 +292,10 @@ module Raptor
|
|
|
271
292
|
# @param status [Integer] HTTP status code
|
|
272
293
|
# @param headers [Hash] response headers from the Rack application
|
|
273
294
|
# @param body [Object] response body responding to each
|
|
274
|
-
# @return [
|
|
295
|
+
# @return [String] the response body size in bytes
|
|
275
296
|
#
|
|
276
|
-
# @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, FlowControl flow_control, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) ->
|
|
277
|
-
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) ->
|
|
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
|
|
278
299
|
|
|
279
300
|
# Writes a 500 error response as HTTP/2 frames.
|
|
280
301
|
#
|
|
@@ -286,6 +307,18 @@ module Raptor
|
|
|
286
307
|
# @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id) -> void
|
|
287
308
|
def write_http2_error_response: (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id) -> void
|
|
288
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
|
+
|
|
289
322
|
# Builds a Rack environment hash from HTTP/2 headers and body.
|
|
290
323
|
#
|
|
291
324
|
# Translates HTTP/2 pseudo-headers into Rack-compatible environment keys
|