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.
data/lib/quic/settings.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Quic
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 Quic
4
- # Quic::Stream is defined in the C extension (ext/quic/stream.c); this file
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 Client#pump_until loop.
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 parent Client to have
21
- # been #bind'ed to a socket (raised via Client#pump_once otherwise).
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 Quic::Error::WaitReadable, EOFError
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
- @client.pump_until { @recv_buffer.bytesize > 0 || @fin_received }
45
- begin
46
- read_nonblock(length)
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Quic
3
+ module QUIC
4
4
  TransportParams = Data.define(
5
5
  :initial_max_stream_data_bidi_local,
6
6
  :initial_max_stream_data_bidi_remote,
data/lib/quic/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Quic
4
- VERSION = "0.0.1"
3
+ module QUIC
4
+ VERSION = "0.0.3"
5
5
  end
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 Quic
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 Quic
1
+ module QUIC
2
2
  VERSION: String
3
3
 
4
- def self.library_versions: () -> { ngtcp2: String, openssl: String }
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
- def self.new: (host: String, port: Integer, ?transport_params: TransportParams?, ?settings: Settings?) -> Client
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
- def bind: (untyped sock) -> Client
101
- def bound_socket: () -> untyped
102
- def run: () -> void
103
- def pump_once: (?timeout: Float?) -> void
104
- def pump_until: () { () -> boolish } -> void
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 Client#pump_once) until the peer's flow
119
- # control window has room for the full payload, then queues all bytes
120
- # and returns data.bytesize. Requires the parent Client to be bound to
121
- # a socket via Client#bind; raises Quic::Error::NotBound otherwise.
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 Quic::Error::WaitWritable (mixed in with IO::WaitWritable) when
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.1
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
- description: A thin Ruby binding around ngtcp2 for the QUIC transport protocol, with
13
- LibreSSL as the TLS backend. Both are vendored via mini_portile2 at install time;
14
- no system libraries required. The API is intentionally optimized for synchronous
15
- I/O and String-based buffers. Public API is not yet stable.
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.10
99
+ rubygems_version: 4.0.16
66
100
  specification_version: 4
67
- summary: Thin Ruby binding around ngtcp2 for the QUIC transport protocol, with LibreSSL
68
- as the TLS backend.
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: []