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.
data/lib/http3/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTP3
4
- VERSION = "0.0.3"
4
+ VERSION = "0.0.4"
5
5
  end
@@ -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
- # Submits an HTTP request (client only)
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
@@ -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
@@ -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
@@ -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
- # Raises what the application raises, and InvalidResponse for an
15
- # answer that cannot be sent; the Rack body is closed either way.
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
@@ -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
- def self.attach: (QUIC::Connection::Server quic_conn, max_request_body: Integer, max_field_section_size: Integer) -> Session
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. Yields the request and exception of a body that raised.
21
- def fill_bodies: (Integer limit) ?{ (Request request, Exception error) -> void } -> void
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
- def wants_fill?: (Integer limit) -> bool
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.3
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.3
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.3
39
+ version: 0.0.5
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: rack
42
42
  requirement: !ruby/object:Gem::Requirement