raptor 0.6.0 → 0.7.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 +3 -3
- data/lib/raptor/http2.rb +64 -21
- data/lib/raptor/request.rb +2 -2
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/http2.rbs +18 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d4a7acc08848f71017602d3abfb38b7d352a48d7bc7929e54f3a693dc3a0fe83
|
|
4
|
+
data.tar.gz: 1ee020abeb67db2a2eba6ff64f59054bf2e91447bd3996464f5a3279c3d3e748
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b5b25843f29afe7e47a90c85f48ba5f9d06e7f5dc819d24595821940f8207fa46054208f38601a455271d026246bfd32144329782f1a842f44cd500bf3d10bbf
|
|
7
|
+
data.tar.gz: 5c1f9f18fbf946a8e4a648bed675f83ba7d71bdc9a85354b5df1ccc6ba39bd65b5740c31e1ffd11f4f88960b52aaee0ce2b46747fac52907bae6b6598f4e390e
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
|
@@ -66,9 +66,9 @@ Raptor 0.6.0 vs Puma 8.0.2:
|
|
|
66
66
|
|
|
67
67
|
| Protocol | Raptor | Puma |
|
|
68
68
|
| --------------------- | ----------- | ----------- |
|
|
69
|
-
| HTTP/1.1 |
|
|
70
|
-
| HTTP/1.1 (keep-alive) |
|
|
71
|
-
| HTTP/2 |
|
|
69
|
+
| HTTP/1.1 | 17.9k req/s | 16.8k req/s |
|
|
70
|
+
| HTTP/1.1 (keep-alive) | 60k req/s | 29.6k req/s |
|
|
71
|
+
| HTTP/2 | 57.2k req/s | N/A |
|
|
72
72
|
|
|
73
73
|
> ruby 4.0.5 (2026-05-20 revision 64336ffd0e) +YJIT +PRISM [arm64-darwin23]
|
|
74
74
|
> 4 workers, 3 threads, 12 concurrent connections
|
data/lib/raptor/http2.rb
CHANGED
|
@@ -223,6 +223,10 @@ module Raptor
|
|
|
223
223
|
end
|
|
224
224
|
end
|
|
225
225
|
|
|
226
|
+
EAGER_READ_TIMEOUT = 0.001
|
|
227
|
+
EAGER_READ_BUFFER_SIZE = 64 * 1024
|
|
228
|
+
EAGER_MAX_ROUNDS = 4
|
|
229
|
+
|
|
226
230
|
FLAG_END_STREAM = 0x1
|
|
227
231
|
FLAG_END_HEADERS = 0x4
|
|
228
232
|
FLAG_ACK = 0x1
|
|
@@ -236,7 +240,7 @@ module Raptor
|
|
|
236
240
|
|
|
237
241
|
SERVER_PROTOCOL = "HTTP/2"
|
|
238
242
|
RACK_HEADER_PREFIX = "rack."
|
|
239
|
-
HOP_BY_HOP_HEADERS =
|
|
243
|
+
HOP_BY_HOP_HEADERS = ["connection", "transfer-encoding", "keep-alive", "upgrade", "proxy-connection"].freeze
|
|
240
244
|
|
|
241
245
|
# @rbs @app: ^(Hash[String, untyped]) -> [Integer, Hash[String, String | Array[String]], untyped]
|
|
242
246
|
# @rbs @server_port: Integer
|
|
@@ -500,7 +504,9 @@ module Raptor
|
|
|
500
504
|
# Handles a parsed HTTP/2 request from the ractor pool.
|
|
501
505
|
#
|
|
502
506
|
# Writes outgoing protocol frames to the socket, updates reactor state,
|
|
503
|
-
# and dispatches completed stream requests to the thread pool.
|
|
507
|
+
# and dispatches completed stream requests to the thread pool. Eagerly
|
|
508
|
+
# consumes subsequent frame batches that are already buffered, skipping
|
|
509
|
+
# the reactor and ractor pool hops while the connection is hot.
|
|
504
510
|
#
|
|
505
511
|
# @param result [Hash] the parsed result from the ractor pool
|
|
506
512
|
# @param reactor [Reactor] the reactor managing the connection
|
|
@@ -515,31 +521,42 @@ module Raptor
|
|
|
515
521
|
writer = reactor.writer_for(result[:id])
|
|
516
522
|
flow_control = reactor.flow_control_for(result[:id])
|
|
517
523
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
524
|
+
rounds = 0
|
|
525
|
+
loop do
|
|
526
|
+
if flow_control && (result[:window_updates] || result[:peer_initial_window_size])
|
|
527
|
+
apply_flow_control_updates(flow_control, result)
|
|
528
|
+
end
|
|
523
529
|
|
|
524
|
-
|
|
525
|
-
reactor.close_connection(result[:id])
|
|
526
|
-
return
|
|
527
|
-
end
|
|
530
|
+
writer.write_frames(socket, result[:outgoing_frames])
|
|
528
531
|
|
|
529
|
-
|
|
532
|
+
if result[:close_connection]
|
|
533
|
+
reactor.close_connection(result[:id])
|
|
534
|
+
return
|
|
535
|
+
end
|
|
530
536
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
537
|
+
result[:completed_requests]&.each do |request|
|
|
538
|
+
stream_id = request[:stream_id]
|
|
539
|
+
remote_addr = result[:remote_addr] || Server::DEFAULT_REMOTE_ADDR
|
|
534
540
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
+
thread_pool << proc do
|
|
542
|
+
dispatch_stream_request(
|
|
543
|
+
socket, writer, flow_control, stream_id,
|
|
544
|
+
request[:headers], request[:body],
|
|
545
|
+
remote_addr: remote_addr
|
|
546
|
+
)
|
|
547
|
+
end
|
|
541
548
|
end
|
|
549
|
+
|
|
550
|
+
rounds += 1
|
|
551
|
+
break if rounds >= EAGER_MAX_ROUNDS
|
|
552
|
+
|
|
553
|
+
next_batch = eager_read_next_batch(socket)
|
|
554
|
+
break unless next_batch
|
|
555
|
+
|
|
556
|
+
result = Raptor::Http2.process_frames(result.merge(buffer: result[:buffer] + next_batch))
|
|
542
557
|
end
|
|
558
|
+
|
|
559
|
+
reactor.update_http2_state(result)
|
|
543
560
|
end
|
|
544
561
|
|
|
545
562
|
private
|
|
@@ -566,6 +583,32 @@ module Raptor
|
|
|
566
583
|
end
|
|
567
584
|
end
|
|
568
585
|
|
|
586
|
+
# Reads the next frame batch from `socket` within a short window, or
|
|
587
|
+
# returns nil if nothing arrives in time.
|
|
588
|
+
#
|
|
589
|
+
# @param socket [OpenSSL::SSL::SSLSocket] the connection socket
|
|
590
|
+
# @return [String, nil] the bytes read, or nil if nothing was available
|
|
591
|
+
#
|
|
592
|
+
# @rbs (OpenSSL::SSL::SSLSocket socket) -> String?
|
|
593
|
+
def eager_read_next_batch(socket)
|
|
594
|
+
return unless socket.wait_readable(EAGER_READ_TIMEOUT)
|
|
595
|
+
|
|
596
|
+
data = begin
|
|
597
|
+
socket.read_nonblock(EAGER_READ_BUFFER_SIZE)
|
|
598
|
+
rescue IO::WaitReadable, EOFError, IOError
|
|
599
|
+
return
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
buffer = String.new
|
|
603
|
+
buffer << data
|
|
604
|
+
|
|
605
|
+
while socket.pending > 0
|
|
606
|
+
buffer << socket.read_nonblock(socket.pending)
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
buffer
|
|
610
|
+
end
|
|
611
|
+
|
|
569
612
|
# Dispatches a completed stream request to the Rack app and writes
|
|
570
613
|
# the response back as HTTP/2 frames.
|
|
571
614
|
#
|
data/lib/raptor/request.rb
CHANGED
|
@@ -35,7 +35,7 @@ module Raptor
|
|
|
35
35
|
h[status] = "HTTP/1.1 #{status}#{reason ? " #{reason}" : ""}\r\n".freeze
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
STATUS_WITH_NO_ENTITY_BODY =
|
|
38
|
+
STATUS_WITH_NO_ENTITY_BODY = [204, 304, *100..199].freeze
|
|
39
39
|
BAD_REQUEST_RESPONSE = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
|
|
40
40
|
INTERNAL_SERVER_ERROR_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
|
|
41
41
|
CONTENT_TOO_LARGE_RESPONSE = "HTTP/1.1 413 Content Too Large\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
|
|
@@ -947,7 +947,7 @@ module Raptor
|
|
|
947
947
|
def calculate_content_length(body)
|
|
948
948
|
if body.respond_to?(:to_ary)
|
|
949
949
|
array = body.to_ary
|
|
950
|
-
return
|
|
950
|
+
return unless array.is_a?(Array)
|
|
951
951
|
|
|
952
952
|
array.sum { |chunk| chunk.is_a?(String) ? chunk.bytesize : 0 }
|
|
953
953
|
elsif body.respond_to?(:to_path) && (path = body.to_path) && File.readable?(path)
|
data/lib/raptor/version.rb
CHANGED
|
@@ -112,6 +112,12 @@ module Raptor
|
|
|
112
112
|
def discard_stream: (Integer stream_id) -> void
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
+
EAGER_READ_TIMEOUT: ::Float
|
|
116
|
+
|
|
117
|
+
EAGER_READ_BUFFER_SIZE: untyped
|
|
118
|
+
|
|
119
|
+
EAGER_MAX_ROUNDS: ::Integer
|
|
120
|
+
|
|
115
121
|
FLAG_END_STREAM: ::Integer
|
|
116
122
|
|
|
117
123
|
FLAG_END_HEADERS: ::Integer
|
|
@@ -205,7 +211,9 @@ module Raptor
|
|
|
205
211
|
# Handles a parsed HTTP/2 request from the ractor pool.
|
|
206
212
|
#
|
|
207
213
|
# Writes outgoing protocol frames to the socket, updates reactor state,
|
|
208
|
-
# and dispatches completed stream requests to the thread pool.
|
|
214
|
+
# and dispatches completed stream requests to the thread pool. Eagerly
|
|
215
|
+
# consumes subsequent frame batches that are already buffered, skipping
|
|
216
|
+
# the reactor and ractor pool hops while the connection is hot.
|
|
209
217
|
#
|
|
210
218
|
# @param result [Hash] the parsed result from the ractor pool
|
|
211
219
|
# @param reactor [Reactor] the reactor managing the connection
|
|
@@ -227,6 +235,15 @@ module Raptor
|
|
|
227
235
|
# @rbs (FlowControl flow_control, Hash[Symbol, untyped] result) -> void
|
|
228
236
|
def apply_flow_control_updates: (FlowControl flow_control, Hash[Symbol, untyped] result) -> void
|
|
229
237
|
|
|
238
|
+
# Reads the next frame batch from `socket` within a short window, or
|
|
239
|
+
# returns nil if nothing arrives in time.
|
|
240
|
+
#
|
|
241
|
+
# @param socket [OpenSSL::SSL::SSLSocket] the connection socket
|
|
242
|
+
# @return [String, nil] the bytes read, or nil if nothing was available
|
|
243
|
+
#
|
|
244
|
+
# @rbs (OpenSSL::SSL::SSLSocket socket) -> String?
|
|
245
|
+
def eager_read_next_batch: (OpenSSL::SSL::SSLSocket socket) -> String?
|
|
246
|
+
|
|
230
247
|
# Dispatches a completed stream request to the Rack app and writes
|
|
231
248
|
# the response back as HTTP/2 frames.
|
|
232
249
|
#
|