quic 0.0.1 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -1
- data/LICENSE-DEPENDENCIES.txt +84 -0
- data/README.md +94 -6
- data/Rakefile +7 -0
- data/examples/doq_demo.rb +122 -0
- data/examples/echo_demo.rb +24 -14
- data/examples/echo_server_demo.rb +127 -0
- data/examples/handshake_demo.rb +18 -20
- data/examples/io_loop_demo.rb +144 -0
- data/examples/verify_demo.rb +105 -0
- data/ext/quic/connection_client.c +154 -676
- data/ext/quic/connection_common.c +1262 -0
- data/ext/quic/connection_common.h +217 -0
- data/ext/quic/connection_server.c +186 -0
- data/ext/quic/endpoint.c +823 -0
- data/ext/quic/endpoint.h +28 -0
- data/ext/quic/ext_api.c +112 -0
- data/ext/quic/extconf.rb +145 -28
- data/ext/quic/include/quic/ext.h +84 -0
- data/ext/quic/patches/picotls/0001-openssl-use-raw-public-key-api-for-X25519.patch +74 -0
- data/ext/quic/quic.c +91 -54
- data/ext/quic/quic.h +43 -33
- data/ext/quic/stream.c +137 -40
- data/ext/quic/stream.h +17 -7
- data/ext/quic/tls_context.c +260 -0
- data/ext/quic/tls_context.h +46 -0
- data/lib/quic/connection.rb +173 -28
- data/lib/quic/endpoint.rb +57 -0
- data/lib/quic/ext_include.rb +12 -0
- data/lib/quic/settings.rb +46 -3
- data/lib/quic/stream.rb +20 -16
- data/lib/quic/transport_params.rb +1 -1
- data/lib/quic/version.rb +2 -2
- data/lib/quic.rb +3 -1
- data/sig/quic.rbs +231 -13
- metadata +43 -9
data/lib/quic/settings.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
module
|
|
3
|
+
module QUIC
|
|
4
4
|
Settings = Data.define(
|
|
5
5
|
:cc_algo,
|
|
6
6
|
:initial_rtt,
|
|
@@ -8,10 +8,36 @@ module Quic
|
|
|
8
8
|
:max_stream_window,
|
|
9
9
|
:handshake_timeout,
|
|
10
10
|
:no_pmtud,
|
|
11
|
-
:alpn
|
|
11
|
+
:alpn,
|
|
12
|
+
:verify_mode,
|
|
13
|
+
:ca_file,
|
|
14
|
+
:ca_path,
|
|
15
|
+
:qlog_dir
|
|
12
16
|
)
|
|
13
17
|
|
|
14
18
|
class Settings
|
|
19
|
+
# verify_mode (:peer or :none), ca_file and ca_path control server
|
|
20
|
+
# certificate verification. They default here so that callers building a
|
|
21
|
+
# Settings without them keep working.
|
|
22
|
+
#
|
|
23
|
+
# With verify_mode :peer and both ca_file and ca_path nil, the system
|
|
24
|
+
# default store is used, and SSL_CERT_FILE / SSL_CERT_DIR are honoured.
|
|
25
|
+
# That store is loaded once per process, the first time a connection needs
|
|
26
|
+
# it; later changes to those variables or to the CA bundle are not picked
|
|
27
|
+
# up. Setting ca_file or ca_path trusts only the certificates found there.
|
|
28
|
+
# With verify_mode :none, ca_file and ca_path are ignored.
|
|
29
|
+
#
|
|
30
|
+
# Only a client uses verify_mode, ca_file and ca_path. A server ignores
|
|
31
|
+
# them, since it does not ask for a client certificate.
|
|
32
|
+
#
|
|
33
|
+
# qlog_dir (a String or Pathname) makes every connection write its qlog
|
|
34
|
+
# to <qlog_dir>/<its first source connection ID in hex>.sqlog. A file
|
|
35
|
+
# that cannot be opened when a connection is made only costs that
|
|
36
|
+
# connection's qlog.
|
|
37
|
+
def initialize(verify_mode: :peer, ca_file: nil, ca_path: nil, qlog_dir: nil, **rest)
|
|
38
|
+
super
|
|
39
|
+
end
|
|
40
|
+
|
|
15
41
|
def self.default
|
|
16
42
|
new(
|
|
17
43
|
cc_algo: :cubic,
|
|
@@ -20,8 +46,25 @@ module Quic
|
|
|
20
46
|
max_stream_window: 0,
|
|
21
47
|
handshake_timeout: 10_000_000_000,
|
|
22
48
|
no_pmtud: false,
|
|
23
|
-
alpn: []
|
|
49
|
+
alpn: [],
|
|
50
|
+
verify_mode: :peer,
|
|
51
|
+
ca_file: nil,
|
|
52
|
+
ca_path: nil,
|
|
53
|
+
qlog_dir: nil
|
|
24
54
|
)
|
|
25
55
|
end
|
|
56
|
+
|
|
57
|
+
# Raises ArgumentError unless qlog_dir is nil or a directory this
|
|
58
|
+
# process can write to. Client.new, Connection::Server.accept and
|
|
59
|
+
# Endpoint.new call it, so that a wrong qlog_dir shows up when they are
|
|
60
|
+
# set up rather than as qlog files that never appear.
|
|
61
|
+
def check_qlog_dir
|
|
62
|
+
return if qlog_dir.nil?
|
|
63
|
+
|
|
64
|
+
path = qlog_dir.respond_to?(:to_path) ? qlog_dir.to_path : qlog_dir
|
|
65
|
+
return if path.is_a?(String) && File.directory?(path) && File.writable?(path)
|
|
66
|
+
|
|
67
|
+
raise ArgumentError, "qlog_dir is not a writable directory: #{path}"
|
|
68
|
+
end
|
|
26
69
|
end
|
|
27
70
|
end
|
data/lib/quic/stream.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
module
|
|
4
|
-
#
|
|
3
|
+
module QUIC
|
|
4
|
+
# QUIC::Stream is defined in the C extension (ext/quic/stream.c); this file
|
|
5
5
|
# reopens it to add the bits that are easier to express in Ruby:
|
|
6
6
|
# #initiator (a tiny lookup over @id) and the blocking #read which wraps
|
|
7
|
-
# the C #read_nonblock with an internal
|
|
7
|
+
# the C #read_nonblock with an internal #pump_once loop on the owning
|
|
8
|
+
# connection (@client, a Client or Server).
|
|
8
9
|
class Stream
|
|
9
10
|
INITIATORS = %i[client_bidi server_bidi client_uni server_uni].freeze
|
|
10
11
|
|
|
@@ -17,35 +18,38 @@ module Quic
|
|
|
17
18
|
INITIATORS[@id & 0b11]
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
# IO#read-compatible blocking read. Requires the
|
|
21
|
-
# been #bind'ed to a socket (raised via
|
|
21
|
+
# IO#read-compatible blocking read. Requires the owning connection to
|
|
22
|
+
# have been #bind'ed to a socket (raised via its #pump_once otherwise).
|
|
22
23
|
#
|
|
23
24
|
# read(length): block until at least 1 byte is available or EOF,
|
|
24
25
|
# return up to `length` bytes, return nil at EOF.
|
|
25
26
|
# read(nil): block until EOF, return everything that was received
|
|
26
27
|
# (an empty String if nothing arrived before FIN).
|
|
28
|
+
#
|
|
29
|
+
# Both branches drain first and pump only when there is nothing to drain.
|
|
30
|
+
# #read_nonblock already distinguishes the two states we care about --
|
|
31
|
+
# QUIC::Error::WaitReadable for "nothing buffered yet" and EOFError for
|
|
32
|
+
# "nothing buffered and FIN seen" -- so the loop needs no separate
|
|
33
|
+
# predicate over @recv_buffer / #eof?.
|
|
27
34
|
def read(length = nil)
|
|
28
35
|
if length.nil?
|
|
29
|
-
@client.pump_until { eof? }
|
|
30
36
|
out = +""
|
|
31
37
|
out.force_encoding(Encoding::BINARY)
|
|
32
|
-
until eof?
|
|
33
|
-
# Should not happen given pump_until's predicate, but defensive.
|
|
34
|
-
out << read_nonblock(4096)
|
|
35
|
-
end
|
|
36
|
-
# Drain anything that arrived after the predicate was last evaluated.
|
|
37
38
|
loop do
|
|
38
39
|
out << read_nonblock(4096)
|
|
39
|
-
rescue
|
|
40
|
+
rescue QUIC::Error::WaitReadable
|
|
41
|
+
@client.pump_once
|
|
42
|
+
rescue EOFError
|
|
40
43
|
break
|
|
41
44
|
end
|
|
42
45
|
out
|
|
43
46
|
else
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
loop do
|
|
48
|
+
return read_nonblock(length)
|
|
49
|
+
rescue QUIC::Error::WaitReadable
|
|
50
|
+
@client.pump_once
|
|
47
51
|
rescue EOFError
|
|
48
|
-
nil
|
|
52
|
+
return nil
|
|
49
53
|
end
|
|
50
54
|
end
|
|
51
55
|
end
|
data/lib/quic/version.rb
CHANGED
data/lib/quic.rb
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "quic/version"
|
|
4
|
+
require_relative "quic/ext_include"
|
|
4
5
|
|
|
5
|
-
module
|
|
6
|
+
module QUIC
|
|
6
7
|
class Error < StandardError
|
|
7
8
|
attr_reader :code
|
|
8
9
|
end
|
|
@@ -12,4 +13,5 @@ require "quic/quic"
|
|
|
12
13
|
require_relative "quic/transport_params"
|
|
13
14
|
require_relative "quic/settings"
|
|
14
15
|
require_relative "quic/connection"
|
|
16
|
+
require_relative "quic/endpoint"
|
|
15
17
|
require_relative "quic/stream"
|
data/sig/quic.rbs
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
module
|
|
1
|
+
module QUIC
|
|
2
2
|
VERSION: String
|
|
3
3
|
|
|
4
|
-
|
|
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
|
+
|
|
8
|
+
def self.library_versions: () -> { ngtcp2: String, openssl: String, picotls: String }
|
|
5
9
|
|
|
6
10
|
class Error < StandardError
|
|
7
11
|
attr_reader code: Integer?
|
|
@@ -19,6 +23,15 @@ module Quic
|
|
|
19
23
|
end
|
|
20
24
|
|
|
21
25
|
class CryptoError < Error
|
|
26
|
+
# The TLS alert this endpoint sent when the handshake failed in
|
|
27
|
+
# #read_pkt, or nil. Alerts received from the peer are not recorded.
|
|
28
|
+
attr_reader tls_alert: Integer?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class CertificateVerifyFailed < CryptoError
|
|
32
|
+
# X509_V_ERR_* (comparable with OpenSSL::X509::V_ERR_*), or nil when
|
|
33
|
+
# the server sent no certificate.
|
|
34
|
+
attr_reader verify_result: Integer?
|
|
22
35
|
end
|
|
23
36
|
|
|
24
37
|
class HandshakeTimeout < Error
|
|
@@ -46,6 +59,12 @@ module Quic
|
|
|
46
59
|
|
|
47
60
|
class NotBound < Error
|
|
48
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
|
|
49
68
|
end
|
|
50
69
|
|
|
51
70
|
class TransportParams < Data
|
|
@@ -70,12 +89,63 @@ module Quic
|
|
|
70
89
|
attr_reader no_pmtud: bool
|
|
71
90
|
attr_reader alpn: Array[String]
|
|
72
91
|
|
|
92
|
+
# Client only: a server does not ask for a client certificate and
|
|
93
|
+
# ignores these three.
|
|
94
|
+
attr_reader verify_mode: :peer | :none
|
|
95
|
+
attr_reader ca_file: (String | _ToPath)?
|
|
96
|
+
attr_reader ca_path: (String | _ToPath)?
|
|
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
|
+
|
|
73
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
|
|
74
119
|
end
|
|
75
120
|
|
|
76
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
|
+
|
|
77
143
|
class Client
|
|
78
|
-
|
|
144
|
+
include Pump
|
|
145
|
+
|
|
146
|
+
attr_reader remote_address: Addrinfo
|
|
147
|
+
|
|
148
|
+
def self.new: (host: String, port: Integer, ?address_family: Symbol?, ?transport_params: TransportParams?, ?settings: Settings?) -> Client
|
|
79
149
|
|
|
80
150
|
def self._open: (
|
|
81
151
|
local_sockaddr: String,
|
|
@@ -97,14 +167,148 @@ module Quic
|
|
|
97
167
|
def in_closing_period?: () -> bool
|
|
98
168
|
def in_draining_period?: () -> bool
|
|
99
169
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
170
|
+
# Sends an application CONNECTION_CLOSE (frame type 0x1d) to the peer
|
|
171
|
+
# carrying error_code and reason, then transitions the underlying
|
|
172
|
+
# ngtcp2_conn to the closing period. Requires #bind to have been
|
|
173
|
+
# called; raises QUIC::Error::NotBound otherwise. A second #close call
|
|
174
|
+
# after the connection has already entered the closing or draining
|
|
175
|
+
# period is a no-op.
|
|
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?
|
|
105
249
|
end
|
|
106
250
|
end
|
|
107
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
|
+
|
|
108
312
|
class Stream
|
|
109
313
|
INITIATORS: Array[Symbol]
|
|
110
314
|
|
|
@@ -115,22 +319,36 @@ module Quic
|
|
|
115
319
|
def read: (?Integer? length) -> String?
|
|
116
320
|
def read_nonblock: (Integer length) -> String
|
|
117
321
|
|
|
118
|
-
# Blocks (by internally calling
|
|
119
|
-
# control window has room for the full
|
|
120
|
-
# and returns data.bytesize. Requires the
|
|
121
|
-
# 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.
|
|
122
327
|
def write: (String data, ?fin: bool) -> Integer
|
|
123
328
|
|
|
124
329
|
# Non-blocking variant. Queues as much of `data` as the current flow
|
|
125
330
|
# control window allows and returns the byte count actually queued.
|
|
126
|
-
# Raises
|
|
331
|
+
# Raises QUIC::Error::WaitWritable (mixed in with IO::WaitWritable) when
|
|
127
332
|
# the window is zero and the payload is non-empty. If only a partial
|
|
128
333
|
# prefix is accepted, `fin: true` is NOT propagated; the caller must
|
|
129
334
|
# re-issue with `fin: true` on the trailing chunk.
|
|
130
335
|
def write_nonblock: (String data, ?fin: bool) -> Integer
|
|
131
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
|
+
|
|
132
345
|
def close_write: () -> nil
|
|
133
346
|
def close_read: () -> nil
|
|
134
347
|
def close: () -> nil
|
|
348
|
+
|
|
349
|
+
# Aborts the send side with RESET_STREAM carrying error_code (default 0).
|
|
350
|
+
# After #reset, #write / #write_nonblock raise QUIC::Error::StreamClosed.
|
|
351
|
+
# The read side is unaffected. Idempotent: a second #reset is a no-op.
|
|
352
|
+
def reset: (?Integer error_code) -> nil
|
|
135
353
|
end
|
|
136
354
|
end
|
metadata
CHANGED
|
@@ -1,18 +1,34 @@
|
|
|
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.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yusuke Nakamura
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
-
dependencies:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: mini_portile2
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
description: A thin Ruby binding around ngtcp2 for the QUIC transport protocol, using
|
|
27
|
+
picotls for TLS 1.3. ngtcp2 and picotls are vendored via mini_portile2 at install
|
|
28
|
+
time and linked statically; the crypto primitives and X.509 come from the host's
|
|
29
|
+
OpenSSL (or LibreSSL), linked dynamically, so the process shares one libcrypto with
|
|
30
|
+
Ruby's openssl extension. The API is intentionally optimized for synchronous I/O
|
|
31
|
+
and String-based buffers. Public API is not yet stable.
|
|
16
32
|
email:
|
|
17
33
|
- yusuke1994525@gmail.com
|
|
18
34
|
executables: []
|
|
@@ -22,19 +38,36 @@ extra_rdoc_files: []
|
|
|
22
38
|
files:
|
|
23
39
|
- CHANGELOG.md
|
|
24
40
|
- CODE_OF_CONDUCT.md
|
|
41
|
+
- LICENSE-DEPENDENCIES.txt
|
|
25
42
|
- LICENSE.txt
|
|
26
43
|
- README.md
|
|
27
44
|
- Rakefile
|
|
45
|
+
- examples/doq_demo.rb
|
|
28
46
|
- examples/echo_demo.rb
|
|
47
|
+
- examples/echo_server_demo.rb
|
|
29
48
|
- examples/handshake_demo.rb
|
|
49
|
+
- examples/io_loop_demo.rb
|
|
50
|
+
- examples/verify_demo.rb
|
|
30
51
|
- ext/quic/connection_client.c
|
|
52
|
+
- ext/quic/connection_common.c
|
|
53
|
+
- ext/quic/connection_common.h
|
|
54
|
+
- ext/quic/connection_server.c
|
|
55
|
+
- ext/quic/endpoint.c
|
|
56
|
+
- ext/quic/endpoint.h
|
|
57
|
+
- ext/quic/ext_api.c
|
|
31
58
|
- ext/quic/extconf.rb
|
|
59
|
+
- ext/quic/include/quic/ext.h
|
|
60
|
+
- ext/quic/patches/picotls/0001-openssl-use-raw-public-key-api-for-X25519.patch
|
|
32
61
|
- ext/quic/quic.c
|
|
33
62
|
- ext/quic/quic.h
|
|
34
63
|
- ext/quic/stream.c
|
|
35
64
|
- ext/quic/stream.h
|
|
65
|
+
- ext/quic/tls_context.c
|
|
66
|
+
- ext/quic/tls_context.h
|
|
36
67
|
- lib/quic.rb
|
|
37
68
|
- lib/quic/connection.rb
|
|
69
|
+
- lib/quic/endpoint.rb
|
|
70
|
+
- lib/quic/ext_include.rb
|
|
38
71
|
- lib/quic/settings.rb
|
|
39
72
|
- lib/quic/stream.rb
|
|
40
73
|
- lib/quic/transport_params.rb
|
|
@@ -48,6 +81,7 @@ metadata:
|
|
|
48
81
|
homepage_uri: https://github.com/unasuke/quic-ruby
|
|
49
82
|
source_code_uri: https://github.com/unasuke/quic-ruby
|
|
50
83
|
changelog_uri: https://github.com/unasuke/quic-ruby/blob/main/CHANGELOG.md
|
|
84
|
+
rubygems_mfa_required: 'true'
|
|
51
85
|
rdoc_options: []
|
|
52
86
|
require_paths:
|
|
53
87
|
- lib
|
|
@@ -62,8 +96,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
62
96
|
- !ruby/object:Gem::Version
|
|
63
97
|
version: '0'
|
|
64
98
|
requirements: []
|
|
65
|
-
rubygems_version: 4.0.
|
|
99
|
+
rubygems_version: 4.0.16
|
|
66
100
|
specification_version: 4
|
|
67
|
-
summary: Thin Ruby binding around ngtcp2 for the QUIC transport protocol,
|
|
68
|
-
|
|
101
|
+
summary: Thin Ruby binding around ngtcp2 for the QUIC transport protocol, using picotls
|
|
102
|
+
for TLS 1.3 over the system OpenSSL.
|
|
69
103
|
test_files: []
|