http3 0.0.3 → 0.0.4
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 +46 -0
- data/README.md +66 -9
- data/ext/http3/nghttp3.c +20 -1
- data/ext/http3/nghttp3.h +35 -2
- data/ext/http3/nghttp3_connection.c +377 -205
- data/ext/http3/nghttp3_nv.c +53 -20
- data/ext/http3/nghttp3_qpack.c +64 -20
- data/ext/http3/session.c +197 -48
- data/lib/http3/rack_adapter.rb +141 -26
- data/lib/http3/server.rb +187 -17
- data/lib/http3/session.rb +211 -32
- data/lib/http3/version.rb +1 -1
- data/sig/http3/nghttp3/connection.rbs +43 -9
- data/sig/http3/nghttp3/nv.rbs +4 -0
- data/sig/http3/nghttp3/qpack.rbs +10 -3
- data/sig/http3/rack_adapter.rbs +17 -2
- data/sig/http3/server.rbs +6 -0
- data/sig/http3/session.rbs +52 -8
- data/sig/http3.rbs +3 -0
- metadata +3 -3
data/lib/http3/version.rb
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
module HTTP3
|
|
2
2
|
module Nghttp3
|
|
3
|
+
# A callback, a Callbacks block or a body block, runs while nghttp3 is
|
|
4
|
+
# in the middle of a call on the connection, and may call back into it
|
|
5
|
+
# only as far as nghttp3 allows there. Inside the callbacks of
|
|
6
|
+
# #read_stream, it may call #submit_request, #submit_response,
|
|
7
|
+
# #submit_info, #submit_trailers, #resume_stream, #block_stream,
|
|
8
|
+
# #unblock_stream, #shutdown_stream_read, #shutdown_stream_write,
|
|
9
|
+
# #submit_shutdown_notice, #shutdown, #set_max_client_streams_bidi and
|
|
10
|
+
# #stream_writable?; inside any other callback, none of the methods that
|
|
11
|
+
# call into nghttp3. Anything else, #close among it, raises
|
|
12
|
+
# InvalidStateError. #closed?, #server?, #client?, #pending_data and the
|
|
13
|
+
# stream user data methods call no nghttp3 and can be called anywhere.
|
|
14
|
+
#
|
|
15
|
+
# Once #read_stream or #writev_stream has raised for an error nghttp3
|
|
16
|
+
# returned, or another method for a fatal one, nghttp3 allows nothing
|
|
17
|
+
# but deleting the connection: only #close and the methods that call no
|
|
18
|
+
# nghttp3 can be called, and the rest raise InvalidStateError.
|
|
19
|
+
#
|
|
20
|
+
# What nghttp3 asserts of its callers raises instead: a stream id out of
|
|
21
|
+
# 0..2**62-1 or of the wrong kind raises ArgumentError, and a call on a
|
|
22
|
+
# connection of the wrong role, or before or after the calls nghttp3
|
|
23
|
+
# needs, InvalidStateError.
|
|
3
24
|
class Connection
|
|
4
25
|
# Creates a new client connection
|
|
5
26
|
def self.client_new: (?Settings? settings, ?Callbacks? callbacks) -> Connection
|
|
@@ -7,10 +28,12 @@ module HTTP3
|
|
|
7
28
|
# Creates a new server connection
|
|
8
29
|
def self.server_new: (?Settings? settings, ?Callbacks? callbacks) -> Connection
|
|
9
30
|
|
|
10
|
-
# Binds the control stream
|
|
31
|
+
# Binds the control stream, a unidirectional stream of this end (id % 4
|
|
32
|
+
# == 3 for a server, 2 for a client)
|
|
11
33
|
def bind_control_stream: (Integer stream_id) -> self
|
|
12
34
|
|
|
13
|
-
# Binds QPACK encoder and decoder streams
|
|
35
|
+
# Binds QPACK encoder and decoder streams, unidirectional streams of
|
|
36
|
+
# this end as for bind_control_stream
|
|
14
37
|
def bind_qpack_streams: (Integer encoder_stream_id, Integer decoder_stream_id) -> self
|
|
15
38
|
|
|
16
39
|
# Closes the connection
|
|
@@ -29,7 +52,9 @@ module HTTP3
|
|
|
29
52
|
|
|
30
53
|
# Reads data on a stream from the QUIC layer. ts is the time in
|
|
31
54
|
# nanoseconds on a monotonic clock (CLOCK_MONOTONIC by default). An
|
|
32
|
-
# exception raised by a callback comes out of here.
|
|
55
|
+
# exception raised by a callback comes out of here. A server reads the
|
|
56
|
+
# streams the client opened (even ids), a client the request streams
|
|
57
|
+
# and the server's unidirectional streams (id % 4 of 0 or 3).
|
|
33
58
|
def read_stream: (Integer stream_id, String data, ?fin: bool, ?ts: Integer?) -> Integer
|
|
34
59
|
|
|
35
60
|
# Gets stream data to send to the QUIC layer. An exception raised by a
|
|
@@ -64,7 +89,7 @@ module HTTP3
|
|
|
64
89
|
def shutdown_stream_read: (Integer stream_id) -> self
|
|
65
90
|
|
|
66
91
|
# Tells a server connection how many bidirectional streams the client
|
|
67
|
-
# may open in total
|
|
92
|
+
# may open in total. It never goes down (ArgumentError).
|
|
68
93
|
def set_max_client_streams_bidi: (Integer max_streams) -> self
|
|
69
94
|
|
|
70
95
|
# The body Strings handed to nghttp3 that the peer has not acknowledged
|
|
@@ -72,23 +97,32 @@ module HTTP3
|
|
|
72
97
|
def pending_data: (Integer stream_id) -> Array[String]
|
|
73
98
|
|
|
74
99
|
# HTTP operations
|
|
75
|
-
|
|
76
|
-
#
|
|
100
|
+
#
|
|
101
|
+
# The submit_* methods read each NV's name, value and flags as they
|
|
102
|
+
# are, without to_str or to_int, and raise ArgumentError for an
|
|
103
|
+
# element that is not an NV with String name and value and flags of
|
|
104
|
+
# 0..255; nothing is submitted then. They need #bind_qpack_streams
|
|
105
|
+
# first.
|
|
106
|
+
|
|
107
|
+
# Submits an HTTP request (client only) on a client-initiated
|
|
108
|
+
# bidirectional stream (id % 4 == 0)
|
|
77
109
|
def submit_request: (Integer stream_id, Array[NV] headers, ?body: String?) ?{ (Integer stream_id) -> (String | Symbol | nil) } -> self
|
|
78
110
|
|
|
79
111
|
# Submits an HTTP response (server only)
|
|
80
112
|
def submit_response: (Integer stream_id, Array[NV] headers, ?body: String?) ?{ (Integer stream_id) -> (String | Symbol | nil) } -> self
|
|
81
113
|
|
|
82
|
-
# Submits a 1xx informational response
|
|
114
|
+
# Submits a 1xx informational response (server only)
|
|
83
115
|
def submit_info: (Integer stream_id, Array[NV] headers) -> self
|
|
84
116
|
|
|
85
117
|
# Submits trailer headers (implicitly ends the stream)
|
|
86
118
|
def submit_trailers: (Integer stream_id, Array[NV] trailers) -> self
|
|
87
119
|
|
|
88
|
-
# Signals the intention to shut down the connection gracefully
|
|
120
|
+
# Signals the intention to shut down the connection gracefully. Needs
|
|
121
|
+
# #bind_control_stream first, and cannot follow #shutdown.
|
|
89
122
|
def submit_shutdown_notice: () -> self
|
|
90
123
|
|
|
91
|
-
# Initiates graceful shutdown
|
|
124
|
+
# Initiates graceful shutdown. Needs #bind_control_stream first, and
|
|
125
|
+
# can be called once.
|
|
92
126
|
def shutdown: () -> self
|
|
93
127
|
|
|
94
128
|
# Associates arbitrary data with a stream
|
data/sig/http3/nghttp3/nv.rbs
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
module HTTP3
|
|
2
2
|
module Nghttp3
|
|
3
3
|
class NV
|
|
4
|
+
# flags is an Integer of 0..255, the NV_FLAG_* constants or-ed
|
|
5
|
+
# together; anything else raises ArgumentError. NV_FLAG_NO_COPY_NAME
|
|
6
|
+
# and NV_FLAG_NO_COPY_VALUE are ignored, and nghttp3 copies the name
|
|
7
|
+
# and value all the same.
|
|
4
8
|
def initialize: (String name, String value, ?flags: Integer) -> void
|
|
5
9
|
|
|
6
10
|
def name: () -> String
|
data/sig/http3/nghttp3/qpack.rbs
CHANGED
|
@@ -5,7 +5,9 @@ module HTTP3
|
|
|
5
5
|
# Creates a new QPACK encoder
|
|
6
6
|
def initialize: (Integer max_dtable_capacity) -> void
|
|
7
7
|
|
|
8
|
-
# Encodes headers for a given stream
|
|
8
|
+
# Encodes headers for a given stream. headers are checked as
|
|
9
|
+
# Connection#submit_request checks them, and a stream_id out of
|
|
10
|
+
# 0..2**62-1 raises ArgumentError.
|
|
9
11
|
def encode: (Integer stream_id, Array[NV] headers) -> { prefix: String, data: String, encoder_stream: String }
|
|
10
12
|
|
|
11
13
|
# Reads decoder stream data
|
|
@@ -22,10 +24,15 @@ module HTTP3
|
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
class Decoder
|
|
27
|
+
# The most streams decoded at once: decode on a new stream past it
|
|
28
|
+
# raises InvalidStateError, until one is finished or cancelled.
|
|
29
|
+
MAX_STREAMS: Integer
|
|
30
|
+
|
|
25
31
|
# Creates a new QPACK decoder
|
|
26
32
|
def initialize: (Integer max_dtable_capacity, Integer max_blocked_streams) -> void
|
|
27
33
|
|
|
28
|
-
# Decodes headers from a stream
|
|
34
|
+
# Decodes headers from a stream. A stream_id out of 0..2**62-1 raises
|
|
35
|
+
# ArgumentError.
|
|
29
36
|
def decode: (Integer stream_id, String data, ?fin: bool) -> { headers: Array[{ name: String, value: String, token: Integer }]?, blocked: bool, consumed: Integer }
|
|
30
37
|
|
|
31
38
|
# Reads encoder stream data
|
|
@@ -34,7 +41,7 @@ module HTTP3
|
|
|
34
41
|
# Returns data to be sent on the decoder stream
|
|
35
42
|
def decoder_stream_data: () -> String
|
|
36
43
|
|
|
37
|
-
# Cancels decoding for the given stream
|
|
44
|
+
# Cancels decoding for the given stream (stream_id 0..2**62-1)
|
|
38
45
|
def cancel_stream: (Integer stream_id) -> void
|
|
39
46
|
|
|
40
47
|
# Sets the maximum dynamic table capacity
|
data/sig/http3/rack_adapter.rbs
CHANGED
|
@@ -8,11 +8,26 @@ module HTTP3
|
|
|
8
8
|
CONNECTION_FIELDS: Array[String]
|
|
9
9
|
FIELD_NAME: Regexp
|
|
10
10
|
FORBIDDEN_IN_VALUE: Regexp
|
|
11
|
+
# The schemes a request may have.
|
|
12
|
+
SCHEMES: Array[String]
|
|
13
|
+
# What :authority, or host, must look like.
|
|
14
|
+
AUTHORITY: Regexp
|
|
11
15
|
|
|
12
16
|
def initialize: (untyped app) -> void
|
|
13
17
|
|
|
14
|
-
#
|
|
15
|
-
#
|
|
18
|
+
# Answers, without calling the application, CONNECT with 501, and with
|
|
19
|
+
# 400 a request whose scheme is not http or https, whose path has a
|
|
20
|
+
# fragment, or whose authority is malformed or differs from its host
|
|
21
|
+
# field. Raises what the application raises, and InvalidResponse for an
|
|
22
|
+
# answer that cannot be sent (among them a status outside 200..599, and
|
|
23
|
+
# a content-length that is malformed or that a file or array body does
|
|
24
|
+
# not have); the Rack body is closed either way. A body read with each
|
|
25
|
+
# raises InvalidResponse from next once it passes content-length or ends
|
|
26
|
+
# short of it.
|
|
16
27
|
def call: (Request request, remote: Addrinfo) -> Response
|
|
28
|
+
|
|
29
|
+
# The 400s and 501s #call answered since the last call, as
|
|
30
|
+
# {status => count}; the count starts over.
|
|
31
|
+
def take_refusals: () -> Hash[Integer, Integer]
|
|
17
32
|
end
|
|
18
33
|
end
|
data/sig/http3/server.rbs
CHANGED
|
@@ -22,7 +22,13 @@ module HTTP3
|
|
|
22
22
|
?max_connections: Integer,
|
|
23
23
|
?max_request_body: Integer,
|
|
24
24
|
?max_field_section_size: Integer,
|
|
25
|
+
?max_connection_request_buffer: Integer,
|
|
26
|
+
?max_total_request_buffer: Integer,
|
|
25
27
|
?send_buffer_size: Integer,
|
|
28
|
+
?max_connection_send_buffer: Integer,
|
|
29
|
+
?max_total_send_buffer: Integer,
|
|
30
|
+
?request_timeout: (Integer | Float)?,
|
|
31
|
+
?refusal_log_interval: Integer | Float,
|
|
26
32
|
?shutdown_timeout: Numeric,
|
|
27
33
|
?logger: Logger
|
|
28
34
|
) -> void
|
data/sig/http3/session.rbs
CHANGED
|
@@ -2,44 +2,88 @@ module HTTP3
|
|
|
2
2
|
# HTTP/3 on one QUIC connection, driven by HTTP3::Server's I/O loop. The
|
|
3
3
|
# on_* methods are called by the C part with the parts of each request.
|
|
4
4
|
class Session
|
|
5
|
+
# What .shared_buffer returns, to be passed to .attach as it is.
|
|
6
|
+
type shared_buffer = Object
|
|
7
|
+
|
|
8
|
+
# What a field counts for besides its name and value (RFC 9114 section
|
|
9
|
+
# 4.2.2).
|
|
10
|
+
FIELD_OVERHEAD: Integer
|
|
11
|
+
|
|
5
12
|
# 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
|
-
|
|
13
|
+
# body get 413, over max_field_section_size bytes of headers 431, as
|
|
14
|
+
# soon as they pass the limit; the rest of them is not read. Requests
|
|
15
|
+
# not yet taken may hold max_connection_request_buffer bytes, and those
|
|
16
|
+
# of every Session sharing shared_buffer its limit; more gets 503. One
|
|
17
|
+
# not in full within request_timeout seconds of its headers gets 408
|
|
18
|
+
# (#expire_requests); nil is no limit.
|
|
19
|
+
def self.attach: (QUIC::Connection::Server quic_conn, max_request_body: Integer, max_field_section_size: Integer,
|
|
20
|
+
?max_connection_request_buffer: Integer, ?shared_buffer: shared_buffer?,
|
|
21
|
+
?request_timeout: (Integer | Float)?) -> Session
|
|
22
|
+
|
|
23
|
+
# A limit of limit bytes for the requests of every Session given it.
|
|
24
|
+
def self.shared_buffer: (Integer limit) -> shared_buffer
|
|
8
25
|
|
|
9
26
|
def self._attach: (QUIC::Connection::Server quic_conn, Integer max_field_section_size) -> Session
|
|
10
27
|
|
|
11
28
|
# Finished requests, each with the status to answer it with directly
|
|
12
|
-
# (413, 431) or nil for the application to answer it.
|
|
29
|
+
# (408, 413, 431, 503) or nil for the application to answer it. One
|
|
30
|
+
# answered directly comes out as soon as it is refused, with neither
|
|
31
|
+
# headers nor body.
|
|
13
32
|
def take_requests: () -> Array[[Request, Integer?]]
|
|
14
33
|
|
|
34
|
+
# Refuse with 408 the requests still coming in whose request_timeout
|
|
35
|
+
# has passed at now (CLOCK_MONOTONIC).
|
|
36
|
+
def expire_requests: (Float now) -> void
|
|
37
|
+
|
|
38
|
+
# When the next request still coming in runs out of time, or nil.
|
|
39
|
+
def next_request_deadline: () -> Float?
|
|
40
|
+
|
|
15
41
|
# Send response. Yields the exception of a file body whose descriptor
|
|
16
|
-
# could not be duplicated; its stream is aborted.
|
|
42
|
+
# could not be duplicated; its stream is aborted. Raises
|
|
43
|
+
# Nghttp3::InvalidStateError for a request already answered.
|
|
17
44
|
def respond: (Request request, Response response) ?{ (SystemCallError error) -> void } -> void
|
|
18
45
|
|
|
19
46
|
# Queue more of each Body::Chunks body, up to limit unsent bytes per
|
|
20
|
-
# stream
|
|
21
|
-
|
|
47
|
+
# stream, connection_limit on the connection, and room bytes queued now
|
|
48
|
+
# (nil for no limit); a chunk may pass a limit. Returns the bytes
|
|
49
|
+
# queued. Yields the request (its pseudo-headers only, without body)
|
|
50
|
+
# and exception of a body that raised.
|
|
51
|
+
def fill_bodies: (Integer limit, ?connection_limit: Integer?, ?room: Integer?) ?{ (Request request, Exception error) -> void } -> Integer
|
|
52
|
+
|
|
53
|
+
def wants_fill?: (Integer limit, ?connection_limit: Integer?, ?room: Integer?) -> bool
|
|
22
54
|
|
|
23
|
-
|
|
55
|
+
# Bytes of the Body::Chunks bodies queued but not taken by nghttp3 yet.
|
|
56
|
+
def unsent_total: () -> Integer
|
|
24
57
|
|
|
25
58
|
# True while a request stream is open.
|
|
26
59
|
def busy?: () -> bool
|
|
27
60
|
|
|
28
|
-
# Let go of closed streams; yields a body close that raised.
|
|
61
|
+
# Let go of closed streams; yields a body close that raised. Resets our
|
|
62
|
+
# side of the requests the client reset before their end
|
|
63
|
+
# (H3_REQUEST_INCOMPLETE).
|
|
29
64
|
def release_closed: () ?{ (Integer stream_id, Exception error) -> void } -> void
|
|
30
65
|
|
|
31
66
|
# Close every body still held.
|
|
32
67
|
def release_all: () ?{ (Integer stream_id, Exception error) -> void } -> void
|
|
33
68
|
|
|
69
|
+
# Called inside nghttp3, or inside the QUIC connection's packet
|
|
70
|
+
# handling, where submit_response, stop_reading, push_chunk, push_eof,
|
|
71
|
+
# attach_file, shutdown_notice and abort_stream (and so respond) raise
|
|
72
|
+
# Nghttp3::InvalidStateError.
|
|
34
73
|
def on_begin_headers: (Integer id) -> void
|
|
35
74
|
def on_header: (Integer id, String name, String value) -> void
|
|
36
75
|
def on_fields_too_large: (Integer id) -> void
|
|
37
76
|
def on_end_headers: (Integer id) -> void
|
|
38
77
|
def on_data: (Integer id, String bytes) -> void
|
|
39
78
|
def on_end_stream: (Integer id) -> void
|
|
79
|
+
# The client reset its side of the stream.
|
|
80
|
+
def on_reset: (Integer id) -> void
|
|
40
81
|
|
|
41
82
|
# The C part.
|
|
42
83
|
def submit_response: (Integer stream_id, Array[[String, String]] headers, bool has_body) -> self
|
|
84
|
+
# Read no more of the request on stream_id (STOP_SENDING with
|
|
85
|
+
# H3_NO_ERROR); for one answered before it has arrived in full.
|
|
86
|
+
def stop_reading: (Integer stream_id) -> self
|
|
43
87
|
def push_chunk: (Integer stream_id, String str) -> self
|
|
44
88
|
def push_eof: (Integer stream_id) -> self
|
|
45
89
|
def attach_file: (Integer stream_id, IO io, Integer offset, Integer length) -> self
|
data/sig/http3.rbs
CHANGED
|
@@ -26,6 +26,9 @@ module HTTP3
|
|
|
26
26
|
# NV flag constants
|
|
27
27
|
NV_FLAG_NONE: Integer
|
|
28
28
|
NV_FLAG_NEVER_INDEX: Integer
|
|
29
|
+
# Ignored: nghttp3 always copies the name and value. It would otherwise
|
|
30
|
+
# read them when writev_stream sends them, and nothing keeps the Strings
|
|
31
|
+
# alive, or in place, until then.
|
|
29
32
|
NV_FLAG_NO_COPY_NAME: Integer
|
|
30
33
|
NV_FLAG_NO_COPY_VALUE: Integer
|
|
31
34
|
NV_FLAG_TRY_INDEX: Integer
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: http3
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yusuke Nakamura
|
|
@@ -29,14 +29,14 @@ dependencies:
|
|
|
29
29
|
requirements:
|
|
30
30
|
- - "~>"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: 0.0.
|
|
32
|
+
version: 0.0.5
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: 0.0.
|
|
39
|
+
version: 0.0.5
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: rack
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|