raptor 0.20.2 → 0.22.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.
data/lib/raptor/server.rb CHANGED
@@ -182,6 +182,18 @@ module Raptor
182
182
  return true
183
183
  end
184
184
 
185
+ if listener.is_a?(Binder::H2cListener)
186
+ @http2.eager_accept(
187
+ tcp_client,
188
+ tcp_client.object_id,
189
+ @reactor,
190
+ @thread_pool,
191
+ remote_addr,
192
+ HTTP_SCHEME
193
+ )
194
+ return true
195
+ end
196
+
185
197
  @http1.eager_accept(
186
198
  tcp_client,
187
199
  tcp_client.object_id,
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Raptor
5
- VERSION = "0.20.2"
5
+ VERSION = "0.22.0"
6
6
  end
data/lib/raptor.rb CHANGED
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require_relative "raptor/version"
5
+ require_relative "raptor/detached_body"
5
6
 
6
7
  # Main module for the Raptor web server.
7
8
  #
@@ -1,9 +1,9 @@
1
1
  # Generated from lib/raptor/binder.rb with RBS::Inline
2
2
 
3
3
  module Raptor
4
- # Binds `tcp://`, `unix://`, and `ssl://` URIs to listening sockets and
5
- # holds them for the server. Reconstructs listeners from inherited file
6
- # descriptors when provided (systemd socket activation, hot restart).
4
+ # Binds TCP, Unix, SSL, and cleartext HTTP/2 URIs to listening sockets
5
+ # and holds them for the server. Reconstructs listeners from inherited
6
+ # file descriptors when provided (systemd socket activation, hot restart).
7
7
  class Binder
8
8
  SOCKET_BACKLOG: ::Integer
9
9
 
@@ -27,9 +27,21 @@ module Raptor
27
27
  def members: () -> [ :tcp_server, :ssl_context ]
28
28
  end
29
29
 
30
- @uri_listeners: Hash[String, Array[TCPServer | UNIXServer | SslListener]]
30
+ # Marks a TCPServer as accepting cleartext HTTP/2 connections.
31
+ class H2cListener < Data
32
+ attr_reader tcp_server(): untyped
33
+
34
+ def self.new: (untyped tcp_server) -> instance
35
+ | (tcp_server: untyped) -> instance
36
+
37
+ def self.members: () -> [ :tcp_server ]
38
+
39
+ def members: () -> [ :tcp_server ]
40
+ end
41
+
42
+ @uri_listeners: Hash[String, Array[TCPServer | UNIXServer | SslListener | H2cListener]]
31
43
 
32
- @listeners: Array[TCPServer | UNIXServer | SslListener]
44
+ @listeners: Array[TCPServer | UNIXServer | SslListener | H2cListener]
33
45
 
34
46
  @inherited_fds: Hash[String, Array[Integer]]
35
47
 
@@ -49,7 +61,7 @@ module Raptor
49
61
 
50
62
  # Returns the array of listening sockets.
51
63
  #
52
- # @return [Array<TCPServer, UNIXServer, SslListener>]
64
+ # @return [Array<TCPServer, UNIXServer, SslListener, H2cListener>]
53
65
  attr_reader listeners: untyped
54
66
 
55
67
  # Creates a new Binder and binds each URI. `localhost` expands to both
@@ -66,7 +78,8 @@ module Raptor
66
78
  def initialize: (Array[String] bind_uris, ?socket_backlog: Integer, ?inherited_fds: Hash[String, Array[Integer]]) -> void
67
79
 
68
80
  # Returns the bound addresses as strings: TCP as `host:port`, Unix as
69
- # the socket path, SSL as `ssl://host:port`.
81
+ # the socket path, SSL as `ssl://host:port`, and cleartext HTTP/2 as
82
+ # `h2c://host:port`.
70
83
  #
71
84
  # @return [Array<String>]
72
85
  #
@@ -124,22 +137,22 @@ module Raptor
124
137
  # Creates fresh listeners for the given URI.
125
138
  #
126
139
  # @param uri [URI] the parsed bind URI
127
- # @return [Array<TCPServer, UNIXServer, SslListener>]
140
+ # @return [Array<TCPServer, UNIXServer, SslListener, H2cListener>]
128
141
  # @raise [UnknownBindSchemeError] if the URI scheme is not supported
129
142
  #
130
- # @rbs (URI::Generic uri) -> Array[TCPServer | UNIXServer | SslListener]
131
- def create_listeners: (URI::Generic uri) -> Array[TCPServer | UNIXServer | SslListener]
143
+ # @rbs (URI::Generic uri) -> Array[TCPServer | UNIXServer | SslListener | H2cListener]
144
+ def create_listeners: (URI::Generic uri) -> Array[TCPServer | UNIXServer | SslListener | H2cListener]
132
145
 
133
146
  # Reconstructs listeners for the given URI from inherited file
134
147
  # descriptors.
135
148
  #
136
149
  # @param uri [URI] the parsed bind URI the FDs were bound to
137
150
  # @param filenos [Array<Integer>] file descriptors to wrap
138
- # @return [Array<TCPServer, UNIXServer, SslListener>]
151
+ # @return [Array<TCPServer, UNIXServer, SslListener, H2cListener>]
139
152
  # @raise [UnknownBindSchemeError] if the URI scheme is not supported
140
153
  #
141
- # @rbs (URI::Generic uri, Array[Integer] filenos) -> Array[TCPServer | UNIXServer | SslListener]
142
- def restore_listeners: (URI::Generic uri, Array[Integer] filenos) -> Array[TCPServer | UNIXServer | SslListener]
154
+ # @rbs (URI::Generic uri, Array[Integer] filenos) -> Array[TCPServer | UNIXServer | SslListener | H2cListener]
155
+ def restore_listeners: (URI::Generic uri, Array[Integer] filenos) -> Array[TCPServer | UNIXServer | SslListener | H2cListener]
143
156
 
144
157
  # Creates TCP server sockets for the given host and port.
145
158
  #
@@ -0,0 +1,127 @@
1
+ # Generated from lib/raptor/detached_body.rb with RBS::Inline
2
+
3
+ module Raptor
4
+ # A bounded response body that may be written after its application thread
5
+ # has returned.
6
+ class DetachedBody
7
+ DEFAULT_MAX_BUFFER_SIZE: untyped
8
+
9
+ # Tracks buffered bytes shared by a group of detached bodies.
10
+ class Budget
11
+ @max_size: Integer
12
+
13
+ @size: Atom
14
+
15
+ # @rbs (Integer max_size) -> void
16
+ def initialize: (Integer max_size) -> void
17
+
18
+ # @rbs (Integer bytes) -> bool
19
+ def reserve: (Integer bytes) -> bool
20
+
21
+ # @rbs (Integer bytes) -> void
22
+ def release: (Integer bytes) -> void
23
+ end
24
+
25
+ @on_close: (^(Symbol) -> void)?
26
+
27
+ @on_open: (^(DetachedBody) -> void)?
28
+
29
+ @finished: ^(Symbol) -> void
30
+
31
+ @dispatch: ^(Proc) -> void
32
+
33
+ @wake: ^() -> void
34
+
35
+ @budgets: Array[Budget]
36
+
37
+ @state: Atom
38
+
39
+ @max_buffer_size: Integer
40
+
41
+ # Creates a detached body with a bounded byte buffer.
42
+ #
43
+ # @param max_buffer_size [Integer] maximum body bytes waiting to be written
44
+ # @return [void]
45
+ #
46
+ # @rbs (?max_buffer_size: Integer) -> void
47
+ def initialize: (?max_buffer_size: Integer) -> void
48
+
49
+ # Registers a callback for when the server accepts the response stream.
50
+ #
51
+ # @yieldparam stream [DetachedBody] the opened response stream
52
+ # @return [DetachedBody]
53
+ #
54
+ # @rbs () { (DetachedBody stream) -> void } -> DetachedBody
55
+ def on_open: () { (DetachedBody stream) -> void } -> DetachedBody
56
+
57
+ # Registers a callback for when the response stream closes.
58
+ #
59
+ # @yieldparam reason [Symbol] why the stream closed
60
+ # @return [DetachedBody]
61
+ #
62
+ # @rbs () { (Symbol reason) -> void } -> DetachedBody
63
+ def on_close: () { (Symbol reason) -> void } -> DetachedBody
64
+
65
+ # Adds body bytes without waiting for socket or flow-control capacity.
66
+ #
67
+ # @param chunk [String] response body bytes
68
+ # @return [Symbol] `:accepted`, `:full`, or `:closed`
69
+ #
70
+ # @rbs (String chunk) -> Symbol
71
+ def try_write: (String chunk) -> Symbol
72
+
73
+ # Finishes the body after all accepted bytes have been written.
74
+ #
75
+ # @param trailers [Hash] trailing response headers
76
+ # @return [void]
77
+ #
78
+ # @rbs (?trailers: Hash[String, String | Array[String]]) -> void
79
+ def close: (?trailers: Hash[String, String | Array[String]]) -> void
80
+
81
+ # Returns whether the stream has closed.
82
+ #
83
+ # @return [Boolean]
84
+ #
85
+ # @rbs () -> bool
86
+ def closed?: () -> bool
87
+
88
+ # Connects the body to its reactor-owned response stream.
89
+ #
90
+ # @rbs (Array[Budget] budgets, ^() -> void wake, ^(Proc) -> void dispatch, ^(Symbol) -> void finished) -> bool
91
+ def attach: (Array[Budget] budgets, ^() -> void wake, ^(Proc) -> void dispatch, ^(Symbol) -> void finished) -> bool
92
+
93
+ # Notifies the application that its response stream is ready.
94
+ #
95
+ # @rbs () -> void
96
+ def open: () -> void
97
+
98
+ # Returns the size of the next buffered chunk, 0 when closing, or nil
99
+ # while waiting for more data.
100
+ #
101
+ # @rbs () -> Integer?
102
+ def next_size: () -> Integer?
103
+
104
+ # Removes up to `max_bytes` from the next buffered chunk.
105
+ #
106
+ # @rbs (Integer max_bytes) -> String?
107
+ def shift: (Integer max_bytes) -> String?
108
+
109
+ # Returns the response trailers supplied when the body closed.
110
+ #
111
+ # @rbs () -> Hash[String, String | Array[String]]
112
+ def trailers: () -> Hash[String, String | Array[String]]
113
+
114
+ # Closes the stream and invokes its callback exactly once.
115
+ #
116
+ # @rbs (Symbol reason) -> void
117
+ def finish: (Symbol reason) -> void
118
+
119
+ private
120
+
121
+ # @rbs (Integer bytes) -> bool
122
+ def reserve: (Integer bytes) -> bool
123
+
124
+ # @rbs (Integer bytes) -> void
125
+ def release: (Integer bytes) -> void
126
+ end
127
+ end
@@ -61,6 +61,18 @@ module Raptor
61
61
  # @rbs (Hash[String, untyped] env_template, Integer? max_body_size) -> ^(Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
62
62
  def self.parser_worker: (Hash[String, untyped] env_template, Integer? max_body_size) -> ^(Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
63
63
 
64
+ # Calls every `rack.response_finished` callback in reverse
65
+ # registration order, rescuing any that raise.
66
+ #
67
+ # @param env [Hash, nil] the Rack environment
68
+ # @param status [Integer, nil] the response status code
69
+ # @param headers [Hash, nil] the response headers
70
+ # @param error [Exception, nil] any error raised during processing, or nil on success
71
+ # @return [void]
72
+ #
73
+ # @rbs (Hash[String, untyped]? env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
74
+ def self.call_response_finished: (Hash[String, untyped]? env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
75
+
64
76
  # Writes a Common Log Format entry to `io`. Write failures are silently
65
77
  # ignored.
66
78
  #
@@ -78,6 +78,16 @@ module Raptor
78
78
  # @rbs (Hash[String, untyped] env) -> bool
79
79
  def self.request_smuggling?: (Hash[String, untyped] env) -> bool
80
80
 
81
+ # Encodes one HTTP/1.1 response chunk.
82
+ #
83
+ # @rbs (String chunk) -> String
84
+ def self.encode_chunk: (String chunk) -> String
85
+
86
+ # Encodes the final HTTP/1.1 response chunk and its trailers.
87
+ #
88
+ # @rbs (Hash[String, String | Array[String]] trailers) -> String
89
+ def self.encode_trailers: (Hash[String, String | Array[String]] trailers) -> String
90
+
81
91
  # Advances an HTTP/1.x request parse from the state hash's buffered
82
92
  # bytes. A complete well-formed request returns with `:complete` set
83
93
  # plus populated `:env` and `:body`; malformed or oversized input flips
@@ -282,21 +292,23 @@ module Raptor
282
292
  # for another request.
283
293
  #
284
294
  # @param socket [TCPSocket] the client socket
295
+ # @param id [Integer] unique client identifier
285
296
  # @param env [Hash] partial env hash from the HTTP parser
286
297
  # @param parse_data [Hash] metadata from the parsing pass
287
298
  # @param body [String, nil] decoded request body
299
+ # @param reactor [Reactor] the reactor managing the client connection
288
300
  # @param request_count [Integer] number of requests handled on this connection
289
301
  # @param remote_addr [String] client IP address
290
302
  # @param url_scheme [String] "http" or "https"
291
303
  # @return [Boolean] true if the connection should be kept alive
292
304
  #
293
- # @rbs (TCPSocket socket, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, Integer request_count, String remote_addr, String url_scheme) -> bool
294
- def process_request: (TCPSocket socket, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, Integer request_count, String remote_addr, String url_scheme) -> bool
305
+ # @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
306
+ def process_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
295
307
 
296
308
  # Calls the Rack app and writes its response for one request.
297
309
  #
298
- # @rbs (TCPSocket socket, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, Integer request_count, String remote_addr, String url_scheme) -> bool
299
- def perform_request: (TCPSocket socket, Hash[String, untyped] env, Hash[Symbol, untyped] parse_data, String? body, Integer request_count, String remote_addr, String url_scheme) -> bool
310
+ # @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
+ 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
300
312
 
301
313
  # Handles an exception raised while processing a request. Fires the
302
314
  # `rack.response_finished` callbacks with the error, writes a 500
@@ -419,6 +431,12 @@ module Raptor
419
431
  # @rbs (TCPSocket socket, Hash[String, String | Array[String]] hints) -> void
420
432
  def send_early_hints: (TCPSocket socket, Hash[String, String | Array[String]] hints) -> void
421
433
 
434
+ # Starts a detached response, chunked on HTTP/1.1 and ended by closing
435
+ # the connection on HTTP/1.0.
436
+ #
437
+ # @rbs (TCPSocket socket, Integer status, Hash[String, String | Array[String]] headers, chunked: bool, keep_alive: bool) -> void
438
+ def write_detached_response: (TCPSocket socket, Integer status, Hash[String, String | Array[String]] headers, chunked: bool, keep_alive: bool) -> void
439
+
422
440
  # Writes a complete HTTP response to the socket.
423
441
  #
424
442
  # @param socket [TCPSocket] the client socket to write to
@@ -580,18 +598,6 @@ module Raptor
580
598
  # @rbs (TCPSocket socket, String response, untyped body, bool use_chunked) -> void
581
599
  def write_enumerable_body: (TCPSocket socket, String response, untyped body, bool use_chunked) -> void
582
600
 
583
- # Calls every `rack.response_finished` callback in reverse
584
- # registration order, rescuing any that raise.
585
- #
586
- # @param env [Hash, nil] the Rack environment
587
- # @param status [Integer, nil] the response status code
588
- # @param headers [Hash, nil] the response headers
589
- # @param error [Exception, nil] any error raised during processing, or nil on success
590
- # @return [void]
591
- #
592
- # @rbs (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
593
- def call_response_finished: (Hash[String, untyped] env, Integer? status, Hash[String, String | Array[String]]? headers, Exception? error) -> void
594
-
595
601
  # Instance-level wrapper around {Http.write_access_log} that routes to
596
602
  # the configured `@access_log_io`.
597
603
  #