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.
@@ -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 performed synchronously
13
- # before the connection is dispatched.
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
- @binder: Binder
36
+ @running: AtomicBoolean
37
37
 
38
- @reactor: Reactor
38
+ @client_options: Hash[Symbol, untyped]
39
+
40
+ @request: Request
39
41
 
40
42
  @thread_pool: AtomicThreadPool
41
43
 
42
- @request: Request
44
+ @reactor: Reactor
43
45
 
44
- @running: AtomicBoolean
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 connections with h2 negotiated via ALPN, the server sends
84
- # initial SETTINGS and adds the connection to the reactor as an HTTP/2
85
- # connection. All other connections follow the HTTP/1.1 path.
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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: raptor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Young