http3 0.0.3

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,193 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTP3
4
+ # HTTP/3 on one QUIC connection. The C part (ext/http3/session.c) runs
5
+ # nghttp3 inside the connection's packet I/O and calls the on_* methods
6
+ # below with the parts of each request. This part puts the requests
7
+ # together and keeps the bodies of the responses being sent.
8
+ #
9
+ # A Session belongs to the server's I/O loop: Server takes the finished
10
+ # requests, answers them, keeps the bodies flowing and lets go of what
11
+ # the streams no longer need.
12
+ class Session
13
+ # A request still coming in. reject is the status to answer with
14
+ # instead of calling the application (413, 431), or nil.
15
+ Incoming = Struct.new(:pseudo, :fields, :body, :reject)
16
+
17
+ # A response whose body is being sent.
18
+ Outgoing = Struct.new(:request, :body, :done)
19
+
20
+ # Take over quic_conn's streams. A request body over max_request_body
21
+ # bytes is answered with 413, a header section over
22
+ # max_field_section_size bytes (RFC 9114 section 4.2.2) with 431.
23
+ def self.attach(quic_conn, max_request_body:, max_field_section_size:)
24
+ session = _attach(quic_conn, max_field_section_size)
25
+ session.send(:setup, max_request_body)
26
+ session
27
+ end
28
+
29
+ # The requests received in full since the last call, each as
30
+ # [request, status]: status is nil for the application to answer the
31
+ # request, or the status to answer it with directly.
32
+ def take_requests
33
+ done = @done
34
+ @done = []
35
+ done
36
+ end
37
+
38
+ # Send response to request. Its body, if any, is kept until the stream
39
+ # closes, but the IO of a Body::File is closed at once: the C part reads
40
+ # the file through a descriptor of its own. When that descriptor cannot
41
+ # be had (EMFILE, say), the stream is aborted with H3_INTERNAL_ERROR and
42
+ # the block gets the exception.
43
+ def respond(request, response)
44
+ headers = [[":status", response.status.to_s], *response.headers]
45
+ body = response.body
46
+ submit_response(request.stream_id, headers, !body.nil?)
47
+ return if body.nil?
48
+
49
+ @outgoing[request.stream_id] = Outgoing.new(request, body, false)
50
+ return unless body.is_a?(Body::File)
51
+
52
+ begin
53
+ attach_file(request.stream_id, body.io, body.offset, body.length)
54
+ rescue SystemCallError => e
55
+ abort_stream(request.stream_id, Nghttp3::H3_INTERNAL_ERROR)
56
+ yield e if block_given?
57
+ ensure
58
+ body.io.close
59
+ end
60
+ end
61
+
62
+ # Queue more of each Body::Chunks body until limit bytes wait unsent on
63
+ # its stream. A body whose enum raises is the application failing after
64
+ # its headers went out: the stream is aborted with H3_INTERNAL_ERROR and
65
+ # the block gets (request, exception).
66
+ def fill_bodies(limit)
67
+ @outgoing.each_value do |out|
68
+ next if out.done || !out.body.is_a?(Body::Chunks)
69
+
70
+ fill_body(out, limit) { |error| yield out.request, error if block_given? }
71
+ end
72
+ end
73
+
74
+ # True when a Body::Chunks body could take more right now.
75
+ def wants_fill?(limit)
76
+ @outgoing.any? do |id, out|
77
+ !out.done && out.body.is_a?(Body::Chunks) && unsent_bytes(id) < limit
78
+ end
79
+ end
80
+
81
+ # True while a request stream is open: received in part, waiting for
82
+ # its answer, or with a response still being sent.
83
+ def busy?
84
+ !@open.empty?
85
+ end
86
+
87
+ # Let go of the streams nghttp3 closed: their requests are dropped and
88
+ # their bodies closed. The block gets (stream_id, exception) for a body
89
+ # whose close raised.
90
+ def release_closed(&block)
91
+ take_closed.each { |id| release(id, &block) }
92
+ end
93
+
94
+ # The connection is gone: close every body still held.
95
+ def release_all(&block)
96
+ (@open.keys | @outgoing.keys).each { |id| release(id, &block) }
97
+ end
98
+
99
+ # Called by the C part.
100
+
101
+ def on_begin_headers(id)
102
+ @open[id] = true
103
+ @incoming[id] = Incoming.new([], [], "".b, nil)
104
+ end
105
+
106
+ def on_header(id, name, value)
107
+ request = @incoming[id] or return
108
+ (name.start_with?(":") ? request.pseudo : request.fields) << [name, value]
109
+ end
110
+
111
+ def on_fields_too_large(id)
112
+ request = @incoming[id] or return
113
+ request.reject = 431
114
+ end
115
+
116
+ def on_end_headers(id)
117
+ end
118
+
119
+ def on_data(id, bytes)
120
+ request = @incoming[id] or return
121
+ return if request.reject
122
+
123
+ if request.body.bytesize + bytes.bytesize > @max_request_body
124
+ request.reject = 413
125
+ request.body = "".b
126
+ else
127
+ request.body << bytes
128
+ end
129
+ end
130
+
131
+ def on_end_stream(id)
132
+ request = @incoming.delete(id) or return
133
+ headers = request.pseudo + request.fields
134
+ @done << [Request.new(stream_id: id, headers: headers, body: request.body), request.reject]
135
+ end
136
+
137
+ private
138
+
139
+ def setup(max_request_body)
140
+ @max_request_body = max_request_body
141
+ @incoming = {}
142
+ @done = []
143
+ @outgoing = {}
144
+ @open = {}
145
+ end
146
+
147
+ def fill_body(out, limit)
148
+ id = out.request.stream_id
149
+ while unsent_bytes(id) < limit
150
+ begin
151
+ chunk = out.body.enum.next
152
+ rescue StopIteration
153
+ out.done = true
154
+ push_eof(id)
155
+ return
156
+ rescue => e
157
+ out.done = true
158
+ abort_stream(id, Nghttp3::H3_INTERNAL_ERROR)
159
+ yield e
160
+ return
161
+ end
162
+ unless chunk.is_a?(String)
163
+ out.done = true
164
+ abort_stream(id, Nghttp3::H3_INTERNAL_ERROR)
165
+ yield TypeError.new("body chunk must be a String (got #{chunk.class})")
166
+ return
167
+ end
168
+ push_chunk(id, chunk)
169
+ end
170
+ rescue Nghttp3::Error
171
+ # The stream went away under the body (the peer reset it); it is
172
+ # released when its close comes through.
173
+ out.done = true
174
+ end
175
+
176
+ def release(id)
177
+ @open.delete(id)
178
+ @incoming.delete(id)
179
+ @done.reject! { |request, _| request.stream_id == id }
180
+ out = @outgoing.delete(id) or return
181
+
182
+ close_body(out.body)
183
+ rescue => e
184
+ yield id, e if block_given?
185
+ end
186
+
187
+ def close_body(body)
188
+ body.io.close if body.is_a?(Body::File) && !body.io.closed?
189
+ ensure
190
+ body.rack_body.close if body.rack_body.respond_to?(:close)
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTP3
4
+ VERSION = "0.0.3"
5
+ end
data/lib/http3.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "http3/version"
4
+ require_relative "http3/http3"
5
+
6
+ require_relative "http3/request"
7
+ require_relative "http3/response"
8
+ require_relative "http3/session"
9
+ require_relative "http3/rack_adapter"
10
+ require_relative "http3/server"
11
+
12
+ module HTTP3
13
+ module Nghttp3
14
+ class Error < StandardError; end
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ module HTTP3
2
+ module Nghttp3
3
+ class Callbacks
4
+ def initialize: () -> void
5
+
6
+ # Stream data callbacks
7
+ def on_acked_stream_data: () { (Integer stream_id, Integer datalen) -> void } -> self
8
+ def on_stream_close: () { (Integer stream_id, Integer app_error_code) -> void } -> self
9
+ def on_recv_data: () { (Integer stream_id, String data) -> void } -> self
10
+ def on_deferred_consume: () { (Integer stream_id, Integer consumed) -> void } -> self
11
+
12
+ # Header callbacks
13
+ def on_begin_headers: () { (Integer stream_id) -> void } -> self
14
+ def on_recv_header: () { (Integer stream_id, String name, String value, Integer flags) -> void } -> self
15
+ def on_end_headers: () { (Integer stream_id, bool fin) -> void } -> self
16
+
17
+ # Trailer callbacks
18
+ def on_begin_trailers: () { (Integer stream_id) -> void } -> self
19
+ def on_recv_trailer: () { (Integer stream_id, String name, String value, Integer flags) -> void } -> self
20
+ def on_end_trailers: () { (Integer stream_id, bool fin) -> void } -> self
21
+
22
+ # Stream control callbacks
23
+ def on_stop_sending: () { (Integer stream_id, Integer app_error_code) -> void } -> self
24
+ def on_end_stream: () { (Integer stream_id) -> void } -> self
25
+ def on_reset_stream: () { (Integer stream_id, Integer app_error_code) -> void } -> self
26
+
27
+ # Connection callbacks
28
+ def on_shutdown: () { (Integer id) -> void } -> self
29
+ def on_recv_settings: () { (Hash[Symbol, untyped] settings) -> void } -> self
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,101 @@
1
+ module HTTP3
2
+ module Nghttp3
3
+ class Connection
4
+ # Creates a new client connection
5
+ def self.client_new: (?Settings? settings, ?Callbacks? callbacks) -> Connection
6
+
7
+ # Creates a new server connection
8
+ def self.server_new: (?Settings? settings, ?Callbacks? callbacks) -> Connection
9
+
10
+ # Binds the control stream
11
+ def bind_control_stream: (Integer stream_id) -> self
12
+
13
+ # Binds QPACK encoder and decoder streams
14
+ def bind_qpack_streams: (Integer encoder_stream_id, Integer decoder_stream_id) -> self
15
+
16
+ # Closes the connection
17
+ def close: () -> nil
18
+
19
+ # Returns true if the connection is closed
20
+ def closed?: () -> bool
21
+
22
+ # Returns true if this is a server connection
23
+ def server?: () -> bool
24
+
25
+ # Returns true if this is a client connection
26
+ def client?: () -> bool
27
+
28
+ # Stream operations
29
+
30
+ # Reads data on a stream from the QUIC layer. ts is the time in
31
+ # nanoseconds on a monotonic clock (CLOCK_MONOTONIC by default). An
32
+ # exception raised by a callback comes out of here.
33
+ def read_stream: (Integer stream_id, String data, ?fin: bool, ?ts: Integer?) -> Integer
34
+
35
+ # Gets stream data to send to the QUIC layer. An exception raised by a
36
+ # body block comes out of here.
37
+ def writev_stream: () -> { stream_id: Integer, fin: bool, data: String }?
38
+
39
+ # Tells the connection that n bytes have been accepted by the QUIC layer
40
+ def add_write_offset: (Integer stream_id, Integer n) -> self
41
+
42
+ # Tells the connection that n bytes have been acknowledged by the remote peer
43
+ def add_ack_offset: (Integer stream_id, Integer n) -> self
44
+
45
+ # Marks a stream as blocked due to QUIC flow control
46
+ def block_stream: (Integer stream_id) -> self
47
+
48
+ # Marks a stream as unblocked
49
+ def unblock_stream: (Integer stream_id) -> self
50
+
51
+ # Returns true if the stream is writable
52
+ def stream_writable?: (Integer stream_id) -> bool
53
+
54
+ # Closes the stream with the given error code
55
+ def close_stream: (Integer stream_id, Integer app_error_code) -> self
56
+
57
+ # Prevents any further write operations on the stream
58
+ def shutdown_stream_write: (Integer stream_id) -> self
59
+
60
+ # Resumes a stream that was blocked for input data
61
+ def resume_stream: (Integer stream_id) -> self
62
+
63
+ # Tells the connection that no more data will be read on the stream
64
+ def shutdown_stream_read: (Integer stream_id) -> self
65
+
66
+ # Tells a server connection how many bidirectional streams the client
67
+ # may open in total
68
+ def set_max_client_streams_bidi: (Integer max_streams) -> self
69
+
70
+ # The body Strings handed to nghttp3 that the peer has not acknowledged
71
+ # in full yet, oldest first (a copy)
72
+ def pending_data: (Integer stream_id) -> Array[String]
73
+
74
+ # HTTP operations
75
+
76
+ # Submits an HTTP request (client only)
77
+ def submit_request: (Integer stream_id, Array[NV] headers, ?body: String?) ?{ (Integer stream_id) -> (String | Symbol | nil) } -> self
78
+
79
+ # Submits an HTTP response (server only)
80
+ def submit_response: (Integer stream_id, Array[NV] headers, ?body: String?) ?{ (Integer stream_id) -> (String | Symbol | nil) } -> self
81
+
82
+ # Submits a 1xx informational response
83
+ def submit_info: (Integer stream_id, Array[NV] headers) -> self
84
+
85
+ # Submits trailer headers (implicitly ends the stream)
86
+ def submit_trailers: (Integer stream_id, Array[NV] trailers) -> self
87
+
88
+ # Signals the intention to shut down the connection gracefully
89
+ def submit_shutdown_notice: () -> self
90
+
91
+ # Initiates graceful shutdown
92
+ def shutdown: () -> self
93
+
94
+ # Associates arbitrary data with a stream
95
+ def set_stream_user_data: (Integer stream_id, untyped data) -> self
96
+
97
+ # Returns data associated with a stream
98
+ def get_stream_user_data: (Integer stream_id) -> untyped
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,48 @@
1
+ module HTTP3
2
+ module Nghttp3
3
+ class Error < StandardError
4
+ end
5
+
6
+ class InvalidArgumentError < Error
7
+ end
8
+
9
+ class InvalidStateError < Error
10
+ end
11
+
12
+ class WouldBlockError < Error
13
+ end
14
+
15
+ class StreamInUseError < Error
16
+ end
17
+
18
+ class MalformedHTTPHeaderError < Error
19
+ end
20
+
21
+ class MalformedHTTPMessagingError < Error
22
+ end
23
+
24
+ class QPACKFatalError < Error
25
+ end
26
+
27
+ class QPACKHeaderTooLargeError < Error
28
+ end
29
+
30
+ class StreamNotFoundError < Error
31
+ end
32
+
33
+ class ConnClosingError < Error
34
+ end
35
+
36
+ class StreamDataOverflowError < Error
37
+ end
38
+
39
+ class FatalError < Error
40
+ end
41
+
42
+ class NoMemError < Error
43
+ end
44
+
45
+ class CallbackFailureError < Error
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,9 @@
1
+ module HTTP3
2
+ module Nghttp3
3
+ class Info
4
+ def age: () -> Integer
5
+ def version_num: () -> Integer
6
+ def version_str: () -> String
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module HTTP3
2
+ module Nghttp3
3
+ class NV
4
+ def initialize: (String name, String value, ?flags: Integer) -> void
5
+
6
+ def name: () -> String
7
+ def value: () -> String
8
+ def flags: () -> Integer
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,48 @@
1
+ module HTTP3
2
+ module Nghttp3
3
+ module QPACK
4
+ class Encoder
5
+ # Creates a new QPACK encoder
6
+ def initialize: (Integer max_dtable_capacity) -> void
7
+
8
+ # Encodes headers for a given stream
9
+ def encode: (Integer stream_id, Array[NV] headers) -> { prefix: String, data: String, encoder_stream: String }
10
+
11
+ # Reads decoder stream data
12
+ def read_decoder: (String data) -> Integer
13
+
14
+ # Sets the maximum dynamic table capacity
15
+ def max_dtable_capacity=: (Integer capacity) -> Integer
16
+
17
+ # Sets the maximum number of blocked streams
18
+ def max_blocked_streams=: (Integer num) -> Integer
19
+
20
+ # Returns the number of currently blocked streams
21
+ def num_blocked_streams: () -> Integer
22
+ end
23
+
24
+ class Decoder
25
+ # Creates a new QPACK decoder
26
+ def initialize: (Integer max_dtable_capacity, Integer max_blocked_streams) -> void
27
+
28
+ # Decodes headers from a stream
29
+ def decode: (Integer stream_id, String data, ?fin: bool) -> { headers: Array[{ name: String, value: String, token: Integer }]?, blocked: bool, consumed: Integer }
30
+
31
+ # Reads encoder stream data
32
+ def read_encoder: (String data) -> Integer
33
+
34
+ # Returns data to be sent on the decoder stream
35
+ def decoder_stream_data: () -> String
36
+
37
+ # Cancels decoding for the given stream
38
+ def cancel_stream: (Integer stream_id) -> void
39
+
40
+ # Sets the maximum dynamic table capacity
41
+ def max_dtable_capacity=: (Integer capacity) -> Integer
42
+
43
+ # Returns the current insert count
44
+ def insert_count: () -> Integer
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ module HTTP3
2
+ module Nghttp3
3
+ class Settings
4
+ def self.default: () -> Settings
5
+
6
+ def initialize: () -> void
7
+
8
+ def max_field_section_size: () -> Integer
9
+ def max_field_section_size=: (Integer value) -> Integer
10
+
11
+ def qpack_max_dtable_capacity: () -> Integer
12
+ def qpack_max_dtable_capacity=: (Integer value) -> Integer
13
+
14
+ def qpack_encoder_max_dtable_capacity: () -> Integer
15
+ def qpack_encoder_max_dtable_capacity=: (Integer value) -> Integer
16
+
17
+ def qpack_blocked_streams: () -> Integer
18
+ def qpack_blocked_streams=: (Integer value) -> Integer
19
+
20
+ def enable_connect_protocol: () -> bool
21
+ def enable_connect_protocol=: (boolish value) -> boolish
22
+
23
+ def h3_datagram: () -> bool
24
+ def h3_datagram=: (boolish value) -> boolish
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,18 @@
1
+ module HTTP3
2
+ # Calls a Rack 3 application for a Request and turns its answer into a
3
+ # Response.
4
+ class RackAdapter
5
+ class InvalidResponse < StandardError
6
+ end
7
+
8
+ CONNECTION_FIELDS: Array[String]
9
+ FIELD_NAME: Regexp
10
+ FORBIDDEN_IN_VALUE: Regexp
11
+
12
+ def initialize: (untyped app) -> void
13
+
14
+ # Raises what the application raises, and InvalidResponse for an
15
+ # answer that cannot be sent; the Rack body is closed either way.
16
+ def call: (Request request, remote: Addrinfo) -> Response
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ module HTTP3
2
+ # A request as a Session put it together. headers holds [name, value]
3
+ # pairs with lowercase names, the pseudo-headers first.
4
+ class Request < Data
5
+ attr_reader stream_id: Integer
6
+ attr_reader headers: Array[[String, String]]
7
+ attr_reader body: String
8
+
9
+ def self.new: (stream_id: Integer, headers: Array[[String, String]], body: String) -> Request
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ module HTTP3
2
+ # A response for a Session to send; headers exclude pseudo-headers.
3
+ class Response < Data
4
+ attr_reader status: Integer
5
+ attr_reader headers: Array[[String, String]]
6
+ attr_reader body: Body::File | Body::Chunks | nil
7
+
8
+ def self.new: (status: Integer, headers: Array[[String, String]], body: Body::File | Body::Chunks | nil) -> Response
9
+ end
10
+
11
+ module Body
12
+ # length bytes of io from offset, read by the C part of the Session.
13
+ class File < Data
14
+ attr_reader io: ::File
15
+ attr_reader offset: Integer
16
+ attr_reader length: Integer
17
+ attr_reader rack_body: untyped
18
+
19
+ def self.new: (io: ::File, offset: Integer, length: Integer, rack_body: untyped) -> File
20
+ end
21
+
22
+ # Chunks pulled with enum.next as the send buffer drains.
23
+ class Chunks < Data
24
+ attr_reader enum: Enumerator[String, untyped]
25
+ attr_reader rack_body: untyped
26
+
27
+ def self.new: (enum: Enumerator[String, untyped], rack_body: untyped) -> Chunks
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,32 @@
1
+ module HTTP3
2
+ # An HTTP/3 server for a Rack 3 application on one UDP address. #run
3
+ # serves until SIGTERM or SIGINT, then drains; SIGHUP reloads the
4
+ # certificate and key.
5
+ class Server
6
+ ALPN: Array[String]
7
+ SIGNALS: Hash[String, String]
8
+ RECV_SIZE: Integer
9
+ RECV_BATCH: Integer
10
+
11
+ # The address #run bound; nil until then.
12
+ attr_reader local_address: Addrinfo?
13
+
14
+ def initialize: (
15
+ app: untyped,
16
+ bind: String,
17
+ certificate_path: String | _ToPath,
18
+ private_key_path: String | _ToPath,
19
+ ?transport_params: QUIC::TransportParams?,
20
+ ?settings: QUIC::Settings?,
21
+ ?qlog_dir: (String | _ToPath)?,
22
+ ?max_connections: Integer,
23
+ ?max_request_body: Integer,
24
+ ?max_field_section_size: Integer,
25
+ ?send_buffer_size: Integer,
26
+ ?shutdown_timeout: Numeric,
27
+ ?logger: Logger
28
+ ) -> void
29
+
30
+ def run: () -> nil
31
+ end
32
+ end
@@ -0,0 +1,52 @@
1
+ module HTTP3
2
+ # HTTP/3 on one QUIC connection, driven by HTTP3::Server's I/O loop. The
3
+ # on_* methods are called by the C part with the parts of each request.
4
+ class Session
5
+ # Take over quic_conn's streams. Requests over max_request_body bytes of
6
+ # body get 413, over max_field_section_size bytes of headers 431.
7
+ def self.attach: (QUIC::Connection::Server quic_conn, max_request_body: Integer, max_field_section_size: Integer) -> Session
8
+
9
+ def self._attach: (QUIC::Connection::Server quic_conn, Integer max_field_section_size) -> Session
10
+
11
+ # Finished requests, each with the status to answer it with directly
12
+ # (413, 431) or nil for the application to answer it.
13
+ def take_requests: () -> Array[[Request, Integer?]]
14
+
15
+ # Send response. Yields the exception of a file body whose descriptor
16
+ # could not be duplicated; its stream is aborted.
17
+ def respond: (Request request, Response response) ?{ (SystemCallError error) -> void } -> void
18
+
19
+ # Queue more of each Body::Chunks body, up to limit unsent bytes per
20
+ # stream. Yields the request and exception of a body that raised.
21
+ def fill_bodies: (Integer limit) ?{ (Request request, Exception error) -> void } -> void
22
+
23
+ def wants_fill?: (Integer limit) -> bool
24
+
25
+ # True while a request stream is open.
26
+ def busy?: () -> bool
27
+
28
+ # Let go of closed streams; yields a body close that raised.
29
+ def release_closed: () ?{ (Integer stream_id, Exception error) -> void } -> void
30
+
31
+ # Close every body still held.
32
+ def release_all: () ?{ (Integer stream_id, Exception error) -> void } -> void
33
+
34
+ def on_begin_headers: (Integer id) -> void
35
+ def on_header: (Integer id, String name, String value) -> void
36
+ def on_fields_too_large: (Integer id) -> void
37
+ def on_end_headers: (Integer id) -> void
38
+ def on_data: (Integer id, String bytes) -> void
39
+ def on_end_stream: (Integer id) -> void
40
+
41
+ # The C part.
42
+ def submit_response: (Integer stream_id, Array[[String, String]] headers, bool has_body) -> self
43
+ def push_chunk: (Integer stream_id, String str) -> self
44
+ def push_eof: (Integer stream_id) -> self
45
+ def attach_file: (Integer stream_id, IO io, Integer offset, Integer length) -> self
46
+ def unsent_bytes: (Integer stream_id) -> Integer
47
+ def take_closed: () -> Array[Integer]
48
+ def take_error: () -> Exception?
49
+ def shutdown_notice: () -> self
50
+ def abort_stream: (Integer stream_id, Integer app_error_code) -> self
51
+ end
52
+ end