raptor 0.3.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 +13 -0
- data/README.md +13 -13
- data/ext/raptor_http2/raptor_http2.c +1 -0
- data/lib/rackup/handler/raptor.rb +3 -2
- data/lib/raptor/cli.rb +20 -4
- data/lib/raptor/cluster.rb +16 -10
- data/lib/raptor/http2.rb +321 -40
- data/lib/raptor/reactor.rb +67 -27
- data/lib/raptor/request.rb +100 -49
- data/lib/raptor/server.rb +112 -36
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/cli.rbs +14 -0
- data/sig/generated/raptor/cluster.rbs +2 -2
- data/sig/generated/raptor/http2.rbs +125 -6
- data/sig/generated/raptor/reactor.rbs +22 -0
- data/sig/generated/raptor/request.rbs +34 -13
- data/sig/generated/raptor/server.rbs +51 -12
- metadata +1 -1
|
@@ -33,6 +33,84 @@ module Raptor
|
|
|
33
33
|
def write_frames: (OpenSSL::SSL::SSLSocket socket, Array[String] frames) -> void
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# Per-connection outbound flow-control accounting.
|
|
37
|
+
#
|
|
38
|
+
# Tracks the peer's connection-level and per-stream receive windows so
|
|
39
|
+
# outbound DATA frames respect RFC 7540 §5.2. Threads dispatching stream
|
|
40
|
+
# responses call `acquire` to reserve send capacity; threads applying
|
|
41
|
+
# inbound WINDOW_UPDATE or SETTINGS frames call the mutating methods to
|
|
42
|
+
# replenish it. State is held in a single `Atom` so updates use CAS.
|
|
43
|
+
class FlowControl
|
|
44
|
+
ACQUIRE_POLL_INTERVAL: ::Float
|
|
45
|
+
|
|
46
|
+
@connection_window: Atom
|
|
47
|
+
|
|
48
|
+
@stream_windows: Atom
|
|
49
|
+
|
|
50
|
+
@initial_stream_window: Atom
|
|
51
|
+
|
|
52
|
+
# Creates a new FlowControl with the spec-default windows.
|
|
53
|
+
#
|
|
54
|
+
# @rbs () -> void
|
|
55
|
+
def initialize: () -> void
|
|
56
|
+
|
|
57
|
+
# Reserves outbound capacity on the given stream, polling until at
|
|
58
|
+
# least one byte is available on both the connection and stream
|
|
59
|
+
# windows. The returned size is capped at `MAX_FRAME_SIZE`.
|
|
60
|
+
#
|
|
61
|
+
# When `end_stream` is true, `max_bytes` fits within the peer's
|
|
62
|
+
# initial stream window, and no per-stream override has been
|
|
63
|
+
# recorded, only the connection window is consulted. The stream
|
|
64
|
+
# closes on this frame, so its remaining send window will not be
|
|
65
|
+
# consulted again and need not be tracked.
|
|
66
|
+
#
|
|
67
|
+
# @param stream_id [Integer] the HTTP/2 stream identifier
|
|
68
|
+
# @param max_bytes [Integer] the largest size the caller would like to send
|
|
69
|
+
# @param end_stream [Boolean] true when this is the final frame on the stream
|
|
70
|
+
# @return [Integer] the number of bytes the caller may now send
|
|
71
|
+
#
|
|
72
|
+
# @rbs (Integer stream_id, Integer max_bytes, ?end_stream: bool) -> Integer
|
|
73
|
+
def acquire: (Integer stream_id, Integer max_bytes, ?end_stream: bool) -> Integer
|
|
74
|
+
|
|
75
|
+
# Increments the connection-level send window. Called when the peer
|
|
76
|
+
# sends a WINDOW_UPDATE on stream 0.
|
|
77
|
+
#
|
|
78
|
+
# @param increment [Integer] the byte count to add
|
|
79
|
+
# @return [void]
|
|
80
|
+
#
|
|
81
|
+
# @rbs (Integer increment) -> void
|
|
82
|
+
def add_connection_window: (Integer increment) -> void
|
|
83
|
+
|
|
84
|
+
# Increments the per-stream send window. Called when the peer sends
|
|
85
|
+
# a WINDOW_UPDATE on a specific stream.
|
|
86
|
+
#
|
|
87
|
+
# @param stream_id [Integer] the HTTP/2 stream identifier
|
|
88
|
+
# @param increment [Integer] the byte count to add
|
|
89
|
+
# @return [void]
|
|
90
|
+
#
|
|
91
|
+
# @rbs (Integer stream_id, Integer increment) -> void
|
|
92
|
+
def add_stream_window: (Integer stream_id, Integer increment) -> void
|
|
93
|
+
|
|
94
|
+
# Updates the peer's `SETTINGS_INITIAL_WINDOW_SIZE`. Shifts every
|
|
95
|
+
# existing stream window by the delta as required by RFC 7540 §6.9.2.
|
|
96
|
+
#
|
|
97
|
+
# @param new_size [Integer] the peer's new initial window size
|
|
98
|
+
# @return [void]
|
|
99
|
+
#
|
|
100
|
+
# @rbs (Integer new_size) -> void
|
|
101
|
+
def set_initial_stream_window: (Integer new_size) -> void
|
|
102
|
+
|
|
103
|
+
# Discards any per-stream tracking for the given stream. Called
|
|
104
|
+
# after a stream closes so `@stream_windows` does not grow without
|
|
105
|
+
# bound across the lifetime of a connection.
|
|
106
|
+
#
|
|
107
|
+
# @param stream_id [Integer] the HTTP/2 stream identifier
|
|
108
|
+
# @return [void]
|
|
109
|
+
#
|
|
110
|
+
# @rbs (Integer stream_id) -> void
|
|
111
|
+
def discard_stream: (Integer stream_id) -> void
|
|
112
|
+
end
|
|
113
|
+
|
|
36
114
|
FLAG_END_STREAM: ::Integer
|
|
37
115
|
|
|
38
116
|
FLAG_END_HEADERS: ::Integer
|
|
@@ -41,6 +119,14 @@ module Raptor
|
|
|
41
119
|
|
|
42
120
|
FLAG_PRIORITY: ::Integer
|
|
43
121
|
|
|
122
|
+
ERROR_NO_ERROR: ::Integer
|
|
123
|
+
|
|
124
|
+
ERROR_PROTOCOL_ERROR: ::Integer
|
|
125
|
+
|
|
126
|
+
DEFAULT_WINDOW_SIZE: ::Integer
|
|
127
|
+
|
|
128
|
+
MAX_FRAME_SIZE: ::Integer
|
|
129
|
+
|
|
44
130
|
SERVER_PROTOCOL: ::String
|
|
45
131
|
|
|
46
132
|
RACK_HEADER_PREFIX: ::String
|
|
@@ -82,6 +168,19 @@ module Raptor
|
|
|
82
168
|
# @rbs (Hash[Symbol, untyped] data) -> Hash[Symbol, untyped]
|
|
83
169
|
def self.process_frames: (Hash[Symbol, untyped] data) -> Hash[Symbol, untyped]
|
|
84
170
|
|
|
171
|
+
# Merges a decoded header block into the stream's accumulated state,
|
|
172
|
+
# promoting the stream to `completed_requests` when END_STREAM is set.
|
|
173
|
+
#
|
|
174
|
+
# @param streams [Hash] current open-stream map
|
|
175
|
+
# @param completed_requests [Array<Hash>] accumulator of completed stream requests
|
|
176
|
+
# @param stream_id [Integer] the stream identifier
|
|
177
|
+
# @param decoded_headers [Array<Array(String, String)>] decoded header pairs
|
|
178
|
+
# @param end_stream [Boolean] whether the source frame had END_STREAM set
|
|
179
|
+
# @return [Array(Hash, Array<Hash>)] updated streams and completed_requests
|
|
180
|
+
#
|
|
181
|
+
# @rbs (Hash[Integer, Hash[Symbol, untyped]] streams, Array[Hash[Symbol, untyped]] completed_requests, Integer stream_id, Array[[String, String]] decoded_headers, bool end_stream) -> [Hash[Integer, Hash[Symbol, untyped]], Array[Hash[Symbol, untyped]]]
|
|
182
|
+
def self.finalize_headers: (Hash[Integer, Hash[Symbol, untyped]] streams, Array[Hash[Symbol, untyped]] completed_requests, Integer stream_id, Array[[ String, String ]] decoded_headers, bool end_stream) -> [ Hash[Integer, Hash[Symbol, untyped]], Array[Hash[Symbol, untyped]] ]
|
|
183
|
+
|
|
85
184
|
# Builds a frozen result hash from the current processing state.
|
|
86
185
|
#
|
|
87
186
|
# @param data [Hash] original connection state
|
|
@@ -90,12 +189,17 @@ module Raptor
|
|
|
90
189
|
# @param streams [Hash] updated stream states
|
|
91
190
|
# @param outgoing_frames [Array<String>] frames to write to the socket
|
|
92
191
|
# @param completed_requests [Array<Hash>] fully received stream requests
|
|
192
|
+
# @param window_updates [Array<Array(Integer, Integer)>] inbound WINDOW_UPDATE pairs as [stream_id, increment]
|
|
193
|
+
# @param peer_initial_window_size [Integer, nil] new SETTINGS_INITIAL_WINDOW_SIZE announced by the peer
|
|
93
194
|
# @param connection_window [Integer] current connection flow control window
|
|
94
195
|
# @param preface_received [Boolean] whether the connection preface has been received
|
|
196
|
+
# @param last_client_stream_id [Integer] highest client-initiated stream ID seen
|
|
197
|
+
# @param pending_headers [Hash, nil] in-progress HEADERS+CONTINUATION assembly
|
|
198
|
+
# @param close_connection [Boolean] whether the connection should be closed after writing outgoing frames
|
|
95
199
|
# @return [Hash] frozen result hash
|
|
96
200
|
#
|
|
97
|
-
# @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, Integer connection_window, bool preface_received) -> Hash[Symbol, untyped]
|
|
98
|
-
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, Integer connection_window, bool preface_received) -> Hash[Symbol, untyped]
|
|
201
|
+
# @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]
|
|
202
|
+
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]
|
|
99
203
|
|
|
100
204
|
# Handles a parsed HTTP/2 request from the ractor pool.
|
|
101
205
|
#
|
|
@@ -112,32 +216,47 @@ module Raptor
|
|
|
112
216
|
|
|
113
217
|
private
|
|
114
218
|
|
|
219
|
+
# Applies inbound flow-control updates from a parsed result to the
|
|
220
|
+
# connection's `FlowControl`.
|
|
221
|
+
#
|
|
222
|
+
# @param flow_control [FlowControl] the per-connection flow controller
|
|
223
|
+
# @param result [Hash] the parsed result from `process_frames`
|
|
224
|
+
# @return [void]
|
|
225
|
+
#
|
|
226
|
+
# @rbs (FlowControl flow_control, Hash[Symbol, untyped] result) -> void
|
|
227
|
+
def apply_flow_control_updates: (FlowControl flow_control, Hash[Symbol, untyped] result) -> void
|
|
228
|
+
|
|
115
229
|
# Dispatches a completed stream request to the Rack app and writes
|
|
116
230
|
# the response back as HTTP/2 frames.
|
|
117
231
|
#
|
|
118
232
|
# @param socket [OpenSSL::SSL::SSLSocket] the connection socket
|
|
119
233
|
# @param writer [Writer] lock-free frame writer for the connection
|
|
234
|
+
# @param flow_control [FlowControl] per-connection outbound flow controller
|
|
120
235
|
# @param stream_id [Integer] the HTTP/2 stream identifier
|
|
121
236
|
# @param headers [Array<Array(String, String)>] request headers
|
|
122
237
|
# @param body [String] request body
|
|
123
238
|
# @param remote_addr [String] the client IP address
|
|
124
239
|
# @return [void]
|
|
125
240
|
#
|
|
126
|
-
# @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id, Array[[String, String]] headers, String body, remote_addr: String) -> void
|
|
127
|
-
def dispatch_stream_request: (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id, Array[[ String, String ]] headers, String body, remote_addr: String) -> void
|
|
241
|
+
# @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, FlowControl flow_control, Integer stream_id, Array[[String, String]] headers, String body, remote_addr: String) -> void
|
|
242
|
+
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
|
|
128
243
|
|
|
129
244
|
# Writes a Rack response as HTTP/2 frames to the socket.
|
|
130
245
|
#
|
|
246
|
+
# DATA frames are partitioned through `flow_control` so each write fits
|
|
247
|
+
# within the peer's per-stream and connection windows.
|
|
248
|
+
#
|
|
131
249
|
# @param socket [OpenSSL::SSL::SSLSocket] the connection socket
|
|
132
250
|
# @param writer [Writer] lock-free frame writer for the connection
|
|
251
|
+
# @param flow_control [FlowControl] per-connection outbound flow controller
|
|
133
252
|
# @param stream_id [Integer] the HTTP/2 stream identifier
|
|
134
253
|
# @param status [Integer] HTTP status code
|
|
135
254
|
# @param headers [Hash] response headers from the Rack application
|
|
136
255
|
# @param body [Object] response body responding to each
|
|
137
256
|
# @return [void]
|
|
138
257
|
#
|
|
139
|
-
# @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> void
|
|
140
|
-
def write_http2_response: (OpenSSL::SSL::SSLSocket socket, Writer writer, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> void
|
|
258
|
+
# @rbs (OpenSSL::SSL::SSLSocket socket, Writer writer, FlowControl flow_control, Integer stream_id, Integer status, Hash[String, String | Array[String]] headers, untyped body) -> void
|
|
259
|
+
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) -> void
|
|
141
260
|
|
|
142
261
|
# Writes a 500 error response as HTTP/2 frames.
|
|
143
262
|
#
|
|
@@ -55,6 +55,8 @@ module Raptor
|
|
|
55
55
|
|
|
56
56
|
TIMEOUT_RESPONSE: ::String
|
|
57
57
|
|
|
58
|
+
@id_to_flow_control: Hash[Integer, untyped]
|
|
59
|
+
|
|
58
60
|
@id_to_writer: Hash[Integer, untyped]
|
|
59
61
|
|
|
60
62
|
@id_to_timeout: Hash[Integer, TimeoutClient]
|
|
@@ -169,6 +171,16 @@ module Raptor
|
|
|
169
171
|
# @rbs (Integer id) -> untyped?
|
|
170
172
|
def writer_for: (Integer id) -> untyped?
|
|
171
173
|
|
|
174
|
+
# Returns the flow controller associated with a given connection, if one
|
|
175
|
+
# was supplied when the connection was added. Used by HTTP/2 stream
|
|
176
|
+
# dispatchers to honour the peer's flow-control windows.
|
|
177
|
+
#
|
|
178
|
+
# @param id [Integer] unique client identifier
|
|
179
|
+
# @return [Object, nil] the flow controller, if found
|
|
180
|
+
#
|
|
181
|
+
# @rbs (Integer id) -> untyped?
|
|
182
|
+
def flow_control_for: (Integer id) -> untyped?
|
|
183
|
+
|
|
172
184
|
# Updates connection state for an HTTP/2 connection after frame processing.
|
|
173
185
|
#
|
|
174
186
|
# Re-registers the socket with the selector for further reads and stores
|
|
@@ -180,6 +192,16 @@ module Raptor
|
|
|
180
192
|
# @rbs (Hash[Symbol, untyped] state) -> void
|
|
181
193
|
def update_http2_state: (Hash[Symbol, untyped] state) -> void
|
|
182
194
|
|
|
195
|
+
# Closes the socket for the given connection and drops all reactor state
|
|
196
|
+
# associated with it. Used to terminate HTTP/2 connections after sending
|
|
197
|
+
# a GOAWAY frame.
|
|
198
|
+
#
|
|
199
|
+
# @param id [Integer] unique client identifier
|
|
200
|
+
# @return [void]
|
|
201
|
+
#
|
|
202
|
+
# @rbs (Integer id) -> void
|
|
203
|
+
def close_connection: (Integer id) -> void
|
|
204
|
+
|
|
183
205
|
# Initiates reactor shutdown.
|
|
184
206
|
#
|
|
185
207
|
# Closes the registration queue and wakes up the selector to begin
|
|
@@ -33,6 +33,8 @@ module Raptor
|
|
|
33
33
|
|
|
34
34
|
STATUS_WITH_NO_ENTITY_BODY: untyped
|
|
35
35
|
|
|
36
|
+
BAD_REQUEST_RESPONSE: ::String
|
|
37
|
+
|
|
36
38
|
INTERNAL_SERVER_ERROR_RESPONSE: ::String
|
|
37
39
|
|
|
38
40
|
CONTENT_TOO_LARGE_RESPONSE: ::String
|
|
@@ -78,6 +80,21 @@ module Raptor
|
|
|
78
80
|
# @rbs (String buffer, ?Integer? max_size) -> [String, Symbol]
|
|
79
81
|
def self.decode_chunked: (String buffer, ?Integer? max_size) -> [ String, Symbol ]
|
|
80
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.
|
|
87
|
+
#
|
|
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
|
+
|
|
81
98
|
@on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
|
|
82
99
|
|
|
83
100
|
@body_spool_threshold: Integer?
|
|
@@ -101,6 +118,14 @@ module Raptor
|
|
|
101
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
|
|
102
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
|
|
103
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
|
|
128
|
+
|
|
104
129
|
# Eagerly reads and parses the first request on a freshly accepted
|
|
105
130
|
# connection on the server thread, dispatching directly to the thread pool
|
|
106
131
|
# when complete. Falls back to the reactor when more data is needed.
|
|
@@ -225,6 +250,15 @@ module Raptor
|
|
|
225
250
|
# @rbs (TCPSocket socket) -> void
|
|
226
251
|
def reject_oversized: (TCPSocket socket) -> void
|
|
227
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
|
+
|
|
228
262
|
# Builds a Rack environment hash from parsed HTTP request data.
|
|
229
263
|
#
|
|
230
264
|
# Populates all required Rack env keys including rack.* keys, REMOTE_ADDR,
|
|
@@ -499,19 +533,6 @@ module Raptor
|
|
|
499
533
|
# @rbs (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
|
|
500
534
|
def call_response_finished: (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
|
|
501
535
|
|
|
502
|
-
# Writes a string to the socket, retrying on partial writes and flow control blocks.
|
|
503
|
-
#
|
|
504
|
-
# Uses write_nonblock with a 5-second writable timeout to avoid blocking the
|
|
505
|
-
# thread indefinitely on slow clients.
|
|
506
|
-
#
|
|
507
|
-
# @param socket [TCPSocket] the socket to write to
|
|
508
|
-
# @param string [String] the data to write
|
|
509
|
-
# @return [void]
|
|
510
|
-
# @raise [WriteError] if the socket is not writable within the timeout or raises IOError
|
|
511
|
-
#
|
|
512
|
-
# @rbs (TCPSocket socket, String string) -> void
|
|
513
|
-
def socket_write: (TCPSocket socket, String string) -> void
|
|
514
|
-
|
|
515
536
|
# Enables TCP_CORK on the socket to batch outgoing packets into fewer segments.
|
|
516
537
|
#
|
|
517
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
|