raptor 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +10 -10
- data/docs/raptor-vs-puma.md +29 -29
- data/ext/raptor_bpf/reuseport_select.bpf.c +8 -4
- data/lib/raptor/binder.rb +37 -87
- data/lib/raptor/cli.rb +9 -25
- data/lib/raptor/cluster.rb +17 -53
- data/lib/raptor/http.rb +18 -0
- data/lib/raptor/http1.rb +244 -233
- data/lib/raptor/http2.rb +42 -63
- data/lib/raptor/reactor.rb +21 -52
- data/lib/raptor/reuseport_bpf.rb +14 -5
- data/lib/raptor/server.rb +10 -36
- data/lib/raptor/stats.rb +3 -8
- data/lib/raptor/systemd.rb +1 -1
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/binder.rbs +34 -80
- data/sig/generated/raptor/cli.rbs +9 -25
- data/sig/generated/raptor/cluster.rbs +10 -51
- data/sig/generated/raptor/http.rbs +10 -0
- data/sig/generated/raptor/http1.rbs +129 -113
- data/sig/generated/raptor/http2.rbs +31 -48
- data/sig/generated/raptor/reactor.rbs +21 -52
- data/sig/generated/raptor/reuseport_bpf.rbs +11 -2
- data/sig/generated/raptor/server.rbs +9 -35
- data/sig/generated/raptor/stats.rbs +3 -8
- metadata +1 -1
data/lib/raptor/http2.rb
CHANGED
|
@@ -12,16 +12,9 @@ require_relative "raptor_http2"
|
|
|
12
12
|
module Raptor
|
|
13
13
|
# Handles HTTP/2 request processing and Rack application integration.
|
|
14
14
|
#
|
|
15
|
-
# Http2 manages the HTTP/2 protocol lifecycle including frame processing,
|
|
16
|
-
# HPACK header compression, stream management, and response writing.
|
|
17
|
-
# It integrates with the same reactor, ractor pool, and thread pool
|
|
18
|
-
# pipeline used by HTTP/1.1 connections.
|
|
19
|
-
#
|
|
20
15
|
class Http2
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# Serializes concurrent socket writes from multiple stream workers
|
|
24
|
-
# without blocking any of them.
|
|
16
|
+
# Serialises concurrent frame writes on a single HTTP/2 connection so
|
|
17
|
+
# exactly one thread is writing at any moment.
|
|
25
18
|
#
|
|
26
19
|
class Writer
|
|
27
20
|
IDLE = :idle
|
|
@@ -49,7 +42,7 @@ module Raptor
|
|
|
49
42
|
#
|
|
50
43
|
# @rbs (OpenSSL::SSL::SSLSocket socket, Array[String] frames) -> void
|
|
51
44
|
def write_frames(socket, frames)
|
|
52
|
-
return if frames
|
|
45
|
+
return if !frames || frames.empty?
|
|
53
46
|
|
|
54
47
|
claimed = false
|
|
55
48
|
@state.swap do |current|
|
|
@@ -80,14 +73,8 @@ module Raptor
|
|
|
80
73
|
end
|
|
81
74
|
end
|
|
82
75
|
|
|
83
|
-
# Per-connection outbound flow-control accounting.
|
|
84
|
-
#
|
|
85
76
|
# Tracks the peer's connection-level and per-stream receive windows so
|
|
86
|
-
# outbound DATA frames respect RFC 7540 section 5.2.
|
|
87
|
-
# responses call `acquire` to reserve send capacity; threads applying
|
|
88
|
-
# inbound WINDOW_UPDATE or SETTINGS frames call the mutating methods to
|
|
89
|
-
# replenish it. The connection window and per-stream windows live in
|
|
90
|
-
# separate `Atom`s so the common fast path skips per-stream tracking.
|
|
77
|
+
# outbound `DATA` frames respect RFC 7540 section 5.2.
|
|
91
78
|
#
|
|
92
79
|
class FlowControl
|
|
93
80
|
ACQUIRE_POLL_INTERVAL = 0.001
|
|
@@ -98,6 +85,8 @@ module Raptor
|
|
|
98
85
|
|
|
99
86
|
# Creates a new FlowControl with the spec-default windows.
|
|
100
87
|
#
|
|
88
|
+
# @return [void]
|
|
89
|
+
#
|
|
101
90
|
# @rbs () -> void
|
|
102
91
|
def initialize
|
|
103
92
|
@connection_window = Atom.new(DEFAULT_WINDOW_SIZE)
|
|
@@ -109,12 +98,6 @@ module Raptor
|
|
|
109
98
|
# least one byte is available on both the connection and stream
|
|
110
99
|
# windows. The returned size is capped at `MAX_FRAME_SIZE`.
|
|
111
100
|
#
|
|
112
|
-
# When `end_stream` is true, `max_bytes` fits within the peer's
|
|
113
|
-
# initial stream window, and no per-stream override has been
|
|
114
|
-
# recorded, only the connection window is consulted. The stream
|
|
115
|
-
# closes on this frame, so its remaining send window will not be
|
|
116
|
-
# consulted again and need not be tracked.
|
|
117
|
-
#
|
|
118
101
|
# @param stream_id [Integer] the HTTP/2 stream identifier
|
|
119
102
|
# @param max_bytes [Integer] the largest size the caller would like to send
|
|
120
103
|
# @param end_stream [Boolean] true when this is the final frame on the stream
|
|
@@ -127,11 +110,7 @@ module Raptor
|
|
|
127
110
|
|
|
128
111
|
if end_stream && capped <= initial && !@stream_windows.value.key?(stream_id)
|
|
129
112
|
loop do
|
|
130
|
-
granted =
|
|
131
|
-
@connection_window.swap do |window|
|
|
132
|
-
granted = window > capped ? capped : window
|
|
133
|
-
granted > 0 ? window - granted : window
|
|
134
|
-
end
|
|
113
|
+
granted = reserve_connection(capped)
|
|
135
114
|
return granted if granted > 0
|
|
136
115
|
|
|
137
116
|
sleep ACQUIRE_POLL_INTERVAL
|
|
@@ -141,14 +120,7 @@ module Raptor
|
|
|
141
120
|
loop do
|
|
142
121
|
stream_window = @stream_windows.value[stream_id] || initial
|
|
143
122
|
capped_full = capped < stream_window ? capped : stream_window
|
|
144
|
-
|
|
145
|
-
granted = 0
|
|
146
|
-
if capped_full > 0
|
|
147
|
-
@connection_window.swap do |window|
|
|
148
|
-
granted = window > capped_full ? capped_full : window
|
|
149
|
-
granted > 0 ? window - granted : window
|
|
150
|
-
end
|
|
151
|
-
end
|
|
123
|
+
granted = capped_full > 0 ? reserve_connection(capped_full) : 0
|
|
152
124
|
|
|
153
125
|
if granted > 0
|
|
154
126
|
@stream_windows.swap do |windows|
|
|
@@ -162,8 +134,7 @@ module Raptor
|
|
|
162
134
|
end
|
|
163
135
|
end
|
|
164
136
|
|
|
165
|
-
# Increments the connection-level send window
|
|
166
|
-
# sends a WINDOW_UPDATE on stream 0.
|
|
137
|
+
# Increments the connection-level send window by `increment` bytes.
|
|
167
138
|
#
|
|
168
139
|
# @param increment [Integer] the byte count to add
|
|
169
140
|
# @return [void]
|
|
@@ -173,8 +144,7 @@ module Raptor
|
|
|
173
144
|
@connection_window.swap { |window| window + increment }
|
|
174
145
|
end
|
|
175
146
|
|
|
176
|
-
# Increments the
|
|
177
|
-
# a WINDOW_UPDATE on a specific stream.
|
|
147
|
+
# Increments the send window for the given stream by `increment` bytes.
|
|
178
148
|
#
|
|
179
149
|
# @param stream_id [Integer] the HTTP/2 stream identifier
|
|
180
150
|
# @param increment [Integer] the byte count to add
|
|
@@ -207,9 +177,7 @@ module Raptor
|
|
|
207
177
|
end
|
|
208
178
|
end
|
|
209
179
|
|
|
210
|
-
# Discards any per-stream tracking for the given stream.
|
|
211
|
-
# after a stream closes so `@stream_windows` does not grow without
|
|
212
|
-
# bound across the lifetime of a connection.
|
|
180
|
+
# Discards any per-stream tracking for the given stream.
|
|
213
181
|
#
|
|
214
182
|
# @param stream_id [Integer] the HTTP/2 stream identifier
|
|
215
183
|
# @return [void]
|
|
@@ -226,6 +194,25 @@ module Raptor
|
|
|
226
194
|
pruned
|
|
227
195
|
end
|
|
228
196
|
end
|
|
197
|
+
|
|
198
|
+
private
|
|
199
|
+
|
|
200
|
+
# Reserves up to `capped` bytes from the connection window,
|
|
201
|
+
# returning the number of bytes granted (0 when the window is
|
|
202
|
+
# exhausted).
|
|
203
|
+
#
|
|
204
|
+
# @param capped [Integer] the largest reservation the caller will accept
|
|
205
|
+
# @return [Integer]
|
|
206
|
+
#
|
|
207
|
+
# @rbs (Integer capped) -> Integer
|
|
208
|
+
def reserve_connection(capped)
|
|
209
|
+
granted = 0
|
|
210
|
+
@connection_window.swap do |window|
|
|
211
|
+
granted = window > capped ? capped : window
|
|
212
|
+
granted > 0 ? window - granted : window
|
|
213
|
+
end
|
|
214
|
+
granted
|
|
215
|
+
end
|
|
229
216
|
end
|
|
230
217
|
|
|
231
218
|
EAGER_READ_TIMEOUT = 0.001
|
|
@@ -287,9 +274,10 @@ module Raptor
|
|
|
287
274
|
# @rbs @on_error: ^(Hash[String, untyped]?, Exception) -> void | nil
|
|
288
275
|
# @rbs @initial_settings_frame: String
|
|
289
276
|
|
|
290
|
-
#
|
|
277
|
+
# Returns the initial server SETTINGS frame to send on every new
|
|
278
|
+
# HTTP/2 connection.
|
|
291
279
|
#
|
|
292
|
-
# @return [String]
|
|
280
|
+
# @return [String]
|
|
293
281
|
attr_reader :initial_settings_frame
|
|
294
282
|
|
|
295
283
|
# Creates a new Http2 handler.
|
|
@@ -329,11 +317,9 @@ module Raptor
|
|
|
329
317
|
Writer.new(write_timeout: @write_timeout)
|
|
330
318
|
end
|
|
331
319
|
|
|
332
|
-
#
|
|
333
|
-
#
|
|
334
|
-
# Parses frames, handles HPACK decoding, tracks stream state, and returns
|
|
320
|
+
# Advances HTTP/2 frame parsing from the connection buffer, returning
|
|
335
321
|
# updated connection state along with any outgoing protocol frames and
|
|
336
|
-
# completed stream requests.
|
|
322
|
+
# completed stream requests.
|
|
337
323
|
#
|
|
338
324
|
# @param data [Hash] the connection state including buffer and HPACK table
|
|
339
325
|
# @return [Hash] updated state with outgoing_frames and completed_requests
|
|
@@ -414,7 +400,7 @@ module Raptor
|
|
|
414
400
|
end
|
|
415
401
|
|
|
416
402
|
when :continuation
|
|
417
|
-
if pending_headers
|
|
403
|
+
if !pending_headers || frame[:stream_id] != pending_headers[:stream_id]
|
|
418
404
|
goaway_error = ERROR_PROTOCOL_ERROR
|
|
419
405
|
break
|
|
420
406
|
end
|
|
@@ -566,12 +552,10 @@ module Raptor
|
|
|
566
552
|
end
|
|
567
553
|
private_class_method :build_result
|
|
568
554
|
|
|
569
|
-
# Handles a parsed HTTP/2
|
|
570
|
-
#
|
|
571
|
-
#
|
|
572
|
-
#
|
|
573
|
-
# consumes subsequent frame batches that are already buffered, skipping
|
|
574
|
-
# the reactor and ractor pool hops while the connection is hot.
|
|
555
|
+
# Handles a parsed HTTP/2 result. Writes outgoing frames, dispatches
|
|
556
|
+
# completed stream requests to the thread pool, and eagerly consumes
|
|
557
|
+
# further buffered frame batches before returning control to the
|
|
558
|
+
# reactor.
|
|
575
559
|
#
|
|
576
560
|
# @param result [Hash] the parsed result from the ractor pool
|
|
577
561
|
# @param reactor [Reactor] the reactor managing the connection
|
|
@@ -706,10 +690,8 @@ module Raptor
|
|
|
706
690
|
flow_control.discard_stream(stream_id) if flow_control
|
|
707
691
|
end
|
|
708
692
|
|
|
709
|
-
# Writes a Rack response as HTTP/2 frames to the socket
|
|
710
|
-
#
|
|
711
|
-
# DATA frames are partitioned through `flow_control` so each write fits
|
|
712
|
-
# within the peer's per-stream and connection windows.
|
|
693
|
+
# Writes a Rack response as HTTP/2 frames to the socket, partitioning
|
|
694
|
+
# DATA frames through `flow_control` to fit within the peer's windows.
|
|
713
695
|
#
|
|
714
696
|
# @param socket [OpenSSL::SSL::SSLSocket] the connection socket
|
|
715
697
|
# @param writer [Writer] lock-free frame writer for the connection
|
|
@@ -805,9 +787,6 @@ module Raptor
|
|
|
805
787
|
|
|
806
788
|
# Builds a Rack environment hash from HTTP/2 headers and body.
|
|
807
789
|
#
|
|
808
|
-
# Translates HTTP/2 pseudo-headers into Rack-compatible environment keys
|
|
809
|
-
# and populates all required Rack env entries.
|
|
810
|
-
#
|
|
811
790
|
# @param headers [Array<Array(String, String)>] HTTP/2 header pairs
|
|
812
791
|
# @param body [String] the request body
|
|
813
792
|
# @param remote_addr [String] the client IP address
|
data/lib/raptor/reactor.rb
CHANGED
|
@@ -5,25 +5,10 @@ require "nio"
|
|
|
5
5
|
require "red-black-tree"
|
|
6
6
|
|
|
7
7
|
module Raptor
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# It coordinates between ractor pools for CPU-intensive HTTP parsing and
|
|
13
|
-
# thread pools for blocking operations, and provides backlog metrics that
|
|
14
|
-
# the server uses for backpressure control to prevent overload.
|
|
15
|
-
#
|
|
16
|
-
# @example
|
|
17
|
-
# reactor = Reactor.new(
|
|
18
|
-
# ractor_pool,
|
|
19
|
-
# thread_pool,
|
|
20
|
-
# connection_options: { first_data_timeout: 30, chunk_data_timeout: 10 },
|
|
21
|
-
# http1_options: { persistent_data_timeout: 65 }
|
|
22
|
-
# )
|
|
23
|
-
# reactor.run
|
|
24
|
-
# reactor.add(id: client.object_id, socket: client)
|
|
25
|
-
# # ... later
|
|
26
|
-
# reactor.shutdown
|
|
8
|
+
# Multiplexes client connections and closes them when they overrun
|
|
9
|
+
# their per-phase timeouts (first data, subsequent chunks, and
|
|
10
|
+
# keep-alive idle). Feeds ready bytes to the parser pipeline and hands
|
|
11
|
+
# completed requests back to the caller-provided handlers.
|
|
27
12
|
#
|
|
28
13
|
class Reactor
|
|
29
14
|
# A client connection node ordered by absolute expiry time so the
|
|
@@ -33,7 +18,7 @@ module Raptor
|
|
|
33
18
|
# @rbs attr_accessor timeout_at: Float
|
|
34
19
|
attr_accessor :timeout_at
|
|
35
20
|
|
|
36
|
-
#
|
|
21
|
+
# Returns the client connection state.
|
|
37
22
|
#
|
|
38
23
|
# @return [Hash] the client connection state
|
|
39
24
|
#
|
|
@@ -112,11 +97,8 @@ module Raptor
|
|
|
112
97
|
@id_to_flow_control = {}
|
|
113
98
|
end
|
|
114
99
|
|
|
115
|
-
# Starts the reactor's main event loop in a new thread.
|
|
116
|
-
#
|
|
117
|
-
# The event loop handles I/O events, processes timeouts, manages
|
|
118
|
-
# the registration queue, and controls server connection acceptance.
|
|
119
|
-
# It continues until the queue is closed and emptied.
|
|
100
|
+
# Starts the reactor's main event loop in a new thread. Runs until
|
|
101
|
+
# the registration queue is closed and drained.
|
|
120
102
|
#
|
|
121
103
|
# @return [Thread] the thread running the reactor event loop
|
|
122
104
|
#
|
|
@@ -185,10 +167,8 @@ module Raptor
|
|
|
185
167
|
read_and_queue_for_parse(socket, state)
|
|
186
168
|
end
|
|
187
169
|
|
|
188
|
-
# Updates the state of an existing client connection
|
|
189
|
-
#
|
|
190
|
-
# Called when an incomplete HTTP request needs to be
|
|
191
|
-
# re-registered with the reactor for further processing.
|
|
170
|
+
# Updates the state of an existing client connection and re-registers
|
|
171
|
+
# it for further I/O.
|
|
192
172
|
#
|
|
193
173
|
# @param state [Hash] updated client connection state
|
|
194
174
|
# @option state [Integer] :id client identifier
|
|
@@ -220,11 +200,8 @@ module Raptor
|
|
|
220
200
|
end
|
|
221
201
|
end
|
|
222
202
|
|
|
223
|
-
# Re-registers a kept-alive connection for the next request cycle
|
|
224
|
-
#
|
|
225
|
-
# Called after successfully writing a response when keep-alive is active.
|
|
226
|
-
# Resets the connection state and re-queues the socket in the selector
|
|
227
|
-
# using the persistent data timeout.
|
|
203
|
+
# Re-registers a kept-alive connection for the next request cycle
|
|
204
|
+
# under the persistent-data timeout.
|
|
228
205
|
#
|
|
229
206
|
# @param socket [TCPSocket] the kept-alive client socket
|
|
230
207
|
# @param id [Integer] the unique client identifier
|
|
@@ -253,9 +230,6 @@ module Raptor
|
|
|
253
230
|
|
|
254
231
|
# Returns the socket for a given client identifier without removing it.
|
|
255
232
|
#
|
|
256
|
-
# Used by HTTP/2 connections where the socket remains registered across
|
|
257
|
-
# multiple stream requests.
|
|
258
|
-
#
|
|
259
233
|
# @param id [Integer] unique client identifier
|
|
260
234
|
# @return [TCPSocket, nil] the socket, if found
|
|
261
235
|
#
|
|
@@ -265,8 +239,7 @@ module Raptor
|
|
|
265
239
|
end
|
|
266
240
|
|
|
267
241
|
# Returns the writer object associated with a given connection, if one
|
|
268
|
-
# was supplied when the connection was added.
|
|
269
|
-
# that need to coordinate concurrent socket writes.
|
|
242
|
+
# was supplied when the connection was added.
|
|
270
243
|
#
|
|
271
244
|
# @param id [Integer] unique client identifier
|
|
272
245
|
# @return [Object, nil] the writer, if found
|
|
@@ -276,9 +249,8 @@ module Raptor
|
|
|
276
249
|
@id_to_writer[id]
|
|
277
250
|
end
|
|
278
251
|
|
|
279
|
-
# Returns the flow controller associated with a given connection, if
|
|
280
|
-
# was supplied when the connection was added.
|
|
281
|
-
# dispatchers to honour the peer's flow-control windows.
|
|
252
|
+
# Returns the flow controller associated with a given connection, if
|
|
253
|
+
# one was supplied when the connection was added.
|
|
282
254
|
#
|
|
283
255
|
# @param id [Integer] unique client identifier
|
|
284
256
|
# @return [Object, nil] the flow controller, if found
|
|
@@ -288,10 +260,8 @@ module Raptor
|
|
|
288
260
|
@id_to_flow_control[id]
|
|
289
261
|
end
|
|
290
262
|
|
|
291
|
-
# Updates connection state for an HTTP/2 connection
|
|
292
|
-
#
|
|
293
|
-
# Re-registers the socket with the selector for further reads and stores
|
|
294
|
-
# the updated HPACK table and stream states.
|
|
263
|
+
# Updates connection state for an HTTP/2 connection and re-registers
|
|
264
|
+
# the socket for further reads.
|
|
295
265
|
#
|
|
296
266
|
# @param state [Hash] updated connection state from the ractor pool
|
|
297
267
|
# @return [void]
|
|
@@ -308,9 +278,8 @@ module Raptor
|
|
|
308
278
|
socket.close
|
|
309
279
|
end
|
|
310
280
|
|
|
311
|
-
# Closes the socket for the given connection and drops all reactor
|
|
312
|
-
# associated with it.
|
|
313
|
-
# a GOAWAY frame.
|
|
281
|
+
# Closes the socket for the given connection and drops all reactor
|
|
282
|
+
# state associated with it.
|
|
314
283
|
#
|
|
315
284
|
# @param id [Integer] unique client identifier
|
|
316
285
|
# @return [void]
|
|
@@ -386,8 +355,8 @@ module Raptor
|
|
|
386
355
|
read_and_queue_for_parse(socket, state)
|
|
387
356
|
end
|
|
388
357
|
|
|
389
|
-
# Reads data from a socket and either queues it for parsing
|
|
390
|
-
#
|
|
358
|
+
# Reads data from a socket and either queues it for parsing or
|
|
359
|
+
# returns it to the selector.
|
|
391
360
|
#
|
|
392
361
|
# @param socket [TCPSocket] the socket to read from and queue
|
|
393
362
|
# @param state [Hash] current connection state
|
|
@@ -431,7 +400,7 @@ module Raptor
|
|
|
431
400
|
socket.close
|
|
432
401
|
end
|
|
433
402
|
|
|
434
|
-
#
|
|
403
|
+
# Returns true when the request has been fully parsed.
|
|
435
404
|
#
|
|
436
405
|
# @param state [Hash] connection state
|
|
437
406
|
# @return [Boolean] true if the request is complete
|
data/lib/raptor/reuseport_bpf.rb
CHANGED
|
@@ -23,8 +23,8 @@ module Raptor
|
|
|
23
23
|
LIBBPF_LOADED = !!defined?(LibBPFRuby)
|
|
24
24
|
BPF_OBJECT_PATH = File.expand_path("../../ext/raptor_bpf/reuseport_select.bpf.o", __dir__)
|
|
25
25
|
|
|
26
|
-
#
|
|
27
|
-
#
|
|
26
|
+
# Returns true when the preconditions for BPF-directed reuseport are
|
|
27
|
+
# met on this platform.
|
|
28
28
|
#
|
|
29
29
|
# @return [Boolean]
|
|
30
30
|
#
|
|
@@ -65,9 +65,6 @@ module Raptor
|
|
|
65
65
|
#
|
|
66
66
|
# @rbs (Array[String] bind_uris, Integer worker_index, Integer socket_backlog) -> Array[TCPServer]
|
|
67
67
|
def self.create_worker_listeners(bind_uris, worker_index, socket_backlog)
|
|
68
|
-
@load_key = [worker_index + 1].pack("L")
|
|
69
|
-
@load_value = String.new("\x00\x00\x00\x00", encoding: Encoding::ASCII_8BIT)
|
|
70
|
-
|
|
71
68
|
bind_uris.filter_map do |bind_uri|
|
|
72
69
|
uri = URI(bind_uri)
|
|
73
70
|
next unless uri.scheme == "tcp"
|
|
@@ -92,6 +89,18 @@ module Raptor
|
|
|
92
89
|
end
|
|
93
90
|
end
|
|
94
91
|
|
|
92
|
+
# Prepares this worker's load-reporting slot so subsequent
|
|
93
|
+
# {update_load} calls can publish its backlog.
|
|
94
|
+
#
|
|
95
|
+
# @param worker_index [Integer] slot index for this worker
|
|
96
|
+
# @return [void]
|
|
97
|
+
#
|
|
98
|
+
# @rbs (Integer worker_index) -> void
|
|
99
|
+
def self.enable_load_reporting(worker_index)
|
|
100
|
+
@load_key = [worker_index + 1].pack("L")
|
|
101
|
+
@load_value = String.new("\x00\x00\x00\x00", encoding: Encoding::ASCII_8BIT)
|
|
102
|
+
end
|
|
103
|
+
|
|
95
104
|
# Publishes this worker's current backlog to the BPF map.
|
|
96
105
|
#
|
|
97
106
|
# @param backlog [Integer] the worker's current reactor backlog
|
data/lib/raptor/server.rb
CHANGED
|
@@ -8,26 +8,10 @@ require "atomic-ruby/atomic_boolean"
|
|
|
8
8
|
require_relative "reuseport_bpf"
|
|
9
9
|
|
|
10
10
|
module Raptor
|
|
11
|
-
# Accepts client connections
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# Supports TCP, Unix, and SSL listeners. SSL handshakes are offloaded
|
|
17
|
-
# to the thread pool so a slow client can't pin the server thread.
|
|
18
|
-
# For HTTP/1.1 the first request is parsed inline and dispatched
|
|
19
|
-
# straight to the thread pool; HTTP/2 (negotiated via ALPN) is
|
|
20
|
-
# registered with the reactor for frame processing.
|
|
21
|
-
#
|
|
22
|
-
# @example
|
|
23
|
-
# binder = Binder.new(["tcp://0.0.0.0:3000"])
|
|
24
|
-
# reactor = Reactor.new(ractor_pool, thread_pool, connection_options: {}, http1_options: {})
|
|
25
|
-
# http1 = Http1.new(app, 3000)
|
|
26
|
-
# http2 = Http2.new(app, 3000)
|
|
27
|
-
# server = Server.new(binder, reactor, thread_pool, http1, http2, connection_options: { first_data_timeout: 30 }, listeners: binder.listeners)
|
|
28
|
-
# server.run
|
|
29
|
-
# # ... later
|
|
30
|
-
# server.shutdown
|
|
11
|
+
# Accepts client connections on TCP, Unix, and SSL listeners and
|
|
12
|
+
# dispatches them into the request pipeline. Backs off from accepting
|
|
13
|
+
# when the reactor backlog is high so an overloaded process leaves
|
|
14
|
+
# connections for peers to absorb.
|
|
31
15
|
#
|
|
32
16
|
class Server
|
|
33
17
|
HTTP_SCHEME = "http"
|
|
@@ -80,11 +64,6 @@ module Raptor
|
|
|
80
64
|
|
|
81
65
|
# Starts the server's main accept loop in a new thread.
|
|
82
66
|
#
|
|
83
|
-
# The accept loop polls listening sockets for ready connections and accepts
|
|
84
|
-
# them when the reactor backlog is under the backpressure threshold. On
|
|
85
|
-
# Linux with BPF-directed dispatch active, a companion thread publishes
|
|
86
|
-
# this worker's backlog to the BPF map.
|
|
87
|
-
#
|
|
88
67
|
# @return [Thread] the thread running the accept loop
|
|
89
68
|
#
|
|
90
69
|
# @rbs () -> Thread
|
|
@@ -148,7 +127,7 @@ module Raptor
|
|
|
148
127
|
|
|
149
128
|
while @running.true?
|
|
150
129
|
ReuseportBPF.update_load(@reactor.backlog)
|
|
151
|
-
sleep 0.
|
|
130
|
+
sleep 0.001
|
|
152
131
|
end
|
|
153
132
|
end
|
|
154
133
|
end
|
|
@@ -167,13 +146,8 @@ module Raptor
|
|
|
167
146
|
end
|
|
168
147
|
end
|
|
169
148
|
|
|
170
|
-
# Accepts a connection from the given listener and dispatches it
|
|
171
|
-
#
|
|
172
|
-
# For SSL listeners the TLS handshake is offloaded to the thread pool so
|
|
173
|
-
# a slow client cannot block the server thread. For SSL connections with
|
|
174
|
-
# h2 negotiated via ALPN, the server sends initial SETTINGS and adds the
|
|
175
|
-
# connection to the reactor as an HTTP/2 connection. All other connections
|
|
176
|
-
# follow the HTTP/1.1 path.
|
|
149
|
+
# Accepts a connection from the given listener and dispatches it into
|
|
150
|
+
# the HTTP/1.1 or SSL/HTTP-2 pipeline.
|
|
177
151
|
#
|
|
178
152
|
# @param listener [TCPServer, UNIXServer, Binder::SslListener] the ready listener
|
|
179
153
|
# @return [Boolean] true if a connection was accepted, false if the listener had nothing to dispatch
|
|
@@ -210,9 +184,9 @@ module Raptor
|
|
|
210
184
|
true
|
|
211
185
|
end
|
|
212
186
|
|
|
213
|
-
# Performs the TLS handshake for an accepted SSL connection
|
|
214
|
-
# it through the HTTP/2 or
|
|
215
|
-
#
|
|
187
|
+
# Performs the TLS handshake for an accepted SSL connection, bounded
|
|
188
|
+
# by `:first_data_timeout`, and dispatches it through the HTTP/2 or
|
|
189
|
+
# HTTP/1.1 path.
|
|
216
190
|
#
|
|
217
191
|
# @param listener [Binder::SslListener] the SSL listener that accepted the connection
|
|
218
192
|
# @param tcp_client [TCPSocket] the accepted TCP socket
|
data/lib/raptor/stats.rb
CHANGED
|
@@ -4,12 +4,8 @@
|
|
|
4
4
|
require "mmap-ruby"
|
|
5
5
|
|
|
6
6
|
module Raptor
|
|
7
|
-
# Shared
|
|
8
|
-
#
|
|
9
|
-
# Stats uses an anonymous mmap (MAP_ANON | MAP_SHARED) created before
|
|
10
|
-
# forking so that worker processes can write their stats and the master
|
|
11
|
-
# process can read them without any pipes or signals. Each worker is
|
|
12
|
-
# assigned a fixed-size slot in the shared region.
|
|
7
|
+
# Shared-memory store for per-worker process statistics. Each worker
|
|
8
|
+
# holds a fixed-size slot in an anonymous mmap region.
|
|
13
9
|
#
|
|
14
10
|
# Binary layout per slot (native byte order):
|
|
15
11
|
# pid uint32 4 bytes
|
|
@@ -31,8 +27,7 @@ module Raptor
|
|
|
31
27
|
# @rbs @num_workers: Integer
|
|
32
28
|
# @rbs @mmap: untyped
|
|
33
29
|
|
|
34
|
-
# Allocates the shared mmap region
|
|
35
|
-
# workers so the mapping is inherited by every child process.
|
|
30
|
+
# Allocates the shared mmap region for `num_workers` slots.
|
|
36
31
|
#
|
|
37
32
|
# @param num_workers [Integer] number of worker slots to allocate
|
|
38
33
|
# @return [void]
|
data/lib/raptor/systemd.rb
CHANGED
|
@@ -24,7 +24,7 @@ module Raptor
|
|
|
24
24
|
# @rbs (String message) -> bool
|
|
25
25
|
def self.notify(message)
|
|
26
26
|
socket_path = ENV[NOTIFY_SOCKET_ENV]
|
|
27
|
-
return false if socket_path
|
|
27
|
+
return false if !socket_path || socket_path.empty?
|
|
28
28
|
|
|
29
29
|
address = if socket_path.start_with?("@")
|
|
30
30
|
Socket.pack_sockaddr_un("\0#{socket_path[1..]}")
|
data/lib/raptor/version.rb
CHANGED