nghttp3 0.0.1

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.
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nghttp3
4
+ # An HTTP/3 response object
5
+ #
6
+ # Can be used for both:
7
+ # - Client-side: receiving response from server (populated via callbacks)
8
+ # - Server-side: building response to send to client
9
+ class Response
10
+ # @return [Integer] stream ID
11
+ attr_reader :stream_id
12
+
13
+ # @return [Integer, nil] HTTP status code
14
+ attr_accessor :status
15
+
16
+ # @return [Headers] response headers
17
+ attr_reader :headers
18
+
19
+ # @return [String, nil] response body (for simple responses)
20
+ attr_accessor :body
21
+
22
+ # Create a new response
23
+ # @param stream_id [Integer] the stream ID this response belongs to
24
+ # @param status [Integer, nil] HTTP status code
25
+ # @param headers [Hash, Headers, nil] response headers
26
+ # @param body [String, nil] response body
27
+ def initialize(stream_id:, status: nil, headers: nil, body: nil)
28
+ @stream_id = stream_id
29
+ @status = status
30
+ @headers = headers.is_a?(Headers) ? headers : Headers.new(headers || {})
31
+ @body = body
32
+ @body_chunks = []
33
+ @headers_sent = false
34
+ @finished = false
35
+ end
36
+
37
+ # Convert to NV array for low-level Connection API (server-side)
38
+ # @return [Array<NV>] array of NV objects for status and headers
39
+ def to_nv_array
40
+ nvs = []
41
+ nvs << NV.new(":status", @status.to_s) if @status
42
+ @headers.each { |name, value| nvs << NV.new(name, value) }
43
+ nvs
44
+ end
45
+
46
+ # Mark headers as sent (for streaming responses)
47
+ # @param extra_headers [Hash] additional headers to merge before sending
48
+ # @return [self]
49
+ def write_headers(extra_headers = {})
50
+ raise InvalidStateError, "Headers already sent" if @headers_sent
51
+ @headers.merge!(extra_headers) unless extra_headers.empty?
52
+ @headers_sent = true
53
+ self
54
+ end
55
+
56
+ # Check if headers have been sent
57
+ # @return [Boolean]
58
+ def headers_sent?
59
+ @headers_sent
60
+ end
61
+
62
+ # Write a chunk of body data (for streaming responses)
63
+ # @param chunk [String] body chunk to write
64
+ # @return [self]
65
+ def write(chunk)
66
+ raise InvalidStateError, "Response already finished" if @finished
67
+ @body_chunks << chunk.to_s
68
+ self
69
+ end
70
+
71
+ # Get all body chunks written so far
72
+ # @return [Array<String>]
73
+ def body_chunks
74
+ @body_chunks.dup
75
+ end
76
+
77
+ # Get combined body from chunks
78
+ # @return [String]
79
+ def body_from_chunks
80
+ @body_chunks.join
81
+ end
82
+
83
+ # Mark the response as finished
84
+ # @return [self]
85
+ def finish
86
+ @finished = true
87
+ self
88
+ end
89
+
90
+ # Check if the response is finished
91
+ # @return [Boolean]
92
+ def finished?
93
+ @finished
94
+ end
95
+
96
+ # Check if response has a body
97
+ # @return [Boolean]
98
+ def body?
99
+ (@body && !@body.empty?) || !@body_chunks.empty?
100
+ end
101
+
102
+ # Get the effective body (either set body or joined chunks)
103
+ # @return [String, nil]
104
+ def effective_body
105
+ return @body if @body && !@body.empty?
106
+ return nil if @body_chunks.empty?
107
+ body_from_chunks
108
+ end
109
+
110
+ # Append data to body (for client-side receiving)
111
+ # @param data [String] data to append
112
+ # @return [self]
113
+ def append_body(data)
114
+ @body ||= +""
115
+ @body << data
116
+ self
117
+ end
118
+
119
+ # String representation
120
+ def inspect
121
+ status_str = @status ? @status.to_s : "pending"
122
+ finished_str = @finished ? " finished" : ""
123
+ "#<#{self.class} stream_id=#{@stream_id} status=#{status_str}#{finished_str}>"
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nghttp3
4
+ # High-level HTTP/3 server
5
+ #
6
+ # Wraps the low-level Connection API with automatic request handling,
7
+ # convenient response building, and stream management.
8
+ #
9
+ # @example Basic usage
10
+ # server = Nghttp3::Server.new
11
+ # server.bind_streams(control: 3, qpack_encoder: 7, qpack_decoder: 11)
12
+ #
13
+ # server.on_request do |request, response|
14
+ # response.status = 200
15
+ # response.headers["content-type"] = "text/html"
16
+ # response.body = "<h1>Hello!</h1>"
17
+ # end
18
+ #
19
+ # # Feed data from QUIC layer
20
+ # server.read_stream(stream_id, received_data, fin: false)
21
+ #
22
+ # # Pump data to QUIC layer
23
+ # server.pump_writes do |stream_id, data, fin|
24
+ # quic.write(stream_id, data, fin)
25
+ # end
26
+ class Server
27
+ # @return [Connection] the underlying low-level connection
28
+ attr_reader :connection
29
+
30
+ # @return [Settings] the settings used for this server
31
+ attr_reader :settings
32
+
33
+ # @return [Hash{Integer => Request}] received requests by stream ID
34
+ attr_reader :requests
35
+
36
+ # @return [Hash{Integer => Response}] responses by stream ID
37
+ attr_reader :responses
38
+
39
+ # Create a new HTTP/3 server
40
+ # @param settings [Settings, nil] settings to use (defaults to Settings.default)
41
+ def initialize(settings: nil)
42
+ @settings = settings || Settings.default
43
+ @callbacks = setup_callbacks
44
+ @connection = Connection.server_new(@settings, @callbacks)
45
+ @stream_manager = StreamManager.new(is_server: true)
46
+ @request_handler = nil
47
+ @requests = {}
48
+ @responses = {}
49
+ @building_requests = {} # Requests being built (headers not complete)
50
+ @streams_bound = false
51
+ end
52
+
53
+ # Bind control and QPACK streams
54
+ #
55
+ # Must be called before handling requests. The stream IDs should be
56
+ # unidirectional streams opened by the QUIC layer.
57
+ #
58
+ # @param control [Integer] control stream ID
59
+ # @param qpack_encoder [Integer] QPACK encoder stream ID
60
+ # @param qpack_decoder [Integer] QPACK decoder stream ID
61
+ # @return [self]
62
+ def bind_streams(control:, qpack_encoder:, qpack_decoder:)
63
+ @connection.bind_control_stream(control)
64
+ @connection.bind_qpack_streams(qpack_encoder, qpack_decoder)
65
+ @stream_manager.register_stream(control, type: :uni)
66
+ @stream_manager.register_stream(qpack_encoder, type: :uni)
67
+ @stream_manager.register_stream(qpack_decoder, type: :uni)
68
+ @streams_bound = true
69
+ self
70
+ end
71
+
72
+ # Check if streams are bound
73
+ # @return [Boolean]
74
+ def streams_bound?
75
+ @streams_bound
76
+ end
77
+
78
+ # Register a request handler
79
+ #
80
+ # The handler is called when a complete request is received.
81
+ # The handler should set response.status and response.body (or use streaming).
82
+ #
83
+ # @yield [request, response] for each incoming request
84
+ # @yieldparam request [Request] the incoming request
85
+ # @yieldparam response [Response] the response object to populate
86
+ # @return [self]
87
+ def on_request(&block)
88
+ @request_handler = block
89
+ self
90
+ end
91
+
92
+ # Pump pending writes to the QUIC layer
93
+ #
94
+ # @yield [stream_id, data, fin] for each pending write
95
+ # @yieldparam stream_id [Integer] the stream ID
96
+ # @yieldparam data [String] the data to write
97
+ # @yieldparam fin [Boolean] true if this is the final data for the stream
98
+ # @yieldreturn [Integer] number of bytes accepted by QUIC layer
99
+ # @return [self]
100
+ def pump_writes
101
+ while (result = @connection.writev_stream)
102
+ stream_id = result[:stream_id]
103
+ data = result[:data]
104
+ fin = result[:fin]
105
+
106
+ bytes_written = yield(stream_id, data, fin) if block_given?
107
+ bytes_written ||= data.bytesize
108
+
109
+ @connection.add_write_offset(stream_id, bytes_written)
110
+ end
111
+ self
112
+ end
113
+
114
+ # Read data from QUIC layer into HTTP/3 connection
115
+ # @param stream_id [Integer] stream ID
116
+ # @param data [String] received data
117
+ # @param fin [Boolean] true if this is the final data for the stream
118
+ # @return [Integer] number of bytes consumed
119
+ def read_stream(stream_id, data, fin: false)
120
+ @connection.read_stream(stream_id, data, fin: fin)
121
+ end
122
+
123
+ # Notify that bytes have been acknowledged by the peer
124
+ # @param stream_id [Integer] stream ID
125
+ # @param n [Integer] number of bytes acknowledged
126
+ # @return [self]
127
+ def add_ack_offset(stream_id, n)
128
+ @connection.add_ack_offset(stream_id, n)
129
+ self
130
+ end
131
+
132
+ # Close the server connection
133
+ # @return [nil]
134
+ def close
135
+ @connection.close
136
+ end
137
+
138
+ # Check if the connection is closed
139
+ # @return [Boolean]
140
+ def closed?
141
+ @connection.closed?
142
+ end
143
+
144
+ private
145
+
146
+ def setup_callbacks
147
+ Callbacks.new
148
+ .on_begin_headers { |stream_id| on_begin_headers(stream_id) }
149
+ .on_recv_header { |stream_id, name, value, flags| on_recv_header(stream_id, name, value, flags) }
150
+ .on_end_headers { |stream_id, fin| on_end_headers(stream_id, fin) }
151
+ .on_recv_data { |stream_id, data| on_recv_data(stream_id, data) }
152
+ .on_end_stream { |stream_id| on_end_stream(stream_id) }
153
+ .on_stream_close { |stream_id, app_error_code| on_stream_close(stream_id, app_error_code) }
154
+ end
155
+
156
+ def on_begin_headers(stream_id)
157
+ # Start building a new request
158
+ @building_requests[stream_id] = {
159
+ method: nil,
160
+ scheme: nil,
161
+ authority: nil,
162
+ path: nil,
163
+ headers: Headers.new,
164
+ body: nil
165
+ }
166
+ @stream_manager.register_stream(stream_id, type: :bidi)
167
+ end
168
+
169
+ def on_recv_header(stream_id, name, value, _flags)
170
+ req = @building_requests[stream_id]
171
+ return unless req
172
+
173
+ case name
174
+ when ":method"
175
+ req[:method] = value
176
+ when ":scheme"
177
+ req[:scheme] = value
178
+ when ":authority"
179
+ req[:authority] = value
180
+ when ":path"
181
+ req[:path] = value
182
+ else
183
+ req[:headers][name] = value
184
+ end
185
+ end
186
+
187
+ def on_end_headers(stream_id, fin)
188
+ # Headers complete, create immutable Request
189
+ req_data = @building_requests[stream_id]
190
+ return unless req_data
191
+
192
+ @requests[stream_id] = Request.new(
193
+ method: req_data[:method] || "GET",
194
+ scheme: req_data[:scheme] || "https",
195
+ authority: req_data[:authority],
196
+ path: req_data[:path] || "/",
197
+ headers: req_data[:headers]
198
+ )
199
+
200
+ # Create response object
201
+ @responses[stream_id] = Response.new(stream_id: stream_id)
202
+
203
+ # If request has no body (fin=true), process it immediately
204
+ process_request(stream_id) if fin
205
+ end
206
+
207
+ def on_recv_data(stream_id, data)
208
+ req_data = @building_requests[stream_id]
209
+ if req_data
210
+ req_data[:body] ||= +""
211
+ req_data[:body] << data
212
+ end
213
+ end
214
+
215
+ def on_end_stream(stream_id)
216
+ # Request complete with body
217
+ req_data = @building_requests.delete(stream_id)
218
+ if req_data && @requests[stream_id].nil?
219
+ # Headers weren't finalized yet, create request with body
220
+ @requests[stream_id] = Request.new(
221
+ method: req_data[:method] || "GET",
222
+ scheme: req_data[:scheme] || "https",
223
+ authority: req_data[:authority],
224
+ path: req_data[:path] || "/",
225
+ headers: req_data[:headers],
226
+ body: req_data[:body]
227
+ )
228
+ @responses[stream_id] ||= Response.new(stream_id: stream_id)
229
+ end
230
+
231
+ process_request(stream_id)
232
+ end
233
+
234
+ def on_stream_close(stream_id, _app_error_code)
235
+ @building_requests.delete(stream_id)
236
+ @stream_manager.close_stream(stream_id)
237
+ end
238
+
239
+ def process_request(stream_id)
240
+ request = @requests[stream_id]
241
+ response = @responses[stream_id]
242
+ return unless request && response && @request_handler
243
+
244
+ # Call the request handler
245
+ @request_handler.call(request, response)
246
+
247
+ # Submit the response if status is set
248
+ if response.status
249
+ @connection.submit_response(stream_id, response.to_nv_array, body: response.effective_body)
250
+ end
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nghttp3
4
+ # Internal stream ID management
5
+ #
6
+ # HTTP/3 uses different stream ID allocation for clients and servers:
7
+ # - Client-initiated bidirectional streams: 0, 4, 8, 12, ...
8
+ # - Server-initiated bidirectional streams: 1, 5, 9, 13, ...
9
+ # - Client-initiated unidirectional streams: 2, 6, 10, 14, ...
10
+ # - Server-initiated unidirectional streams: 3, 7, 11, 15, ...
11
+ #
12
+ # @private
13
+ class StreamManager
14
+ # Stream types
15
+ BIDI_CLIENT = 0x00
16
+ BIDI_SERVER = 0x01
17
+ UNI_CLIENT = 0x02
18
+ UNI_SERVER = 0x03
19
+
20
+ def initialize(is_server:)
21
+ @is_server = is_server
22
+ # Next stream IDs by type
23
+ @next_bidi_stream_id = is_server ? 1 : 0
24
+ @next_uni_stream_id = is_server ? 3 : 2
25
+ # Track active streams
26
+ @active_streams = {}
27
+ end
28
+
29
+ # Allocate a new bidirectional stream ID
30
+ # @return [Integer] new stream ID
31
+ def allocate_bidi_stream_id
32
+ id = @next_bidi_stream_id
33
+ @next_bidi_stream_id += 4
34
+ @active_streams[id] = {type: :bidi, state: :open}
35
+ id
36
+ end
37
+
38
+ # Allocate a new unidirectional stream ID
39
+ # @return [Integer] new stream ID
40
+ def allocate_uni_stream_id
41
+ id = @next_uni_stream_id
42
+ @next_uni_stream_id += 4
43
+ @active_streams[id] = {type: :uni, state: :open}
44
+ id
45
+ end
46
+
47
+ # Register an externally-opened stream (e.g., from QUIC layer)
48
+ # @param stream_id [Integer] stream ID
49
+ # @param type [Symbol] :bidi or :uni
50
+ def register_stream(stream_id, type: :bidi)
51
+ @active_streams[stream_id] = {type: type, state: :open}
52
+ end
53
+
54
+ # Close a stream
55
+ # @param stream_id [Integer] stream ID to close
56
+ def close_stream(stream_id)
57
+ if @active_streams[stream_id]
58
+ @active_streams[stream_id][:state] = :closed
59
+ end
60
+ end
61
+
62
+ # Remove a stream from tracking
63
+ # @param stream_id [Integer] stream ID to remove
64
+ def remove_stream(stream_id)
65
+ @active_streams.delete(stream_id)
66
+ end
67
+
68
+ # Check if stream is active
69
+ # @param stream_id [Integer] stream ID
70
+ # @return [Boolean]
71
+ def stream_active?(stream_id)
72
+ @active_streams[stream_id]&.dig(:state) == :open
73
+ end
74
+
75
+ # Get all active stream IDs
76
+ # @return [Array<Integer>]
77
+ def active_stream_ids
78
+ @active_streams.select { |_, v| v[:state] == :open }.keys
79
+ end
80
+
81
+ # Get stream info
82
+ # @param stream_id [Integer] stream ID
83
+ # @return [Hash, nil] stream info or nil if not found
84
+ def stream_info(stream_id)
85
+ @active_streams[stream_id]&.dup
86
+ end
87
+
88
+ # Check if this is a client-initiated stream
89
+ # @param stream_id [Integer] stream ID
90
+ # @return [Boolean]
91
+ def client_initiated?(stream_id)
92
+ (stream_id & 0x01) == 0
93
+ end
94
+
95
+ # Check if this is a server-initiated stream
96
+ # @param stream_id [Integer] stream ID
97
+ # @return [Boolean]
98
+ def server_initiated?(stream_id)
99
+ (stream_id & 0x01) == 1
100
+ end
101
+
102
+ # Check if this is a bidirectional stream
103
+ # @param stream_id [Integer] stream ID
104
+ # @return [Boolean]
105
+ def bidirectional?(stream_id)
106
+ (stream_id & 0x02) == 0
107
+ end
108
+
109
+ # Check if this is a unidirectional stream
110
+ # @param stream_id [Integer] stream ID
111
+ # @return [Boolean]
112
+ def unidirectional?(stream_id)
113
+ (stream_id & 0x02) == 2
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nghttp3
4
+ VERSION = "0.0.1"
5
+ end
data/lib/nghttp3.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "nghttp3/version"
4
+ require_relative "nghttp3/nghttp3"
5
+
6
+ # High-level API
7
+ require_relative "nghttp3/headers"
8
+ require_relative "nghttp3/request"
9
+ require_relative "nghttp3/response"
10
+ require_relative "nghttp3/stream_manager"
11
+ require_relative "nghttp3/client"
12
+ require_relative "nghttp3/server"
13
+
14
+ module Nghttp3
15
+ class Error < StandardError; end
16
+ end
@@ -0,0 +1,30 @@
1
+ module Nghttp3
2
+ class Callbacks
3
+ def initialize: () -> void
4
+
5
+ # Stream data callbacks
6
+ def on_acked_stream_data: () { (Integer stream_id, Integer datalen) -> void } -> self
7
+ def on_stream_close: () { (Integer stream_id, Integer app_error_code) -> void } -> self
8
+ def on_recv_data: () { (Integer stream_id, String data) -> void } -> self
9
+ def on_deferred_consume: () { (Integer stream_id, Integer consumed) -> void } -> self
10
+
11
+ # Header callbacks
12
+ def on_begin_headers: () { (Integer stream_id) -> void } -> self
13
+ def on_recv_header: () { (Integer stream_id, String name, String value, Integer flags) -> void } -> self
14
+ def on_end_headers: () { (Integer stream_id, bool fin) -> void } -> self
15
+
16
+ # Trailer callbacks
17
+ def on_begin_trailers: () { (Integer stream_id) -> void } -> self
18
+ def on_recv_trailer: () { (Integer stream_id, String name, String value, Integer flags) -> void } -> self
19
+ def on_end_trailers: () { (Integer stream_id, bool fin) -> void } -> self
20
+
21
+ # Stream control callbacks
22
+ def on_stop_sending: () { (Integer stream_id, Integer app_error_code) -> void } -> self
23
+ def on_end_stream: () { (Integer stream_id) -> void } -> self
24
+ def on_reset_stream: () { (Integer stream_id, Integer app_error_code) -> void } -> self
25
+
26
+ # Connection callbacks
27
+ def on_shutdown: () { (Integer id) -> void } -> self
28
+ def on_recv_settings: () { (Hash[Symbol, untyped] settings) -> void } -> self
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ module Nghttp3
2
+ class Client
3
+ attr_reader connection: Connection
4
+ attr_reader settings: Settings
5
+ attr_reader responses: Hash[Integer, Response]
6
+ attr_reader pending_requests: Hash[Integer, Request]
7
+
8
+ def initialize: (?settings: Settings?) -> void
9
+
10
+ def bind_streams: (control: Integer, qpack_encoder: Integer, qpack_decoder: Integer) -> self
11
+ def streams_bound?: () -> bool
12
+
13
+ def submit: (Request request) -> Integer
14
+
15
+ def get: (String url, ?headers: Hash[String, String]) -> Integer
16
+ def post: (String url, ?body: String?, ?headers: Hash[String, String]) -> Integer
17
+ def put: (String url, ?body: String?, ?headers: Hash[String, String]) -> Integer
18
+ def delete: (String url, ?headers: Hash[String, String]) -> Integer
19
+ def head: (String url, ?headers: Hash[String, String]) -> Integer
20
+
21
+ def pump_writes: () { (Integer stream_id, String data, bool fin) -> Integer? } -> self
22
+ def read_stream: (Integer stream_id, String data, ?fin: bool) -> Integer
23
+ def add_ack_offset: (Integer stream_id, Integer n) -> self
24
+
25
+ def close: () -> nil
26
+ def closed?: () -> bool
27
+
28
+ private
29
+
30
+ def setup_callbacks: () -> Callbacks
31
+ def on_begin_headers: (Integer stream_id) -> void
32
+ def on_recv_header: (Integer stream_id, String name, String value, Integer flags) -> void
33
+ def on_end_headers: (Integer stream_id, bool fin) -> void
34
+ def on_recv_data: (Integer stream_id, String data) -> void
35
+ def on_end_stream: (Integer stream_id) -> void
36
+ def on_stream_close: (Integer stream_id, Integer app_error_code) -> void
37
+ end
38
+ end
@@ -0,0 +1,85 @@
1
+ module Nghttp3
2
+ class Connection
3
+ # Creates a new client connection
4
+ def self.client_new: (?Settings? settings, ?Callbacks? callbacks) -> Connection
5
+
6
+ # Creates a new server connection
7
+ def self.server_new: (?Settings? settings, ?Callbacks? callbacks) -> Connection
8
+
9
+ # Binds the control stream
10
+ def bind_control_stream: (Integer stream_id) -> self
11
+
12
+ # Binds QPACK encoder and decoder streams
13
+ def bind_qpack_streams: (Integer encoder_stream_id, Integer decoder_stream_id) -> self
14
+
15
+ # Closes the connection
16
+ def close: () -> nil
17
+
18
+ # Returns true if the connection is closed
19
+ def closed?: () -> bool
20
+
21
+ # Returns true if this is a server connection
22
+ def server?: () -> bool
23
+
24
+ # Returns true if this is a client connection
25
+ def client?: () -> bool
26
+
27
+ # Stream operations
28
+
29
+ # Reads data on a stream from the QUIC layer
30
+ def read_stream: (Integer stream_id, String data, ?fin: bool) -> Integer
31
+
32
+ # Gets stream data to send to the QUIC layer
33
+ def writev_stream: () -> { stream_id: Integer, fin: bool, data: String }?
34
+
35
+ # Tells the connection that n bytes have been accepted by the QUIC layer
36
+ def add_write_offset: (Integer stream_id, Integer n) -> self
37
+
38
+ # Tells the connection that n bytes have been acknowledged by the remote peer
39
+ def add_ack_offset: (Integer stream_id, Integer n) -> self
40
+
41
+ # Marks a stream as blocked due to QUIC flow control
42
+ def block_stream: (Integer stream_id) -> self
43
+
44
+ # Marks a stream as unblocked
45
+ def unblock_stream: (Integer stream_id) -> self
46
+
47
+ # Returns true if the stream is writable
48
+ def stream_writable?: (Integer stream_id) -> bool
49
+
50
+ # Closes the stream with the given error code
51
+ def close_stream: (Integer stream_id, Integer app_error_code) -> self
52
+
53
+ # Prevents any further write operations on the stream
54
+ def shutdown_stream_write: (Integer stream_id) -> self
55
+
56
+ # Resumes a stream that was blocked for input data
57
+ def resume_stream: (Integer stream_id) -> self
58
+
59
+ # HTTP operations
60
+
61
+ # Submits an HTTP request (client only)
62
+ def submit_request: (Integer stream_id, Array[NV] headers, ?body: String?) ?{ (Integer stream_id) -> (String | Symbol | nil) } -> self
63
+
64
+ # Submits an HTTP response (server only)
65
+ def submit_response: (Integer stream_id, Array[NV] headers, ?body: String?) ?{ (Integer stream_id) -> (String | Symbol | nil) } -> self
66
+
67
+ # Submits a 1xx informational response
68
+ def submit_info: (Integer stream_id, Array[NV] headers) -> self
69
+
70
+ # Submits trailer headers (implicitly ends the stream)
71
+ def submit_trailers: (Integer stream_id, Array[NV] trailers) -> self
72
+
73
+ # Signals the intention to shut down the connection gracefully
74
+ def submit_shutdown_notice: () -> self
75
+
76
+ # Initiates graceful shutdown
77
+ def shutdown: () -> self
78
+
79
+ # Associates arbitrary data with a stream
80
+ def set_stream_user_data: (Integer stream_id, untyped data) -> self
81
+
82
+ # Returns data associated with a stream
83
+ def get_stream_user_data: (Integer stream_id) -> untyped
84
+ end
85
+ end