raptor 0.22.0 → 0.22.2
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 +10 -0
- data/README.md +15 -15
- data/docs/raptor-vs-puma.md +1 -1
- data/lib/raptor/cli.rb +5 -4
- data/lib/raptor/cluster.rb +4 -4
- data/lib/raptor/control_server.rb +36 -17
- data/lib/raptor/detached_body.rb +47 -4
- data/lib/raptor/http.rb +10 -0
- data/lib/raptor/http1.rb +27 -3
- data/lib/raptor/http2.rb +33 -9
- data/lib/raptor/reactor.rb +128 -19
- data/lib/raptor/thread_locals.rb +4 -2
- data/lib/raptor/version.rb +1 -1
- data/sig/generated/raptor/control_server.rbs +17 -5
- data/sig/generated/raptor/detached_body.rbs +45 -1
- data/sig/generated/raptor/http.rbs +8 -0
- data/sig/generated/raptor/http1.rbs +24 -0
- data/sig/generated/raptor/http2.rbs +29 -6
- data/sig/generated/raptor/reactor.rbs +105 -3
- data/sig/generated/raptor/thread_locals.rbs +3 -1
- metadata +1 -1
data/lib/raptor/reactor.rb
CHANGED
|
@@ -6,12 +6,15 @@ require "openssl"
|
|
|
6
6
|
require "red-black-tree"
|
|
7
7
|
require "timeout"
|
|
8
8
|
|
|
9
|
-
require "atomic-ruby/atomic_queue"
|
|
10
9
|
require "atomic-ruby/atom"
|
|
10
|
+
require "atomic-ruby/atomic_boolean"
|
|
11
11
|
require "atomic-ruby/atomic_condition_variable"
|
|
12
|
-
require "atomic-ruby/
|
|
12
|
+
require "atomic-ruby/atomic_queue"
|
|
13
13
|
|
|
14
14
|
require_relative "detached_body"
|
|
15
|
+
require_relative "http1"
|
|
16
|
+
require_relative "http2"
|
|
17
|
+
require_relative "log"
|
|
15
18
|
|
|
16
19
|
module Raptor
|
|
17
20
|
# Multiplexes client connections, manages connection deadlines, feeds
|
|
@@ -82,6 +85,10 @@ module Raptor
|
|
|
82
85
|
# @rbs attr_reader output: Array[String]
|
|
83
86
|
attr_reader :output
|
|
84
87
|
|
|
88
|
+
# Creates empty connection I/O state for reactor-owned writes.
|
|
89
|
+
#
|
|
90
|
+
# @return [void]
|
|
91
|
+
#
|
|
85
92
|
# @rbs () -> void
|
|
86
93
|
def initialize
|
|
87
94
|
@output = []
|
|
@@ -93,6 +100,10 @@ module Raptor
|
|
|
93
100
|
@timeout = nil
|
|
94
101
|
end
|
|
95
102
|
|
|
103
|
+
# Returns whether the connection has no output waiting to be written.
|
|
104
|
+
#
|
|
105
|
+
# @return [Boolean]
|
|
106
|
+
#
|
|
96
107
|
# @rbs () -> bool
|
|
97
108
|
def empty?
|
|
98
109
|
output.empty?
|
|
@@ -117,6 +128,12 @@ module Raptor
|
|
|
117
128
|
# @rbs attr_reader state: Hash[Symbol, untyped]
|
|
118
129
|
attr_reader :state
|
|
119
130
|
|
|
131
|
+
# Creates I/O state for an HTTP/1.x detached response.
|
|
132
|
+
#
|
|
133
|
+
# @param body [DetachedBody] the response body
|
|
134
|
+
# @param state [Hash] connection state restored after the response finishes
|
|
135
|
+
# @return [void]
|
|
136
|
+
#
|
|
120
137
|
# @rbs (DetachedBody body, Hash[Symbol, untyped] state) -> void
|
|
121
138
|
def initialize(body, state)
|
|
122
139
|
super()
|
|
@@ -128,6 +145,10 @@ module Raptor
|
|
|
128
145
|
@finishing = false
|
|
129
146
|
end
|
|
130
147
|
|
|
148
|
+
# Returns whether the connection has no output or detached body remaining.
|
|
149
|
+
#
|
|
150
|
+
# @return [Boolean]
|
|
151
|
+
#
|
|
131
152
|
# @rbs () -> bool
|
|
132
153
|
def empty?
|
|
133
154
|
super && !body
|
|
@@ -149,6 +170,10 @@ module Raptor
|
|
|
149
170
|
# @rbs attr_reader budget: DetachedBody::Budget
|
|
150
171
|
attr_reader :budget
|
|
151
172
|
|
|
173
|
+
# Creates empty I/O state for an HTTP/2 connection and its detached streams.
|
|
174
|
+
#
|
|
175
|
+
# @return [void]
|
|
176
|
+
#
|
|
152
177
|
# @rbs () -> void
|
|
153
178
|
def initialize
|
|
154
179
|
super
|
|
@@ -158,6 +183,10 @@ module Raptor
|
|
|
158
183
|
@budget = DetachedBody::Budget.new(DETACHED_CONNECTION_BUFFER_SIZE)
|
|
159
184
|
end
|
|
160
185
|
|
|
186
|
+
# Returns whether the connection has no output or detached streams remaining.
|
|
187
|
+
#
|
|
188
|
+
# @return [Boolean]
|
|
189
|
+
#
|
|
161
190
|
# @rbs () -> bool
|
|
162
191
|
def empty?
|
|
163
192
|
super && detached.empty?
|
|
@@ -166,8 +195,7 @@ module Raptor
|
|
|
166
195
|
|
|
167
196
|
CHUNK_SIZE = 64 * 1024
|
|
168
197
|
DETACHED_CONNECTION_BUFFER_SIZE = 1024 * 1024
|
|
169
|
-
|
|
170
|
-
DETACHED_MAX_FRAMES = 8
|
|
198
|
+
DETACHED_WRITE_BATCH = 8
|
|
171
199
|
DETACHED_WORKER_BUFFER_SIZE = 16 * 1024 * 1024
|
|
172
200
|
TIMEOUT_RESPONSE = "HTTP/1.1 408 Request Timeout\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
|
|
173
201
|
|
|
@@ -183,6 +211,7 @@ module Raptor
|
|
|
183
211
|
# @rbs @selector: NIO::Selector
|
|
184
212
|
# @rbs @queue: Queue[TCPSocket]
|
|
185
213
|
# @rbs @io_queue: AtomicQueue
|
|
214
|
+
# @rbs @io_applied: AtomicConditionVariable
|
|
186
215
|
# @rbs @timeouts: RedBlackTree[TimeoutClient]
|
|
187
216
|
# @rbs @id_to_socket: Hash[Integer, TCPSocket]
|
|
188
217
|
# @rbs @socket_to_state: Hash[TCPSocket, Hash[Symbol, untyped]]
|
|
@@ -206,6 +235,7 @@ module Raptor
|
|
|
206
235
|
# @param connection_options [Hash] per-connection timeout configuration
|
|
207
236
|
# @option connection_options [Integer] :first_data_timeout timeout for initial data
|
|
208
237
|
# @option connection_options [Integer] :chunk_data_timeout timeout for subsequent chunks
|
|
238
|
+
# @option connection_options [Integer] :write_timeout timeout for non-blocking writes
|
|
209
239
|
# @param http1_options [Hash] HTTP/1.1-specific configuration
|
|
210
240
|
# @option http1_options [Integer] :persistent_data_timeout timeout for keep-alive idle connections
|
|
211
241
|
# @param http2_options [Hash] HTTP/2-specific configuration
|
|
@@ -228,6 +258,7 @@ module Raptor
|
|
|
228
258
|
@selector = NIO::Selector.new
|
|
229
259
|
@queue = Queue.new
|
|
230
260
|
@io_queue = AtomicQueue.new
|
|
261
|
+
@io_applied = AtomicConditionVariable.new
|
|
231
262
|
@timeouts = RedBlackTree.new
|
|
232
263
|
|
|
233
264
|
@id_to_socket = {}
|
|
@@ -409,12 +440,19 @@ module Raptor
|
|
|
409
440
|
|
|
410
441
|
# Attaches a detached response body to an HTTP/1.x connection.
|
|
411
442
|
#
|
|
443
|
+
# @param socket [TCPSocket] the client socket
|
|
444
|
+
# @param id [Integer] unique client identifier
|
|
445
|
+
# @param body [DetachedBody] the response body
|
|
446
|
+
# @param state [Hash] connection state restored once the response finishes
|
|
447
|
+
# @param finished [Proc] called with the close reason
|
|
448
|
+
# @return [Boolean] whether the body was attached
|
|
449
|
+
#
|
|
412
450
|
# @rbs (TCPSocket socket, Integer id, DetachedBody body, Hash[Symbol, untyped] state, ^(Symbol) -> void finished) -> bool
|
|
413
451
|
def attach_http1_body(socket, id, body, state, finished)
|
|
414
|
-
attached =
|
|
452
|
+
attached = AtomicBoolean.new(false)
|
|
415
453
|
@io_queue << [:attach_http1, id, socket, body, attached, finished, state]
|
|
416
454
|
@selector.wakeup rescue nil
|
|
417
|
-
attached.
|
|
455
|
+
@io_applied.wait { attached.true? }
|
|
418
456
|
return false if body.closed?
|
|
419
457
|
|
|
420
458
|
body.open
|
|
@@ -484,12 +522,18 @@ module Raptor
|
|
|
484
522
|
|
|
485
523
|
# Attaches a detached response body to an HTTP/2 stream.
|
|
486
524
|
#
|
|
525
|
+
# @param id [Integer] unique connection identifier
|
|
526
|
+
# @param stream_id [Integer] HTTP/2 stream identifier
|
|
527
|
+
# @param body [DetachedBody] the response body
|
|
528
|
+
# @param finished [Proc] called with the close reason
|
|
529
|
+
# @return [Boolean] whether the body was attached
|
|
530
|
+
#
|
|
487
531
|
# @rbs (Integer id, Integer stream_id, DetachedBody body, ^(Symbol) -> void finished) -> bool
|
|
488
532
|
def attach_http2_body(id, stream_id, body, finished)
|
|
489
|
-
attached =
|
|
533
|
+
attached = AtomicBoolean.new(false)
|
|
490
534
|
@io_queue << [:attach_http2, id, stream_id, body, attached, finished]
|
|
491
535
|
@selector.wakeup rescue nil
|
|
492
|
-
attached.
|
|
536
|
+
@io_applied.wait { attached.true? }
|
|
493
537
|
return false if body.closed?
|
|
494
538
|
|
|
495
539
|
body.open
|
|
@@ -501,6 +545,9 @@ module Raptor
|
|
|
501
545
|
|
|
502
546
|
# Reconsiders detached streams after an outbound window update.
|
|
503
547
|
#
|
|
548
|
+
# @param id [Integer] unique connection identifier
|
|
549
|
+
# @return [void]
|
|
550
|
+
#
|
|
504
551
|
# @rbs (Integer id) -> void
|
|
505
552
|
def resume_http2_bodies(id)
|
|
506
553
|
return if @detached_count.value.zero?
|
|
@@ -511,6 +558,10 @@ module Raptor
|
|
|
511
558
|
|
|
512
559
|
# Cancels a detached response stream.
|
|
513
560
|
#
|
|
561
|
+
# @param id [Integer] unique connection identifier
|
|
562
|
+
# @param stream_id [Integer] HTTP/2 stream identifier
|
|
563
|
+
# @return [void]
|
|
564
|
+
#
|
|
514
565
|
# @rbs (Integer id, Integer stream_id) -> void
|
|
515
566
|
def cancel_http2_body(id, stream_id)
|
|
516
567
|
return if @detached_count.value.zero?
|
|
@@ -522,12 +573,15 @@ module Raptor
|
|
|
522
573
|
# Waits for detached responses to finish, then cancels any that outlive
|
|
523
574
|
# the worker drain period.
|
|
524
575
|
#
|
|
576
|
+
# @param timeout [Numeric] seconds to wait before cancelling open bodies
|
|
577
|
+
# @return [void]
|
|
578
|
+
#
|
|
525
579
|
# @rbs (Numeric timeout) -> void
|
|
526
580
|
def drain_detached_bodies(timeout)
|
|
527
|
-
barrier =
|
|
581
|
+
barrier = AtomicBoolean.new(false)
|
|
528
582
|
@io_queue << [:barrier, nil, barrier]
|
|
529
583
|
@selector.wakeup rescue nil
|
|
530
|
-
barrier.
|
|
584
|
+
@io_applied.wait { barrier.true? }
|
|
531
585
|
|
|
532
586
|
Timeout.timeout(timeout) do
|
|
533
587
|
@detached_drained.wait { @detached_count.value.zero? }
|
|
@@ -560,7 +614,7 @@ module Raptor
|
|
|
560
614
|
@id_to_io[id] = Http2IO.new
|
|
561
615
|
writer.attach(self, id)
|
|
562
616
|
if @http2_keepalive_interval.positive?
|
|
563
|
-
@id_to_http2_keepalive[id] = {frame: ping_frame, payload: ping_payload}
|
|
617
|
+
@id_to_http2_keepalive[id] = { frame: ping_frame, payload: ping_payload }
|
|
564
618
|
end
|
|
565
619
|
end
|
|
566
620
|
|
|
@@ -696,14 +750,16 @@ module Raptor
|
|
|
696
750
|
begin
|
|
697
751
|
attach_http2_detached_body(id, value, body, finished)
|
|
698
752
|
ensure
|
|
699
|
-
attached.
|
|
753
|
+
attached.make_true
|
|
754
|
+
@io_applied.broadcast
|
|
700
755
|
end
|
|
701
756
|
connections[id] = true
|
|
702
757
|
when :attach_http1
|
|
703
758
|
begin
|
|
704
759
|
attach_http1_detached_body(value, id, body, state, finished)
|
|
705
760
|
ensure
|
|
706
|
-
attached.
|
|
761
|
+
attached.make_true
|
|
762
|
+
@io_applied.broadcast
|
|
707
763
|
end
|
|
708
764
|
when :ready_http1
|
|
709
765
|
flush_http1_detached_body(id)
|
|
@@ -720,7 +776,8 @@ module Raptor
|
|
|
720
776
|
when :cancel_all
|
|
721
777
|
cancel_detached_bodies
|
|
722
778
|
when :barrier
|
|
723
|
-
value.
|
|
779
|
+
value.make_true
|
|
780
|
+
@io_applied.broadcast
|
|
724
781
|
end
|
|
725
782
|
end
|
|
726
783
|
|
|
@@ -733,6 +790,13 @@ module Raptor
|
|
|
733
790
|
|
|
734
791
|
# Adds a detached body to an HTTP/1.x connection.
|
|
735
792
|
#
|
|
793
|
+
# @param socket [TCPSocket] the client socket
|
|
794
|
+
# @param id [Integer] unique client identifier
|
|
795
|
+
# @param body [DetachedBody] the response body
|
|
796
|
+
# @param state [Hash] connection state restored once the response finishes
|
|
797
|
+
# @param finished [Proc] called with the close reason
|
|
798
|
+
# @return [void]
|
|
799
|
+
#
|
|
736
800
|
# @rbs (TCPSocket socket, Integer id, DetachedBody body, Hash[Symbol, untyped] state, ^(Symbol) -> void finished) -> void
|
|
737
801
|
def attach_http1_detached_body(socket, id, body, state, finished)
|
|
738
802
|
io = Http1IO.new(body, state)
|
|
@@ -747,7 +811,7 @@ module Raptor
|
|
|
747
811
|
end
|
|
748
812
|
|
|
749
813
|
@id_to_socket[id] = socket
|
|
750
|
-
@socket_to_state[socket] = {id: id, protocol: :http1, detached: true}
|
|
814
|
+
@socket_to_state[socket] = { id: id, protocol: :http1, detached: true }
|
|
751
815
|
@id_to_io[id] = io
|
|
752
816
|
@detached_count.swap { |count| count + 1 }
|
|
753
817
|
update_monitor(id, io)
|
|
@@ -755,12 +819,15 @@ module Raptor
|
|
|
755
819
|
|
|
756
820
|
# Writes buffered HTTP/1.x body chunks without blocking.
|
|
757
821
|
#
|
|
822
|
+
# @param id [Integer] unique client identifier
|
|
823
|
+
# @return [void]
|
|
824
|
+
#
|
|
758
825
|
# @rbs (Integer id) -> void
|
|
759
826
|
def flush_http1_detached_body(id)
|
|
760
827
|
io = @id_to_io[id]
|
|
761
828
|
return unless io.is_a?(Http1IO) && !io.wait && io.output.empty?
|
|
762
829
|
|
|
763
|
-
|
|
830
|
+
DETACHED_WRITE_BATCH.times do
|
|
764
831
|
size = io.body&.next_size
|
|
765
832
|
break unless size
|
|
766
833
|
|
|
@@ -785,6 +852,10 @@ module Raptor
|
|
|
785
852
|
|
|
786
853
|
# Finishes an HTTP/1.x detached response and either reuses or closes its connection.
|
|
787
854
|
#
|
|
855
|
+
# @param id [Integer] unique client identifier
|
|
856
|
+
# @param io [Http1IO] connection I/O state
|
|
857
|
+
# @return [void]
|
|
858
|
+
#
|
|
788
859
|
# @rbs (Integer id, Http1IO io) -> void
|
|
789
860
|
def finish_http1_detached_body(id, io)
|
|
790
861
|
socket = @id_to_socket[id]
|
|
@@ -804,7 +875,7 @@ module Raptor
|
|
|
804
875
|
request_count: io.state[:request_count],
|
|
805
876
|
remote_addr: io.state[:remote_addr],
|
|
806
877
|
url_scheme: io.state[:url_scheme],
|
|
807
|
-
persisted: true
|
|
878
|
+
persisted: true
|
|
808
879
|
}
|
|
809
880
|
unless io.input.empty?
|
|
810
881
|
state[:buffer] = io.input
|
|
@@ -823,6 +894,10 @@ module Raptor
|
|
|
823
894
|
|
|
824
895
|
# Cancels an HTTP/1.x detached response and closes its connection.
|
|
825
896
|
#
|
|
897
|
+
# @param id [Integer] unique client identifier
|
|
898
|
+
# @param reason [Symbol] why the response closed
|
|
899
|
+
# @return [void]
|
|
900
|
+
#
|
|
826
901
|
# @rbs (Integer id, Symbol reason) -> void
|
|
827
902
|
def remove_http1_detached_body(id, reason)
|
|
828
903
|
io = @id_to_io[id]
|
|
@@ -837,6 +912,12 @@ module Raptor
|
|
|
837
912
|
|
|
838
913
|
# Adds a detached body to an HTTP/2 stream.
|
|
839
914
|
#
|
|
915
|
+
# @param id [Integer] unique connection identifier
|
|
916
|
+
# @param stream_id [Integer] HTTP/2 stream identifier
|
|
917
|
+
# @param body [DetachedBody] the response body
|
|
918
|
+
# @param finished [Proc] called with the close reason
|
|
919
|
+
# @return [void]
|
|
920
|
+
#
|
|
840
921
|
# @rbs (Integer id, Integer stream_id, DetachedBody body, ^(Symbol) -> void finished) -> void
|
|
841
922
|
def attach_http2_detached_body(id, stream_id, body, finished)
|
|
842
923
|
io = @id_to_io[id]
|
|
@@ -859,6 +940,10 @@ module Raptor
|
|
|
859
940
|
|
|
860
941
|
# Marks an HTTP/2 detached stream eligible for fair scheduling.
|
|
861
942
|
#
|
|
943
|
+
# @param id [Integer] unique connection identifier
|
|
944
|
+
# @param stream_id [Integer] HTTP/2 stream identifier
|
|
945
|
+
# @return [void]
|
|
946
|
+
#
|
|
862
947
|
# @rbs (Integer id, Integer stream_id) -> void
|
|
863
948
|
def mark_http2_detached_body_ready(id, stream_id)
|
|
864
949
|
io = @id_to_io[id]
|
|
@@ -870,6 +955,9 @@ module Raptor
|
|
|
870
955
|
|
|
871
956
|
# Marks every HTTP/2 detached stream eligible after flow-control capacity changes.
|
|
872
957
|
#
|
|
958
|
+
# @param id [Integer] unique connection identifier
|
|
959
|
+
# @return [void]
|
|
960
|
+
#
|
|
873
961
|
# @rbs (Integer id) -> void
|
|
874
962
|
def resume_http2_detached_bodies(id)
|
|
875
963
|
io = @id_to_io[id]
|
|
@@ -880,6 +968,9 @@ module Raptor
|
|
|
880
968
|
|
|
881
969
|
# Writes HTTP/2 detached streams in round-robin order without waiting for flow control.
|
|
882
970
|
#
|
|
971
|
+
# @param id [Integer] unique connection identifier
|
|
972
|
+
# @return [void]
|
|
973
|
+
#
|
|
883
974
|
# @rbs (Integer id) -> void
|
|
884
975
|
def flush_http2_detached_bodies(id)
|
|
885
976
|
io = @id_to_io[id]
|
|
@@ -887,7 +978,7 @@ module Raptor
|
|
|
887
978
|
socket = @id_to_socket[id]
|
|
888
979
|
return unless io && flow_control && socket
|
|
889
980
|
|
|
890
|
-
|
|
981
|
+
DETACHED_WRITE_BATCH.times do
|
|
891
982
|
break if io.wait || !io.output.empty?
|
|
892
983
|
|
|
893
984
|
stream_id = io.ready.shift
|
|
@@ -925,6 +1016,11 @@ module Raptor
|
|
|
925
1016
|
|
|
926
1017
|
# Finishes an HTTP/2 detached stream with DATA or trailing HEADERS.
|
|
927
1018
|
#
|
|
1019
|
+
# @param id [Integer] unique connection identifier
|
|
1020
|
+
# @param stream_id [Integer] HTTP/2 stream identifier
|
|
1021
|
+
# @param body [DetachedBody] the response body
|
|
1022
|
+
# @return [void]
|
|
1023
|
+
#
|
|
928
1024
|
# @rbs (Integer id, Integer stream_id, DetachedBody body) -> void
|
|
929
1025
|
def finish_http2_detached_body(id, stream_id, body)
|
|
930
1026
|
parser = Http2Parser.new
|
|
@@ -942,6 +1038,11 @@ module Raptor
|
|
|
942
1038
|
|
|
943
1039
|
# Removes an HTTP/2 detached stream and schedules its close callback.
|
|
944
1040
|
#
|
|
1041
|
+
# @param id [Integer] unique connection identifier
|
|
1042
|
+
# @param stream_id [Integer] HTTP/2 stream identifier
|
|
1043
|
+
# @param reason [Symbol] why the stream closed
|
|
1044
|
+
# @return [void]
|
|
1045
|
+
#
|
|
945
1046
|
# @rbs (Integer id, Integer stream_id, Symbol reason) -> void
|
|
946
1047
|
def remove_http2_detached_body(id, stream_id, reason)
|
|
947
1048
|
io = @id_to_io[id]
|
|
@@ -955,6 +1056,8 @@ module Raptor
|
|
|
955
1056
|
|
|
956
1057
|
# Cancels every detached response during worker shutdown.
|
|
957
1058
|
#
|
|
1059
|
+
# @return [void]
|
|
1060
|
+
#
|
|
958
1061
|
# @rbs () -> void
|
|
959
1062
|
def cancel_detached_bodies
|
|
960
1063
|
@id_to_io.to_a.each do |id, io|
|
|
@@ -970,6 +1073,8 @@ module Raptor
|
|
|
970
1073
|
|
|
971
1074
|
# Records one detached stream finishing.
|
|
972
1075
|
#
|
|
1076
|
+
# @return [void]
|
|
1077
|
+
#
|
|
973
1078
|
# @rbs () -> void
|
|
974
1079
|
def detached_body_removed
|
|
975
1080
|
remaining = nil
|
|
@@ -1127,7 +1232,7 @@ module Raptor
|
|
|
1127
1232
|
# @rbs (Integer id, ConnectionIO io) -> void
|
|
1128
1233
|
def track_write_timeout(id, io)
|
|
1129
1234
|
clear_write_timeout(io)
|
|
1130
|
-
client = TimeoutClient.new({id: id, write: true})
|
|
1235
|
+
client = TimeoutClient.new({ id: id, write: true })
|
|
1131
1236
|
client.timeout_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @write_timeout
|
|
1132
1237
|
@timeouts << client
|
|
1133
1238
|
io.timeout = client
|
|
@@ -1281,6 +1386,10 @@ module Raptor
|
|
|
1281
1386
|
|
|
1282
1387
|
# Handles readiness for an HTTP/1.x detached response.
|
|
1283
1388
|
#
|
|
1389
|
+
# @param monitor [NIO::Monitor] ready selector monitor
|
|
1390
|
+
# @param id [Integer] unique client identifier
|
|
1391
|
+
# @return [void]
|
|
1392
|
+
#
|
|
1284
1393
|
# @rbs (NIO::Monitor monitor, Integer id) -> void
|
|
1285
1394
|
def handle_http1_detached_monitor(monitor, id)
|
|
1286
1395
|
socket = monitor.value
|
data/lib/raptor/thread_locals.rb
CHANGED
|
@@ -9,12 +9,14 @@ module Raptor
|
|
|
9
9
|
PRESERVED_KEYS = [
|
|
10
10
|
:raptor_http_parser,
|
|
11
11
|
:raptor_read_buffer,
|
|
12
|
-
:raptor_response_buffer
|
|
12
|
+
:raptor_response_buffer
|
|
13
13
|
].freeze
|
|
14
14
|
|
|
15
15
|
# Returns a reusable value stored on the current thread.
|
|
16
16
|
#
|
|
17
|
-
# @
|
|
17
|
+
# @param key [Symbol] thread-variable key used to cache the value
|
|
18
|
+
# @yieldreturn [Object] value to store when the key is unset
|
|
19
|
+
# @return [Object] the existing or newly stored value
|
|
18
20
|
#
|
|
19
21
|
# @rbs (Symbol key) { () -> untyped } -> untyped
|
|
20
22
|
def self.fetch(key)
|
data/lib/raptor/version.rb
CHANGED
|
@@ -3,20 +3,18 @@
|
|
|
3
3
|
module Raptor
|
|
4
4
|
# Serves cluster statistics over a Unix socket.
|
|
5
5
|
class ControlServer
|
|
6
|
+
SHUTDOWN: ::Symbol
|
|
7
|
+
|
|
6
8
|
@path: String
|
|
7
9
|
|
|
8
10
|
@stats: ^() -> Hash[Symbol, untyped]
|
|
9
11
|
|
|
10
12
|
@server: UNIXServer?
|
|
11
13
|
|
|
12
|
-
@client:
|
|
14
|
+
@client: Atom
|
|
13
15
|
|
|
14
16
|
@thread: Thread?
|
|
15
17
|
|
|
16
|
-
@running: bool
|
|
17
|
-
|
|
18
|
-
@mutex: Mutex
|
|
19
|
-
|
|
20
18
|
# Creates a control server for `url` without binding it.
|
|
21
19
|
#
|
|
22
20
|
# @param url [String] `unix://` URL to listen on
|
|
@@ -50,12 +48,26 @@ module Raptor
|
|
|
50
48
|
|
|
51
49
|
private
|
|
52
50
|
|
|
51
|
+
# Removes a stale socket while refusing to replace an active server.
|
|
52
|
+
#
|
|
53
|
+
# @return [void]
|
|
54
|
+
# @raise [RuntimeError] if another server is listening on the socket
|
|
55
|
+
#
|
|
53
56
|
# @rbs () -> void
|
|
54
57
|
def remove_stale_socket: () -> void
|
|
55
58
|
|
|
59
|
+
# Accepts and handles control requests until shutdown begins.
|
|
60
|
+
#
|
|
61
|
+
# @return [void]
|
|
62
|
+
#
|
|
56
63
|
# @rbs () -> void
|
|
57
64
|
def serve: () -> void
|
|
58
65
|
|
|
66
|
+
# Writes the response for one control-socket request.
|
|
67
|
+
#
|
|
68
|
+
# @param client [UNIXSocket] connected control client
|
|
69
|
+
# @return [void]
|
|
70
|
+
#
|
|
59
71
|
# @rbs (UNIXSocket client) -> void
|
|
60
72
|
def handle: (UNIXSocket client) -> void
|
|
61
73
|
end
|
|
@@ -12,12 +12,27 @@ module Raptor
|
|
|
12
12
|
|
|
13
13
|
@size: Atom
|
|
14
14
|
|
|
15
|
+
# Creates a shared budget with the given byte limit.
|
|
16
|
+
#
|
|
17
|
+
# @param max_size [Integer] maximum bytes that may be reserved
|
|
18
|
+
# @return [void]
|
|
19
|
+
#
|
|
15
20
|
# @rbs (Integer max_size) -> void
|
|
16
21
|
def initialize: (Integer max_size) -> void
|
|
17
22
|
|
|
23
|
+
# Reserves bytes when they fit within the shared limit.
|
|
24
|
+
#
|
|
25
|
+
# @param bytes [Integer] number of bytes to reserve
|
|
26
|
+
# @return [Boolean] whether the bytes were reserved
|
|
27
|
+
#
|
|
18
28
|
# @rbs (Integer bytes) -> bool
|
|
19
29
|
def reserve: (Integer bytes) -> bool
|
|
20
30
|
|
|
31
|
+
# Releases bytes previously charged to the shared limit.
|
|
32
|
+
#
|
|
33
|
+
# @param bytes [Integer] number of bytes to release
|
|
34
|
+
# @return [void]
|
|
35
|
+
#
|
|
21
36
|
# @rbs (Integer bytes) -> void
|
|
22
37
|
def release: (Integer bytes) -> void
|
|
23
38
|
end
|
|
@@ -46,7 +61,8 @@ module Raptor
|
|
|
46
61
|
# @rbs (?max_buffer_size: Integer) -> void
|
|
47
62
|
def initialize: (?max_buffer_size: Integer) -> void
|
|
48
63
|
|
|
49
|
-
# Registers a callback
|
|
64
|
+
# Registers a callback that runs on the request thread once the server
|
|
65
|
+
# accepts the response stream.
|
|
50
66
|
#
|
|
51
67
|
# @yieldparam stream [DetachedBody] the opened response stream
|
|
52
68
|
# @return [DetachedBody]
|
|
@@ -87,40 +103,68 @@ module Raptor
|
|
|
87
103
|
|
|
88
104
|
# Connects the body to its reactor-owned response stream.
|
|
89
105
|
#
|
|
106
|
+
# @param budgets [Array<Budget>] buffer budgets shared with other bodies
|
|
107
|
+
# @param wake [Proc] called when buffered data or a close is ready to write
|
|
108
|
+
# @param dispatch [Proc] schedules application callbacks off the reactor thread
|
|
109
|
+
# @param finished [Proc] called with the close reason after `on_close`
|
|
110
|
+
# @return [Boolean] false when the bytes already buffered exceed a budget
|
|
111
|
+
#
|
|
90
112
|
# @rbs (Array[Budget] budgets, ^() -> void wake, ^(Proc) -> void dispatch, ^(Symbol) -> void finished) -> bool
|
|
91
113
|
def attach: (Array[Budget] budgets, ^() -> void wake, ^(Proc) -> void dispatch, ^(Symbol) -> void finished) -> bool
|
|
92
114
|
|
|
93
115
|
# Notifies the application that its response stream is ready.
|
|
94
116
|
#
|
|
117
|
+
# @return [void]
|
|
118
|
+
#
|
|
95
119
|
# @rbs () -> void
|
|
96
120
|
def open: () -> void
|
|
97
121
|
|
|
98
122
|
# Returns the size of the next buffered chunk, 0 when closing, or nil
|
|
99
123
|
# while waiting for more data.
|
|
100
124
|
#
|
|
125
|
+
# @return [Integer, nil]
|
|
126
|
+
#
|
|
101
127
|
# @rbs () -> Integer?
|
|
102
128
|
def next_size: () -> Integer?
|
|
103
129
|
|
|
104
130
|
# Removes up to `max_bytes` from the next buffered chunk.
|
|
105
131
|
#
|
|
132
|
+
# @param max_bytes [Integer] the largest chunk to return
|
|
133
|
+
# @return [String, nil] the removed bytes, or nil when nothing is buffered
|
|
134
|
+
#
|
|
106
135
|
# @rbs (Integer max_bytes) -> String?
|
|
107
136
|
def shift: (Integer max_bytes) -> String?
|
|
108
137
|
|
|
109
138
|
# Returns the response trailers supplied when the body closed.
|
|
110
139
|
#
|
|
140
|
+
# @return [Hash] trailing response headers
|
|
141
|
+
#
|
|
111
142
|
# @rbs () -> Hash[String, String | Array[String]]
|
|
112
143
|
def trailers: () -> Hash[String, String | Array[String]]
|
|
113
144
|
|
|
114
145
|
# Closes the stream and invokes its callback exactly once.
|
|
115
146
|
#
|
|
147
|
+
# @param reason [Symbol] why the stream closed
|
|
148
|
+
# @return [void]
|
|
149
|
+
#
|
|
116
150
|
# @rbs (Symbol reason) -> void
|
|
117
151
|
def finish: (Symbol reason) -> void
|
|
118
152
|
|
|
119
153
|
private
|
|
120
154
|
|
|
155
|
+
# Reserves bytes from every budget attached to this body.
|
|
156
|
+
#
|
|
157
|
+
# @param bytes [Integer] number of bytes to reserve
|
|
158
|
+
# @return [Boolean] whether every budget accepted the reservation
|
|
159
|
+
#
|
|
121
160
|
# @rbs (Integer bytes) -> bool
|
|
122
161
|
def reserve: (Integer bytes) -> bool
|
|
123
162
|
|
|
163
|
+
# Releases bytes from every budget attached to this body.
|
|
164
|
+
#
|
|
165
|
+
# @param bytes [Integer] number of bytes to release
|
|
166
|
+
# @return [void]
|
|
167
|
+
#
|
|
124
168
|
# @rbs (Integer bytes) -> void
|
|
125
169
|
def release: (Integer bytes) -> void
|
|
126
170
|
end
|
|
@@ -26,6 +26,14 @@ module Raptor
|
|
|
26
26
|
def message: () -> String
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# Returns whether an HTTP status forbids an entity body.
|
|
30
|
+
#
|
|
31
|
+
# @param status [Integer] the response status code
|
|
32
|
+
# @return [Boolean]
|
|
33
|
+
#
|
|
34
|
+
# @rbs (Integer status) -> bool
|
|
35
|
+
def self.no_entity_body_status?: (Integer status) -> bool
|
|
36
|
+
|
|
29
37
|
# Writes `string` in full, retrying on partial writes. Bounded by
|
|
30
38
|
# `timeout` so a slow client can't pin the writing thread.
|
|
31
39
|
#
|
|
@@ -80,11 +80,17 @@ module Raptor
|
|
|
80
80
|
|
|
81
81
|
# Encodes one HTTP/1.1 response chunk.
|
|
82
82
|
#
|
|
83
|
+
# @param chunk [String] response body bytes
|
|
84
|
+
# @return [String] the encoded chunk
|
|
85
|
+
#
|
|
83
86
|
# @rbs (String chunk) -> String
|
|
84
87
|
def self.encode_chunk: (String chunk) -> String
|
|
85
88
|
|
|
86
89
|
# Encodes the final HTTP/1.1 response chunk and its trailers.
|
|
87
90
|
#
|
|
91
|
+
# @param trailers [Hash] trailing response headers
|
|
92
|
+
# @return [String] the final chunk followed by the trailer section
|
|
93
|
+
#
|
|
88
94
|
# @rbs (Hash[String, String | Array[String]] trailers) -> String
|
|
89
95
|
def self.encode_trailers: (Hash[String, String | Array[String]] trailers) -> String
|
|
90
96
|
|
|
@@ -307,6 +313,17 @@ module Raptor
|
|
|
307
313
|
|
|
308
314
|
# Calls the Rack app and writes its response for one request.
|
|
309
315
|
#
|
|
316
|
+
# @param socket [TCPSocket] the client socket
|
|
317
|
+
# @param id [Integer] unique client identifier
|
|
318
|
+
# @param env [Hash] partial env hash from the HTTP parser
|
|
319
|
+
# @param parse_data [Hash] metadata from the parsing pass
|
|
320
|
+
# @param body [String, nil] decoded request body
|
|
321
|
+
# @param reactor [Reactor] the reactor managing the client connection
|
|
322
|
+
# @param request_count [Integer] number of requests handled on this connection
|
|
323
|
+
# @param remote_addr [String] client IP address
|
|
324
|
+
# @param url_scheme [String] "http" or "https"
|
|
325
|
+
# @return [Boolean] true if the connection should be kept alive
|
|
326
|
+
#
|
|
310
327
|
# @rbs (TCPSocket socket, Integer id, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, Reactor reactor, Integer request_count, String remote_addr, String url_scheme) -> bool
|
|
311
328
|
def perform_request: (TCPSocket socket, Integer id, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, Reactor reactor, Integer request_count, String remote_addr, String url_scheme) -> bool
|
|
312
329
|
|
|
@@ -434,6 +451,13 @@ module Raptor
|
|
|
434
451
|
# Starts a detached response, chunked on HTTP/1.1 and ended by closing
|
|
435
452
|
# the connection on HTTP/1.0.
|
|
436
453
|
#
|
|
454
|
+
# @param socket [TCPSocket] the client socket to write to
|
|
455
|
+
# @param status [Integer] HTTP status code
|
|
456
|
+
# @param headers [Hash] response headers from the Rack application
|
|
457
|
+
# @param chunked [Boolean] whether to use chunked transfer encoding
|
|
458
|
+
# @param keep_alive [Boolean] whether to send a keep-alive connection header
|
|
459
|
+
# @return [void]
|
|
460
|
+
#
|
|
437
461
|
# @rbs (TCPSocket socket, Integer status, Hash[String, String | Array[String]] headers, chunked: bool, keep_alive: bool) -> void
|
|
438
462
|
def write_detached_response: (TCPSocket socket, Integer status, Hash[String, String | Array[String]] headers, chunked: bool, keep_alive: bool) -> void
|
|
439
463
|
|