quic 0.0.2 → 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/.clang-format +1 -0
- data/.git-blame-ignore-revs +2 -0
- data/CHANGELOG.md +33 -0
- data/LICENSE-DEPENDENCIES.txt +2 -19
- data/README.md +101 -2
- data/Rakefile +7 -0
- data/examples/demo_certificates.rb +67 -0
- data/examples/echo_demo.rb +5 -6
- data/examples/echo_endpoint_demo.rb +170 -0
- data/examples/echo_server_demo.rb +71 -0
- data/ext/quic/connection_client.c +95 -934
- data/ext/quic/connection_common.c +1255 -0
- data/ext/quic/connection_common.h +226 -0
- data/ext/quic/connection_server.c +205 -0
- data/ext/quic/endpoint.c +829 -0
- data/ext/quic/endpoint.h +31 -0
- data/ext/quic/ext_api.c +106 -0
- data/ext/quic/extconf.rb +12 -5
- data/ext/quic/include/quic/ext.h +91 -0
- data/ext/quic/quic.c +95 -62
- data/ext/quic/quic.h +12 -10
- data/ext/quic/stream.c +124 -82
- data/ext/quic/stream.h +18 -12
- data/ext/quic/tls_context.c +269 -0
- data/ext/quic/tls_context.h +47 -0
- data/lib/quic/connection.rb +142 -57
- data/lib/quic/endpoint.rb +57 -0
- data/lib/quic/ext_include.rb +12 -0
- data/lib/quic/settings.rb +26 -3
- data/lib/quic/stream.rb +4 -3
- data/lib/quic/version.rb +1 -1
- data/lib/quic.rb +2 -0
- data/sig/quic.rbs +203 -22
- metadata +17 -1
data/sig/quic.rbs
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
module QUIC
|
|
2
2
|
VERSION: String
|
|
3
3
|
|
|
4
|
+
# The directory holding quic/ext.h, the C function table other native
|
|
5
|
+
# extensions build against. Defined without loading the extension.
|
|
6
|
+
EXT_INCLUDE_DIR: String
|
|
7
|
+
|
|
4
8
|
def self.library_versions: () -> { ngtcp2: String, openssl: String, picotls: String }
|
|
5
9
|
|
|
6
10
|
class Error < StandardError
|
|
@@ -19,7 +23,7 @@ module QUIC
|
|
|
19
23
|
end
|
|
20
24
|
|
|
21
25
|
class CryptoError < Error
|
|
22
|
-
# The TLS alert this
|
|
26
|
+
# The TLS alert this endpoint sent when the handshake failed in
|
|
23
27
|
# #read_pkt, or nil. Alerts received from the peer are not recorded.
|
|
24
28
|
attr_reader tls_alert: Integer?
|
|
25
29
|
end
|
|
@@ -55,6 +59,12 @@ module QUIC
|
|
|
55
59
|
|
|
56
60
|
class NotBound < Error
|
|
57
61
|
end
|
|
62
|
+
|
|
63
|
+
# Raised by #open_bidi_stream, #open_uni_stream, #accept_stream and
|
|
64
|
+
# #accept_stream_nonblock of a connection whose streams belong to a C
|
|
65
|
+
# stream handler registered through quic/ext.h.
|
|
66
|
+
class InvalidState < Error
|
|
67
|
+
end
|
|
58
68
|
end
|
|
59
69
|
|
|
60
70
|
class TransportParams < Data
|
|
@@ -78,15 +88,61 @@ module QUIC
|
|
|
78
88
|
attr_reader handshake_timeout: Integer
|
|
79
89
|
attr_reader no_pmtud: bool
|
|
80
90
|
attr_reader alpn: Array[String]
|
|
91
|
+
|
|
92
|
+
# Client only: a server does not ask for a client certificate and
|
|
93
|
+
# ignores these three.
|
|
81
94
|
attr_reader verify_mode: :peer | :none
|
|
82
95
|
attr_reader ca_file: (String | _ToPath)?
|
|
83
96
|
attr_reader ca_path: (String | _ToPath)?
|
|
84
97
|
|
|
98
|
+
# Every connection writes its qlog to <qlog_dir>/<its first source
|
|
99
|
+
# connection ID in hex>.sqlog. nil (the default) writes none. A file
|
|
100
|
+
# that cannot be opened when a connection is made only costs that
|
|
101
|
+
# connection's qlog.
|
|
102
|
+
attr_reader qlog_dir: (String | _ToPath)?
|
|
103
|
+
|
|
85
104
|
def self.default: () -> Settings
|
|
105
|
+
|
|
106
|
+
# Raises ArgumentError unless qlog_dir is nil or a directory this
|
|
107
|
+
# process can write to. Client.new, Connection::Server.accept and
|
|
108
|
+
# Endpoint.new call it.
|
|
109
|
+
def check_qlog_dir: () -> nil
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# A server's certificate chain, private key and ALPN protocols (in order
|
|
113
|
+
# of preference), read once and shared by every connection made from it.
|
|
114
|
+
# A connection keeps the context it started with even after this object
|
|
115
|
+
# is gone. Only .server makes one; a misconfiguration raises
|
|
116
|
+
# ArgumentError naming what could not be loaded or used.
|
|
117
|
+
class TLSContext
|
|
118
|
+
def self.server: (certificate_path: String | _ToPath, private_key_path: String | _ToPath, alpn: Array[String]) -> TLSContext
|
|
86
119
|
end
|
|
87
120
|
|
|
88
121
|
module Connection
|
|
122
|
+
# The I/O loop shared by Client and Server. An internal implementation
|
|
123
|
+
# detail: not meant to be included in other classes.
|
|
124
|
+
module Pump
|
|
125
|
+
def bind: (untyped sock) -> self
|
|
126
|
+
def bound_socket: () -> untyped
|
|
127
|
+
def run: () -> void
|
|
128
|
+
def pump_once: (?timeout: Float?) -> void
|
|
129
|
+
def pump_until: () { () -> boolish } -> void
|
|
130
|
+
|
|
131
|
+
# Pops the next peer-initiated stream off the accept queue, driving
|
|
132
|
+
# the I/O loop until one arrives. timeout: nil blocks forever
|
|
133
|
+
# (requires #bind), timeout: 0 returns immediately, timeout: Numeric
|
|
134
|
+
# blocks up to that many seconds. Returns nil on timeout. Raises
|
|
135
|
+
# QUIC::Error::InvalidState when a C stream handler owns the streams.
|
|
136
|
+
def accept_stream: (?timeout: Numeric?) -> Stream?
|
|
137
|
+
|
|
138
|
+
# Non-blocking #accept_stream. Returns the next queued peer-initiated
|
|
139
|
+
# stream, or raises QUIC::Error::WaitReadable when the queue is empty.
|
|
140
|
+
def accept_stream_nonblock: () -> Stream
|
|
141
|
+
end
|
|
142
|
+
|
|
89
143
|
class Client
|
|
144
|
+
include Pump
|
|
145
|
+
|
|
90
146
|
attr_reader remote_address: Addrinfo
|
|
91
147
|
|
|
92
148
|
def self.new: (host: String, port: Integer, ?address_family: Symbol?, ?transport_params: TransportParams?, ?settings: Settings?) -> Client
|
|
@@ -111,32 +167,148 @@ module QUIC
|
|
|
111
167
|
def in_closing_period?: () -> bool
|
|
112
168
|
def in_draining_period?: () -> bool
|
|
113
169
|
|
|
114
|
-
def bind: (untyped sock) -> Client
|
|
115
|
-
def bound_socket: () -> untyped
|
|
116
|
-
def run: () -> void
|
|
117
|
-
def pump_once: (?timeout: Float?) -> void
|
|
118
|
-
def pump_until: () { () -> boolish } -> void
|
|
119
|
-
|
|
120
|
-
# Pops the next peer-initiated (server) stream off the accept queue,
|
|
121
|
-
# driving the I/O loop until one arrives. timeout: nil blocks forever
|
|
122
|
-
# (requires #bind), timeout: 0 returns immediately, timeout: Numeric
|
|
123
|
-
# blocks up to that many seconds. Returns nil on timeout.
|
|
124
|
-
def accept_stream: (?timeout: Numeric?) -> Stream?
|
|
125
|
-
|
|
126
|
-
# Non-blocking #accept_stream. Returns the next queued server stream,
|
|
127
|
-
# or raises QUIC::Error::WaitReadable when the queue is empty.
|
|
128
|
-
def accept_stream_nonblock: () -> Stream
|
|
129
|
-
|
|
130
170
|
# Sends an application CONNECTION_CLOSE (frame type 0x1d) to the peer
|
|
131
171
|
# carrying error_code and reason, then transitions the underlying
|
|
132
|
-
# ngtcp2_conn to the closing period. Requires
|
|
172
|
+
# ngtcp2_conn to the closing period. Requires #bind to have been
|
|
133
173
|
# called; raises QUIC::Error::NotBound otherwise. A second #close call
|
|
134
174
|
# after the connection has already entered the closing or draining
|
|
135
175
|
# period is a no-op.
|
|
136
176
|
def close: (?error_code: Integer, ?reason: String) -> nil
|
|
177
|
+
|
|
178
|
+
# Always nil: only a connection an Endpoint made has a closing reason.
|
|
179
|
+
def error: () -> Exception?
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
class Server
|
|
183
|
+
include Pump
|
|
184
|
+
|
|
185
|
+
attr_reader remote_address: Addrinfo
|
|
186
|
+
|
|
187
|
+
# Waits for the first datagram on sock (bound to the listen address,
|
|
188
|
+
# not connected), connects sock to its sender, and returns a Server
|
|
189
|
+
# for that one client; call #run to complete the handshake.
|
|
190
|
+
# - The wait has no timeout; close sock from another thread to give up.
|
|
191
|
+
# - If anything fails after the first datagram arrived, sock stays
|
|
192
|
+
# connected to its sender: discard it and accept on a new socket.
|
|
193
|
+
# - settings must name at least one ALPN protocol; leaving it out
|
|
194
|
+
# raises ArgumentError, since QUIC::Settings.default has none.
|
|
195
|
+
# - The certificate file is PEM, the leaf certificate first and then
|
|
196
|
+
# any intermediates.
|
|
197
|
+
def self.accept: (
|
|
198
|
+
sock: untyped,
|
|
199
|
+
certificate_path: String | _ToPath,
|
|
200
|
+
private_key_path: String | _ToPath,
|
|
201
|
+
?transport_params: TransportParams?,
|
|
202
|
+
?settings: Settings?
|
|
203
|
+
) -> Server
|
|
204
|
+
|
|
205
|
+
# Builds a server connection from initial_packet, the first datagram a
|
|
206
|
+
# client sent; feed that datagram to #read_pkt afterwards. The
|
|
207
|
+
# certificate file is PEM, the leaf certificate first and then any
|
|
208
|
+
# intermediates. settings.alpn must not be empty (ArgumentError).
|
|
209
|
+
# A first packet that cannot start a connection raises
|
|
210
|
+
# QUIC::Error::Proto.
|
|
211
|
+
def self._accept: (
|
|
212
|
+
initial_packet: String,
|
|
213
|
+
local_sockaddr: String,
|
|
214
|
+
remote_sockaddr: String,
|
|
215
|
+
certificate_path: String | _ToPath,
|
|
216
|
+
private_key_path: String | _ToPath,
|
|
217
|
+
transport_params: TransportParams,
|
|
218
|
+
settings: Settings
|
|
219
|
+
) -> Server
|
|
220
|
+
|
|
221
|
+
def open_bidi_stream: () -> Stream
|
|
222
|
+
def open_uni_stream: () -> Stream
|
|
223
|
+
|
|
224
|
+
def write_pkt: (?String? buffer) -> String?
|
|
225
|
+
def read_pkt: (String packet, local_sockaddr: String, remote_sockaddr: String) -> nil
|
|
226
|
+
|
|
227
|
+
def expiry: () -> Integer?
|
|
228
|
+
def handle_expiry: () -> nil
|
|
229
|
+
def handshake_completed?: () -> bool
|
|
230
|
+
def in_closing_period?: () -> bool
|
|
231
|
+
def in_draining_period?: () -> bool
|
|
232
|
+
|
|
233
|
+
# Sends an application CONNECTION_CLOSE (frame type 0x1d) to the peer
|
|
234
|
+
# carrying error_code and reason, then transitions the underlying
|
|
235
|
+
# ngtcp2_conn to the closing period. Requires #bind to have been
|
|
236
|
+
# called; raises QUIC::Error::NotBound otherwise. A connection an
|
|
237
|
+
# Endpoint made needs no #bind: the CONNECTION_CLOSE goes out through
|
|
238
|
+
# Endpoint#write_pkt. A second #close call after the connection has
|
|
239
|
+
# already entered the closing or draining period, or after it left
|
|
240
|
+
# its Endpoint, is a no-op.
|
|
241
|
+
def close: (?error_code: Integer, ?reason: String) -> nil
|
|
242
|
+
|
|
243
|
+
# Why the Endpoint closed this connection: nil when the peer closed it,
|
|
244
|
+
# it timed out idle or #close was called; the exception itself when
|
|
245
|
+
# Ruby code in a callback raised; otherwise a QUIC::Error made (not
|
|
246
|
+
# raised) from ngtcp2's error code. Always nil for a connection no
|
|
247
|
+
# Endpoint made.
|
|
248
|
+
def error: () -> Exception?
|
|
137
249
|
end
|
|
138
250
|
end
|
|
139
251
|
|
|
252
|
+
# Many server connections on one UDP socket, which must not be connected
|
|
253
|
+
# and must be bound to the address it serves. The Endpoint routes each
|
|
254
|
+
# datagram to its connection, makes connections for new clients and
|
|
255
|
+
# answers what no connection can (Version Negotiation, Stateless Reset,
|
|
256
|
+
# CONNECTION_REFUSED); the socket and the I/O loop stay with the caller.
|
|
257
|
+
# A connection's failure never raises from these methods; it closes that
|
|
258
|
+
# connection alone (see Connection::Server#error). The connections have
|
|
259
|
+
# no socket, so only their non-blocking methods work.
|
|
260
|
+
class Endpoint
|
|
261
|
+
attr_reader local_address: Addrinfo
|
|
262
|
+
|
|
263
|
+
# tls_context's ALPN list is the server's; settings.alpn is not read.
|
|
264
|
+
# max_connections counts closing and draining connections too.
|
|
265
|
+
def self.new: (
|
|
266
|
+
local_address: Addrinfo,
|
|
267
|
+
tls_context: TLSContext,
|
|
268
|
+
?transport_params: TransportParams?,
|
|
269
|
+
?settings: Settings?,
|
|
270
|
+
?max_connections: Integer
|
|
271
|
+
) -> Endpoint
|
|
272
|
+
|
|
273
|
+
def self._new: (
|
|
274
|
+
local_sockaddr: String,
|
|
275
|
+
tls_context: TLSContext,
|
|
276
|
+
transport_params: TransportParams,
|
|
277
|
+
settings: Settings,
|
|
278
|
+
max_connections: Integer
|
|
279
|
+
) -> Endpoint
|
|
280
|
+
|
|
281
|
+
# Routes one datagram from remote (an AF_INET or AF_INET6 sockaddr, as
|
|
282
|
+
# Addrinfo#to_sockaddr makes; ArgumentError otherwise); local defaults
|
|
283
|
+
# to local_address. Returns the connection it went to, which may be a
|
|
284
|
+
# new one, or nil when the Endpoint answered or dropped it.
|
|
285
|
+
def read_pkt: (String packet, remote: String, ?local: String?) -> Connection::Server?
|
|
286
|
+
|
|
287
|
+
# The next datagram to send and its destination sockaddr, or nil when
|
|
288
|
+
# there is none left: the Endpoint's own answers first (at most 1024
|
|
289
|
+
# wait; any beyond are dropped), then each connection's packets in turn.
|
|
290
|
+
def write_pkt: () -> [String, String]?
|
|
291
|
+
|
|
292
|
+
# The earliest CLOCK_MONOTONIC time (ns) #handle_expiry is due, or nil.
|
|
293
|
+
def expiry: () -> Integer?
|
|
294
|
+
|
|
295
|
+
# Runs the timers that are due and takes out of the table the
|
|
296
|
+
# connections that are finished.
|
|
297
|
+
def handle_expiry: () -> nil
|
|
298
|
+
|
|
299
|
+
# The connections that left the table since the last call.
|
|
300
|
+
def take_closed: () -> Array[Connection::Server]
|
|
301
|
+
|
|
302
|
+
# The connections in the table, closing and draining ones included.
|
|
303
|
+
def connections: () -> Array[Connection::Server]
|
|
304
|
+
|
|
305
|
+
# Refuses every new connection from now on with CONNECTION_REFUSED.
|
|
306
|
+
def stop_accepting: () -> nil
|
|
307
|
+
|
|
308
|
+
# The context new connections are made with; existing ones keep theirs.
|
|
309
|
+
def tls_context=: (TLSContext) -> TLSContext
|
|
310
|
+
end
|
|
311
|
+
|
|
140
312
|
class Stream
|
|
141
313
|
INITIATORS: Array[Symbol]
|
|
142
314
|
|
|
@@ -147,10 +319,11 @@ module QUIC
|
|
|
147
319
|
def read: (?Integer? length) -> String?
|
|
148
320
|
def read_nonblock: (Integer length) -> String
|
|
149
321
|
|
|
150
|
-
# Blocks (by internally calling
|
|
151
|
-
# control window has room for the full
|
|
152
|
-
# and returns data.bytesize. Requires the
|
|
153
|
-
# a socket via
|
|
322
|
+
# Blocks (by internally calling #pump_once on the owning Client or
|
|
323
|
+
# Server) until the peer's flow control window has room for the full
|
|
324
|
+
# payload, then queues all bytes and returns data.bytesize. Requires the
|
|
325
|
+
# owning connection to be bound to a socket via #bind; raises
|
|
326
|
+
# QUIC::Error::NotBound otherwise.
|
|
154
327
|
def write: (String data, ?fin: bool) -> Integer
|
|
155
328
|
|
|
156
329
|
# Non-blocking variant. Queues as much of `data` as the current flow
|
|
@@ -161,6 +334,14 @@ module QUIC
|
|
|
161
334
|
# re-issue with `fin: true` on the trailing chunk.
|
|
162
335
|
def write_nonblock: (String data, ?fin: bool) -> Integer
|
|
163
336
|
def eof?: () -> bool
|
|
337
|
+
|
|
338
|
+
# True once the stream is fully closed: both directions have finished
|
|
339
|
+
# (FIN or reset) and everything sent has been acknowledged. Stays false
|
|
340
|
+
# if the connection closes first, since ngtcp2 does not report stream
|
|
341
|
+
# closure then. Keeps returning true after the stream has left the
|
|
342
|
+
# connection. A bare Stream is never closed.
|
|
343
|
+
def closed?: () -> bool
|
|
344
|
+
|
|
164
345
|
def close_write: () -> nil
|
|
165
346
|
def close_read: () -> nil
|
|
166
347
|
def close: () -> nil
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: quic
|
|
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
|
|
@@ -36,26 +36,42 @@ extensions:
|
|
|
36
36
|
- ext/quic/extconf.rb
|
|
37
37
|
extra_rdoc_files: []
|
|
38
38
|
files:
|
|
39
|
+
- ".clang-format"
|
|
40
|
+
- ".git-blame-ignore-revs"
|
|
39
41
|
- CHANGELOG.md
|
|
40
42
|
- CODE_OF_CONDUCT.md
|
|
41
43
|
- LICENSE-DEPENDENCIES.txt
|
|
42
44
|
- LICENSE.txt
|
|
43
45
|
- README.md
|
|
44
46
|
- Rakefile
|
|
47
|
+
- examples/demo_certificates.rb
|
|
45
48
|
- examples/doq_demo.rb
|
|
46
49
|
- examples/echo_demo.rb
|
|
50
|
+
- examples/echo_endpoint_demo.rb
|
|
51
|
+
- examples/echo_server_demo.rb
|
|
47
52
|
- examples/handshake_demo.rb
|
|
48
53
|
- examples/io_loop_demo.rb
|
|
49
54
|
- examples/verify_demo.rb
|
|
50
55
|
- ext/quic/connection_client.c
|
|
56
|
+
- ext/quic/connection_common.c
|
|
57
|
+
- ext/quic/connection_common.h
|
|
58
|
+
- ext/quic/connection_server.c
|
|
59
|
+
- ext/quic/endpoint.c
|
|
60
|
+
- ext/quic/endpoint.h
|
|
61
|
+
- ext/quic/ext_api.c
|
|
51
62
|
- ext/quic/extconf.rb
|
|
63
|
+
- ext/quic/include/quic/ext.h
|
|
52
64
|
- ext/quic/patches/picotls/0001-openssl-use-raw-public-key-api-for-X25519.patch
|
|
53
65
|
- ext/quic/quic.c
|
|
54
66
|
- ext/quic/quic.h
|
|
55
67
|
- ext/quic/stream.c
|
|
56
68
|
- ext/quic/stream.h
|
|
69
|
+
- ext/quic/tls_context.c
|
|
70
|
+
- ext/quic/tls_context.h
|
|
57
71
|
- lib/quic.rb
|
|
58
72
|
- lib/quic/connection.rb
|
|
73
|
+
- lib/quic/endpoint.rb
|
|
74
|
+
- lib/quic/ext_include.rb
|
|
59
75
|
- lib/quic/settings.rb
|
|
60
76
|
- lib/quic/stream.rb
|
|
61
77
|
- lib/quic/transport_params.rb
|