raptor 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,10 +51,16 @@ module Raptor
51
51
  def members: () -> [ :tcp_server, :ssl_context ]
52
52
  end
53
53
 
54
- @bind_uris: Array[String]
54
+ @uri_listeners: Hash[String, Array[TCPServer | UNIXServer | SslListener]]
55
55
 
56
56
  @listeners: Array[TCPServer | UNIXServer | SslListener]
57
57
 
58
+ @inherited_fds: Hash[String, Array[Integer]]
59
+
60
+ @socket_backlog: Integer
61
+
62
+ @bind_uris: Array[String]
63
+
58
64
  # Array of listening sockets.
59
65
  #
60
66
  # @return [Array<TCPServer, UNIXServer, SslListener>] the server sockets
@@ -64,17 +70,21 @@ module Raptor
64
70
  #
65
71
  # Parses the provided bind URIs and creates listening sockets for each one.
66
72
  # Supports tcp://, unix://, and ssl:// schemes. Localhost is expanded to
67
- # all available loopback addresses (both IPv4 and IPv6).
73
+ # all available loopback addresses (both IPv4 and IPv6). When `inherited_fds`
74
+ # supplies file descriptors for a URI, the listener is reconstructed from
75
+ # those FDs instead of binding fresh.
68
76
  #
69
77
  # @param bind_uris [Array<String>] array of URI strings to bind to
78
+ # @param socket_backlog [Integer] kernel listen() queue depth for TCP/SSL listeners
79
+ # @param inherited_fds [Hash{String => Array<Integer>}] inherited listener FDs keyed by bind URI
70
80
  # @return [void]
71
81
  # @raise [UnknownBindSchemeError] if a URI has an unsupported scheme
72
82
  #
73
83
  # @example
74
84
  # binder = Binder.new(["tcp://0.0.0.0:3000", "unix:///tmp/raptor.sock"])
75
85
  #
76
- # @rbs (Array[String] bind_uris) -> void
77
- def initialize: (Array[String] bind_uris) -> void
86
+ # @rbs (Array[String] bind_uris, ?socket_backlog: Integer, ?inherited_fds: Hash[String, Array[Integer]]) -> void
87
+ def initialize: (Array[String] bind_uris, ?socket_backlog: Integer, ?inherited_fds: Hash[String, Array[Integer]]) -> void
78
88
 
79
89
  # Returns the bound addresses as strings.
80
90
  #
@@ -106,9 +116,27 @@ module Raptor
106
116
  # @rbs () -> void
107
117
  def close: () -> void
108
118
 
119
+ # Returns the file descriptors of every listener, grouped by the bind URI
120
+ # they were created from. The result is the payload to hand to a successor
121
+ # process via the `inherited_fds:` constructor argument.
122
+ #
123
+ # @return [Hash{String => Array<Integer>}]
124
+ #
125
+ # @rbs () -> Hash[String, Array[Integer]]
126
+ def inheritable_fds: () -> Hash[String, Array[Integer]]
127
+
128
+ # Clears the close-on-exec flag on every listener so the file descriptors
129
+ # survive `Kernel.exec`.
130
+ #
131
+ # @return [void]
132
+ #
133
+ # @rbs () -> void
134
+ def clear_close_on_exec: () -> void
135
+
109
136
  private
110
137
 
111
- # Parses bind URIs and creates listening sockets.
138
+ # Parses bind URIs and creates listening sockets, reusing inherited file
139
+ # descriptors for URIs supplied in `@inherited_fds`.
112
140
  #
113
141
  # @return [void]
114
142
  # @raise [UnknownBindSchemeError] if a URI scheme is not supported
@@ -116,6 +144,26 @@ module Raptor
116
144
  # @rbs () -> void
117
145
  def parse: () -> void
118
146
 
147
+ # Creates fresh listeners for the given bind URI.
148
+ #
149
+ # @param bind_uri [String] the URI to bind
150
+ # @return [Array<TCPServer, UNIXServer, SslListener>]
151
+ # @raise [UnknownBindSchemeError] if the URI scheme is not supported
152
+ #
153
+ # @rbs (String bind_uri) -> Array[TCPServer | UNIXServer | SslListener]
154
+ def create_listeners: (String bind_uri) -> Array[TCPServer | UNIXServer | SslListener]
155
+
156
+ # Reconstructs listeners for the given bind URI from inherited file
157
+ # descriptors.
158
+ #
159
+ # @param bind_uri [String] the URI the FDs were bound to
160
+ # @param filenos [Array<Integer>] file descriptors to wrap
161
+ # @return [Array<TCPServer, UNIXServer, SslListener>]
162
+ # @raise [UnknownBindSchemeError] if the URI scheme is not supported
163
+ #
164
+ # @rbs (String bind_uri, Array[Integer] filenos) -> Array[TCPServer | UNIXServer | SslListener]
165
+ def restore_listeners: (String bind_uri, Array[Integer] filenos) -> Array[TCPServer | UNIXServer | SslListener]
166
+
119
167
  # Creates TCP server sockets for the given host and port.
120
168
  #
121
169
  # @param host [String, nil] hostname or IP address to bind to
@@ -138,6 +186,16 @@ module Raptor
138
186
  # @rbs (String path) -> Array[UNIXServer]
139
187
  def create_unix_listeners: (String path) -> Array[UNIXServer]
140
188
 
189
+ # Registers an `at_exit` hook that removes the Unix socket file on the
190
+ # owning master's clean exit. Each call records the current process so
191
+ # forked workers won't delete a socket their master still owns.
192
+ #
193
+ # @param path [String] filesystem path of the Unix socket
194
+ # @return [void]
195
+ #
196
+ # @rbs (String path) -> void
197
+ def register_unix_socket_cleanup: (String path) -> void
198
+
141
199
  # Creates SSL server sockets for the given host, port, and SSL parameters.
142
200
  #
143
201
  # Wraps each TCP listener with an SSL context to produce SslListener objects.
@@ -152,6 +210,15 @@ module Raptor
152
210
  # @rbs (String? host, Integer? port, Hash[String, String] ssl_params) -> Array[SslListener]
153
211
  def create_ssl_listeners: (String? host, Integer? port, Hash[String, String] ssl_params) -> Array[SslListener]
154
212
 
213
+ # Builds a frozen `OpenSSL::SSL::SSLContext` configured for HTTP/2 and
214
+ # HTTP/1.1 ALPN negotiation.
215
+ #
216
+ # @param ssl_params [Hash<String, String>] SSL options ("cert" and "key" paths)
217
+ # @return [OpenSSL::SSL::SSLContext]
218
+ #
219
+ # @rbs (Hash[String, String] ssl_params) -> OpenSSL::SSL::SSLContext
220
+ def build_ssl_context: (Hash[String, String] ssl_params) -> OpenSSL::SSL::SSLContext
221
+
155
222
  # Returns all available loopback IP addresses.
156
223
  #
157
224
  # @return [Array<String>] unique loopback addresses (IPv4 and IPv6)
@@ -18,6 +18,8 @@ module Raptor
18
18
  class CLI
19
19
  DEFAULT_WORKER_COUNT: untyped
20
20
 
21
+ NESTED_OPTION_KEYS: untyped
22
+
21
23
  DEFAULT_OPTIONS: untyped
22
24
 
23
25
  DEFAULT_CONFIG_PATHS: untyped
@@ -103,9 +105,6 @@ module Raptor
103
105
 
104
106
  # Loads a config file and merges it into `@options` over the defaults.
105
107
  #
106
- # Top-level keys replace defaults; the nested `:client` hash is merged
107
- # key-by-key so a config file does not need to restate every client option.
108
- #
109
108
  # @param path [String, nil] path to the config file, or nil to no-op
110
109
  # @return [void]
111
110
  #
@@ -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
- # client: { first_data_timeout: 30, chunk_data_timeout: 10 }
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,15 @@ module Raptor
38
40
  # @rbs (Hash[Symbol, untyped] options) -> void
39
41
  def self.run: (Hash[Symbol, untyped] options) -> void
40
42
 
41
- @thread_count: Integer
43
+ @http1_options: Hash[Symbol, untyped]
42
44
 
43
- @client_options: Hash[Symbol, Integer]
45
+ @http2_options: Hash[Symbol, untyped]
46
+
47
+ @worker_boot_timeout: Integer
44
48
 
45
49
  @worker_timeout: Integer
46
50
 
47
- @worker_boot_timeout: Integer
51
+ @worker_drain_timeout: Integer
48
52
 
49
53
  @worker_shutdown_timeout: Integer
50
54
 
@@ -52,6 +56,18 @@ module Raptor
52
56
 
53
57
  @pid_file: String?
54
58
 
59
+ @stdout_file: String?
60
+
61
+ @stderr_file: String?
62
+
63
+ @access_log_file: String?
64
+
65
+ @access_log_io: IO?
66
+
67
+ @launch_command: String?
68
+
69
+ @launch_argv: Array[String]?
70
+
55
71
  @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
56
72
 
57
73
  @binder: Binder
@@ -74,10 +90,20 @@ module Raptor
74
90
 
75
91
  @phased_restarting: bool
76
92
 
93
+ @hot_restart_requested: bool
94
+
95
+ @connection_options: Hash[Symbol, untyped]
96
+
97
+ @environment: String
98
+
99
+ @thread_count: Integer
100
+
77
101
  @ractor_count: Integer
78
102
 
79
103
  @worker_count: Integer
80
104
 
105
+ @drain_accept_queue: bool
106
+
81
107
  # Creates a new Cluster with the specified configuration.
82
108
  #
83
109
  # Initializes the cluster with worker, ractor, and thread counts,
@@ -86,18 +112,30 @@ module Raptor
86
112
  #
87
113
  # @param options [Hash] cluster configuration options
88
114
  # @option options [Array<String>] :binds array of bind URIs
115
+ # @option options [Integer] :socket_backlog kernel listen() queue depth for TCP/SSL listeners
116
+ # @option options [Boolean] :drain_accept_queue whether to drain the kernel accept queue on shutdown
89
117
  # @option options [Integer] :workers number of worker processes
90
118
  # @option options [Integer] :ractors number of ractors per worker process
91
119
  # @option options [Integer] :threads number of threads per worker process
92
120
  # @option options [#call] :app pre-built Rack application
93
121
  # @option options [String] :rackup path to Rack configuration file
94
- # @option options [Hash] :client client configuration
95
- # @option options [Integer] :worker_timeout seconds to wait for a booted worker to check in before killing it
122
+ # @option options [String, nil] :chdir directory to change to before loading the Rack application, or nil to leave the working directory unchanged
123
+ # @option options [String, nil] :environment Raptor's application environment label; falls back to `$RAILS_ENV`, then `$RACK_ENV`, then `"development"`
124
+ # @option options [Hash] :connection per-connection settings shared across protocols
125
+ # @option options [Hash] :http1 HTTP/1.1-specific settings
126
+ # @option options [Hash] :http2 HTTP/2-specific settings
96
127
  # @option options [Integer] :worker_boot_timeout seconds to wait for a worker to finish booting before killing it
128
+ # @option options [Integer] :worker_timeout seconds to wait for a booted worker to check in before killing it
129
+ # @option options [Integer] :worker_drain_timeout seconds a worker waits for in-flight requests during shutdown before force-killing app threads
97
130
  # @option options [Integer] :worker_shutdown_timeout seconds to wait for graceful worker exit before force-killing
98
131
  # @option options [String, nil] :stats_file path to write per-worker stats JSON, or nil to disable
99
132
  # @option options [String, nil] :pid_file path to write the master PID to, or nil to disable
100
- # @option options [#call] :on_error callback invoked with (env, exception) when the Rack app raises
133
+ # @option options [String, nil] :stdout_file path to redirect stdout to, reopened on SIGHUP, or nil to disable
134
+ # @option options [String, nil] :stderr_file path to redirect stderr to, reopened on SIGHUP, or nil to disable
135
+ # @option options [String, nil] :access_log_file path to write Common Log Format access logs to, reopened on SIGHUP, or nil to disable
136
+ # @option options [String, nil] :launch_command path of the program to re-exec on hot restart, or nil to disable
137
+ # @option options [Array<String>, nil] :launch_argv command-line arguments for the hot-restart exec, or nil to disable
138
+ # @option options [#call, nil] :on_error callback invoked with (env, exception) when the Rack app raises
101
139
  # @return [void]
102
140
  #
103
141
  # @rbs (Hash[Symbol, untyped] options) -> void
@@ -107,8 +145,8 @@ module Raptor
107
145
  #
108
146
  # Forks the configured number of worker processes and monitors them,
109
147
  # restarting any that exit unexpectedly or stop checking in. Handles
110
- # graceful shutdown via INT or TERM signals, stats logging via USR1,
111
- # and phased restart via USR2.
148
+ # graceful shutdown via INT or TERM signals, phased restart via USR1,
149
+ # and hot restart via USR2.
112
150
  #
113
151
  # Each worker process includes:
114
152
  # - 1 server thread (continuously accepts connections with backpressure control)
@@ -133,6 +171,18 @@ module Raptor
133
171
 
134
172
  private
135
173
 
174
+ # Returns the inherited-FDs hash for a systemd socket-activation handoff,
175
+ # pairing each bind URI with the FD systemd passed at the same index.
176
+ # The activation is skipped (with a warning) when the FD count doesn't
177
+ # match the number of bind URIs.
178
+ #
179
+ # @param bind_uris [Array<String>] the configured bind URIs
180
+ # @param filenos [Array<Integer>] file descriptors passed by systemd
181
+ # @return [Hash{String => Array<Integer>}]
182
+ #
183
+ # @rbs (Array[String] bind_uris, Array[Integer] filenos) -> Hash[String, Array[Integer]]
184
+ def pair_systemd_fds: (Array[String] bind_uris, Array[Integer] filenos) -> Hash[String, Array[Integer]]
185
+
136
186
  # Forks a new worker process and registers it at the given index.
137
187
  # The worker inherits the cluster's current phase.
138
188
  #
@@ -170,13 +220,22 @@ module Raptor
170
220
  def timeout_hung_workers: () -> void
171
221
 
172
222
  # Replaces each worker process one at a time, waiting for the new
173
- # worker to boot before moving on to the next. Triggered by SIGUSR2.
223
+ # worker to boot before moving on to the next.
174
224
  #
175
225
  # @return [void]
176
226
  #
177
227
  # @rbs () -> void
178
228
  def perform_phased_restart: () -> void
179
229
 
230
+ # Re-execs the master process with a fresh boot of the same Raptor
231
+ # invocation, handing the new master its listening sockets so accepted
232
+ # connections continue to be served across the swap.
233
+ #
234
+ # @return [void]
235
+ #
236
+ # @rbs () -> void
237
+ def perform_hot_restart: () -> void
238
+
180
239
  # Runs the full server stack inside a worker process.
181
240
  #
182
241
  # Sets up and coordinates the reactor, server, ractor pool, thread pool,
@@ -190,6 +249,16 @@ module Raptor
190
249
  # @rbs (Integer index, Integer phase) -> void
191
250
  def run_worker: (Integer index, Integer phase) -> void
192
251
 
252
+ # Shuts down the worker's application thread pool, force-killing the
253
+ # underlying threads if in-flight requests have not finished within
254
+ # `worker_drain_timeout` seconds.
255
+ #
256
+ # @param thread_pool [AtomicThreadPool] the worker's thread pool
257
+ # @return [void]
258
+ #
259
+ # @rbs (AtomicThreadPool thread_pool) -> void
260
+ def drain_thread_pool: (AtomicThreadPool thread_pool) -> void
261
+
193
262
  # Returns a human-readable description of how a process exited.
194
263
  #
195
264
  # @param status [Process::Status] the exit status of the process
@@ -213,14 +282,21 @@ module Raptor
213
282
  # @rbs () -> void
214
283
  def log_initialization: () -> void
215
284
 
216
- # Logs current stats for all workers to stdout.
285
+ # Redirects `$stdout`, `$stderr`, and the access log to their configured
286
+ # paths. No-op for any stream whose target path is nil.
217
287
  #
218
- # Triggered by SIGUSR1 in the master process.
288
+ # @return [void]
289
+ #
290
+ # @rbs () -> void
291
+ def reopen_logs: () -> void
292
+
293
+ # Reopens the master's log files and forwards SIGHUP to each worker so
294
+ # they reopen their own inherited file descriptors.
219
295
  #
220
296
  # @return [void]
221
297
  #
222
298
  # @rbs () -> void
223
- def log_stats: () -> void
299
+ def reopen_logs_and_signal_workers: () -> void
224
300
 
225
301
  # Writes the stats file on a 1-second interval until shutdown.
226
302
  #
@@ -0,0 +1,52 @@
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 a Common Log Format entry to `io`. Write failures are silently
40
+ # ignored.
41
+ #
42
+ # @param io [IO] the destination IO
43
+ # @param env [Hash] the Rack environment
44
+ # @param status [Integer] the response status code
45
+ # @param size [String] the response body size in bytes, or `-` if unknown
46
+ # @param remote_addr [String] the client IP address
47
+ # @return [void]
48
+ #
49
+ # @rbs (IO io, Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
50
+ def self.write_access_log: (IO io, Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
51
+ end
52
+ end
@@ -1,18 +1,18 @@
1
- # Generated from lib/raptor/request.rb with RBS::Inline
1
+ # Generated from lib/raptor/http1.rb with RBS::Inline
2
2
 
3
3
  module Raptor
4
4
  # Parses HTTP/1.x requests and dispatches them to the Rack
5
5
  # application. Coordinates with the Ractor pool for parsing and
6
6
  # with the reactor for requests that need more data before they
7
7
  # can be handled.
8
- class Request
8
+ class Http1
9
9
  BODY_BUFFER_THRESHOLD: untyped
10
10
 
11
11
  FILE_CHUNK_SIZE: untyped
12
12
 
13
- READ_BUFFER_SIZE: untyped
13
+ MAX_CHUNK_OVERHEAD: untyped
14
14
 
15
- WRITE_TIMEOUT: ::Integer
15
+ READ_BUFFER_SIZE: untyped
16
16
 
17
17
  KEEPALIVE_READ_TIMEOUT: ::Float
18
18
 
@@ -28,20 +28,26 @@ module Raptor
28
28
 
29
29
  STATUS_WITH_NO_ENTITY_BODY: untyped
30
30
 
31
- BAD_REQUEST_RESPONSE: ::String
31
+ CONTINUE_RESPONSE: ::String
32
32
 
33
- INTERNAL_SERVER_ERROR_RESPONSE: ::String
33
+ BAD_REQUEST_RESPONSE: ::String
34
34
 
35
35
  CONTENT_TOO_LARGE_RESPONSE: ::String
36
36
 
37
+ INTERNAL_SERVER_ERROR_RESPONSE: ::String
38
+
37
39
  CONNECTION_CLOSE: ::String
38
40
 
39
41
  CONNECTION_KEEPALIVE: ::String
40
42
 
43
+ EXPECT_100_CONTINUE: ::String
44
+
41
45
  TRANSFER_ENCODING_CHUNKED: ::String
42
46
 
43
47
  HTTP_CONNECTION: ::String
44
48
 
49
+ HTTP_EXPECT: ::String
50
+
45
51
  HTTP_TRANSFER_ENCODING: ::String
46
52
 
47
53
  RACK_HEADER_PREFIX: ::String
@@ -54,19 +60,24 @@ module Raptor
54
60
 
55
61
  ILLEGAL_HEADER_VALUE_REGEX: ::Regexp
56
62
 
57
- class Error < StandardError
58
- end
59
-
60
- class WriteError < Error
61
- # @rbs () -> String
62
- def message: () -> String
63
- end
63
+ # Returns true when the message framing shows a request-smuggling vector
64
+ # per RFC 9112 section 6.3: a `Transfer-Encoding` where `chunked` is
65
+ # missing, not the final encoding, or duplicated; a `Transfer-Encoding`
66
+ # paired with a `Content-Length`; or a `Content-Length` containing any
67
+ # non-digit character.
68
+ #
69
+ # @param env [Hash] the Rack environment after header parsing
70
+ # @return [Boolean]
71
+ #
72
+ # @rbs (Hash[String, untyped] env) -> bool
73
+ def self.request_smuggling?: (Hash[String, untyped] env) -> bool
64
74
 
65
75
  # Decodes a chunked transfer-encoded body buffer.
66
76
  #
67
77
  # Returns the decoded bytes and a state symbol: `:complete` when the
68
78
  # terminating zero-length chunk was found, `:too_large` when the decoded
69
- # size would exceed `max_size`, or `:incomplete` otherwise.
79
+ # size would exceed `max_size`, `:malformed` when chunk framing overhead
80
+ # exceeds `MAX_CHUNK_OVERHEAD`, or `:incomplete` otherwise.
70
81
  #
71
82
  # @param buffer [String] the raw body buffer to decode
72
83
  # @param max_size [Integer, nil] maximum decoded body size, or nil for unlimited
@@ -75,41 +86,51 @@ module Raptor
75
86
  # @rbs (String buffer, ?Integer? max_size) -> [String, Symbol]
76
87
  def self.decode_chunked: (String buffer, ?Integer? max_size) -> [ String, Symbol ]
77
88
 
78
- # Writes `string` in full, retrying on partial writes. Bounded by
79
- # `WRITE_TIMEOUT` so a slow client can't pin the writing thread.
80
- #
81
- # @param socket [TCPSocket] the socket to write to
82
- # @param string [String] the data to write
83
- # @return [void]
84
- # @raise [WriteError] if the socket is not writable within the timeout or raises IOError
85
- #
86
- # @rbs (TCPSocket socket, String string) -> void
87
- def self.socket_write: (TCPSocket socket, String string) -> void
88
-
89
89
  @running: AtomicBoolean
90
90
 
91
91
  @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
92
92
 
93
+ @access_log_io: IO?
94
+
95
+ @max_keepalive_requests: Integer
96
+
93
97
  @body_spool_threshold: Integer?
94
98
 
95
99
  @max_body_size: Integer?
96
100
 
101
+ @write_timeout: Integer
102
+
97
103
  @server_port: Integer
98
104
 
99
105
  @app: ^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ]
100
106
 
101
- # Creates a new Request handler.
107
+ # Creates a new Http1 handler.
102
108
  #
103
109
  # @param app [#call] the Rack application to dispatch complete requests to
104
110
  # @param server_port [Integer] port number used to populate SERVER_PORT in the Rack env
105
- # @param client_options [Hash] client limits configuration
106
- # @option client_options [Integer, nil] :max_body_size maximum request body size in bytes
107
- # @option client_options [Integer, nil] :body_spool_threshold spool bodies larger than this to a tempfile
111
+ # @param connection_options [Hash] per-connection settings shared across protocols
112
+ # @option connection_options [Integer] :write_timeout per-write socket timeout in seconds
113
+ # @option connection_options [Integer, nil] :max_body_size maximum request body size in bytes
114
+ # @option connection_options [Integer, nil] :body_spool_threshold spool bodies larger than this to a tempfile
115
+ # @param http1_options [Hash] HTTP/1.1-specific settings
116
+ # @option http1_options [Integer] :max_keepalive_requests maximum requests per HTTP/1.1 keep-alive connection
117
+ # @param access_log_io [IO, nil] IO to write Common Log Format access entries to, or nil to disable
108
118
  # @param on_error [#call, nil] callback invoked with (env, exception) when the Rack app raises
109
119
  # @return [void]
110
120
  #
111
- # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port, ?client_options: Hash[Symbol, untyped], ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
112
- def initialize: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Integer server_port, ?client_options: Hash[Symbol, untyped], ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
121
+ # @rbs (^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped] app, Integer server_port, ?connection_options: Hash[Symbol, untyped], ?http1_options: Hash[Symbol, untyped], ?access_log_io: IO?, ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
122
+ def initialize: (^(Hash[String, untyped]) -> [ Integer, Hash[String, String | Array[String]], untyped ] app, Integer server_port, ?connection_options: Hash[Symbol, untyped], ?http1_options: Hash[Symbol, untyped], ?access_log_io: IO?, ?on_error: ^(Hash[String, untyped]?, Exception) -> void | nil) -> void
123
+
124
+ # Instance-level wrapper around {Http.socket_write} that applies the
125
+ # configured `write_timeout`.
126
+ #
127
+ # @param socket [TCPSocket] the socket to write to
128
+ # @param string [String] the data to write
129
+ # @return [void]
130
+ # @raise [Http::WriteError] if the socket is not writable within the timeout or raises IOError
131
+ #
132
+ # @rbs (TCPSocket socket, String string) -> void
133
+ def socket_write: (TCPSocket socket, String string) -> void
113
134
 
114
135
  # Signals eager keep-alive loops to stop processing further requests on
115
136
  # their connections. In-flight requests complete normally.
@@ -160,6 +181,28 @@ module Raptor
160
181
 
161
182
  private
162
183
 
184
+ # Returns true if the request expects a 100 Continue response per
185
+ # RFC 7231 section 5.1.1.
186
+ #
187
+ # @param env [Hash] the parsed Rack environment (possibly incomplete)
188
+ # @return [Boolean]
189
+ #
190
+ # @rbs (Hash[String, untyped] env) -> bool
191
+ def expects_100_continue?: (Hash[String, untyped] env) -> bool
192
+
193
+ # Sends an HTTP 100 Continue response when an HTTP/1.1 client requested
194
+ # `Expect: 100-continue` and the request body has not yet been received.
195
+ #
196
+ # Returns the state hash with `:continued` set when the response has been
197
+ # written. A write failure is silently ignored.
198
+ #
199
+ # @param state [Hash] the partially-parsed connection state
200
+ # @param reactor [Reactor] the reactor holding the connection's socket
201
+ # @return [Hash] the state, with `:continued` set if 100 was written
202
+ #
203
+ # @rbs (Hash[Symbol, untyped] state, Reactor reactor) -> Hash[Symbol, untyped]
204
+ def send_continue_if_expected: (Hash[Symbol, untyped] state, Reactor reactor) -> Hash[Symbol, untyped]
205
+
163
206
  # Processes a client connection by handling the current request and,
164
207
  # if keep-alive, eagerly reading subsequent requests inline.
165
208
  #
@@ -278,6 +321,16 @@ module Raptor
278
321
  # @rbs (String? body) -> IO
279
322
  def build_rack_input: (String? body) -> IO
280
323
 
324
+ # Returns true when an upstream proxy signals that it terminated TLS for
325
+ # this request via `X-Forwarded-Proto`, `X-Forwarded-Scheme`, or
326
+ # `X-Forwarded-Ssl`. Only the first comma-separated value is consulted.
327
+ #
328
+ # @param env [Hash] the Rack environment
329
+ # @return [Boolean]
330
+ #
331
+ # @rbs (Hash[String, untyped] env) -> bool
332
+ def forwarded_https?: (Hash[String, untyped] env) -> bool
333
+
281
334
  # Determines whether the connection should be kept alive after the response.
282
335
  #
283
336
  # Returns false if the request limit has been reached. For HTTP/1.1, keep-alive
@@ -526,6 +579,29 @@ module Raptor
526
579
  # @rbs (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
527
580
  def call_response_finished: (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
528
581
 
582
+ # Instance-level wrapper around {Http.write_access_log} that routes to
583
+ # the configured `@access_log_io`.
584
+ #
585
+ # @param env [Hash] the Rack environment
586
+ # @param status [Integer] the response status code
587
+ # @param size [String] the response body size in bytes, or `-` if unknown
588
+ # @param remote_addr [String] the client IP address
589
+ # @return [void]
590
+ #
591
+ # @rbs (Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
592
+ def write_access_log: (Hash[String, untyped] env, Integer status, String size, String remote_addr) -> void
593
+
594
+ # Returns the response body size as a String for the access log, taken
595
+ # from the `content-length` header when set, computed from the body
596
+ # otherwise, or `-` when the size cannot be determined upfront.
597
+ #
598
+ # @param headers [Hash] the response headers
599
+ # @param body [Object] the response body
600
+ # @return [String]
601
+ #
602
+ # @rbs (Hash[String, String | Array[String]] headers, untyped body) -> String
603
+ def response_size: (Hash[String, String | Array[String]] headers, untyped body) -> String
604
+
529
605
  # Enables TCP_CORK on the socket to batch outgoing packets into fewer segments.
530
606
  #
531
607
  # Only applies to TCP sockets. No-op on non-TCP sockets.