quic 0.0.2 → 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.
@@ -1,39 +1,10 @@
1
- #include "quic.h"
2
- #include "stream.h"
1
+ #include "connection_common.h"
3
2
 
4
- #include <netinet/in.h>
5
3
  #include <string.h>
6
- #include <sys/socket.h>
7
- #include <time.h>
8
4
 
9
5
  #include <openssl/err.h>
10
6
  #include <openssl/rand.h>
11
7
 
12
- /* Buffer size for #write_pkt. NGTCP2_MAX_UDP_PAYLOAD_SIZE (1200) is the
13
- minimum destlen ngtcp2 accepts. Revisit when PMTUD is enabled (Phase 4+). */
14
- #define QUIC_WRITE_PKT_BUFLEN NGTCP2_MAX_UDP_PAYLOAD_SIZE
15
-
16
- /* picotls sends a key share only for the first entry; the others are offered
17
- in supported_groups and reached via HelloRetryRequest. X25519 is first so
18
- the common case stays 1-RTT. */
19
- static ptls_key_exchange_algorithm_t *quic_key_exchanges[] = {
20
- &ptls_openssl_x25519,
21
- &ptls_openssl_secp256r1,
22
- #if PTLS_OPENSSL_HAVE_SECP384R1
23
- &ptls_openssl_secp384r1,
24
- #endif
25
- NULL,
26
- };
27
-
28
- static ptls_cipher_suite_t *quic_cipher_suites[] = {
29
- &ptls_openssl_aes128gcmsha256,
30
- &ptls_openssl_aes256gcmsha384,
31
- #if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
32
- &ptls_openssl_chacha20poly1305sha256,
33
- #endif
34
- NULL,
35
- };
36
-
37
8
  /* The system default store is loaded once per process: reading the CA bundle
38
9
  takes several milliseconds. X509_STORE is reference counted and safe to
39
10
  share across verifications; creation runs under the GVL. Never freed. */
@@ -53,347 +24,6 @@ quic_get_default_store(void)
53
24
  return quic_default_store;
54
25
  }
55
26
 
56
- typedef struct {
57
- ngtcp2_conn *conn;
58
- /* Referenced by cptls.ptls, which only keeps the pointer, so this has to
59
- outlive the ptls_t. */
60
- ptls_context_t tls_ctx;
61
- /* Referenced by tls_ctx.verify_certificate when verify_mode is :peer. Holds
62
- a reference to an X509_STORE, released in quic_client_free. */
63
- ptls_openssl_verify_certificate_t verify_cert;
64
- bool verify_cert_initialized;
65
- /* Filled by quic_override_verify_cb during the handshake so that a failed
66
- read_pkt can raise CertificateVerifyFailed with the X509 error. */
67
- bool verify_failed;
68
- int verify_result; /* X509_V_ERR_*, 0 when the server sent no certificate */
69
- /* The TLS native handle handed to ngtcp2 (&cptls, not cptls.ptls). */
70
- ngtcp2_crypto_picotls_ctx cptls;
71
- /* [0]: QUIC transport params, filled in by ngtcp2; [1]: terminator. */
72
- ptls_raw_extension_t tls_exts[2];
73
- /* ALPN list handed to picotls, which keeps the pointers for the whole
74
- handshake (unlike SSL_set_alpn_protos, which copies). The entries point
75
- into alpn_buf, and both are owned by this struct. */
76
- ptls_iovec_t *alpn;
77
- size_t alpn_count;
78
- uint8_t *alpn_buf;
79
- ngtcp2_cid scid;
80
- ngtcp2_cid dcid;
81
- /* ngtcp2 1.x requires applications to associate an ngtcp2_conn with the
82
- TLS object so that crypto callbacks (e.g. add_handshake_data) can recover
83
- the conn. picotls carries it in the ptls_t's data pointer, set via
84
- ptls_get_data_ptr. The ref must outlive the ptls_t. */
85
- ngtcp2_crypto_conn_ref conn_ref;
86
- /* Back-reference to the QUIC::Connection::Client Ruby object that owns
87
- this struct. ngtcp2 stream callbacks receive a void* user_data equal
88
- to this struct, and they look up @streams via owner. We are stored
89
- INSIDE owner via TypedData_Wrap_Struct, so owner is guaranteed alive
90
- while we exist (no dmark needed). GC.compact may relocate owner, so
91
- quic_client_compact updates this field via rb_gc_location. */
92
- VALUE owner;
93
- } quic_client_t;
94
-
95
- static void
96
- quic_client_free(void *ptr)
97
- {
98
- quic_client_t *c = (quic_client_t *)ptr;
99
- if (c->conn) ngtcp2_conn_del(c->conn);
100
- if (c->cptls.ptls) {
101
- /* Safe even when configure_client_session never ran or failed midway. */
102
- ngtcp2_crypto_picotls_deconfigure_session(&c->cptls);
103
- ptls_free(c->cptls.ptls);
104
- }
105
- /* After ptls_free: the ptls_t references verify_cert through tls_ctx. */
106
- if (c->verify_cert_initialized) ptls_openssl_dispose_verify_certificate(&c->verify_cert);
107
- xfree(c->alpn);
108
- xfree(c->alpn_buf);
109
- xfree(c);
110
- }
111
-
112
- static size_t
113
- quic_client_size(const void *ptr)
114
- {
115
- (void)ptr;
116
- return sizeof(quic_client_t);
117
- }
118
-
119
- /* GC.compact may relocate the owner Client object. Follow it so the
120
- void* user_data ngtcp2 callbacks receive (which equals this struct,
121
- stored inside owner) keeps resolving @streams / @accept_queue. The
122
- raw ngtcp2/picotls pointers are outside Ruby's heap and are left alone. */
123
- static void
124
- quic_client_compact(void *ptr)
125
- {
126
- quic_client_t *c = (quic_client_t *)ptr;
127
- c->owner = rb_gc_location(c->owner);
128
- }
129
-
130
- static const rb_data_type_t quic_client_data_type = {
131
- "QUIC::Connection::Client",
132
- {NULL, quic_client_free, quic_client_size, quic_client_compact,},
133
- NULL, NULL,
134
- RUBY_TYPED_FREE_IMMEDIATELY,
135
- };
136
-
137
- static VALUE
138
- quic_client_alloc(VALUE klass)
139
- {
140
- quic_client_t *c = ALLOC(quic_client_t);
141
- memset(c, 0, sizeof(*c));
142
- return TypedData_Wrap_Struct(klass, &quic_client_data_type, c);
143
- }
144
-
145
- ngtcp2_conn *
146
- quic_client_conn(VALUE client_v)
147
- {
148
- quic_client_t *c;
149
- TypedData_Get_Struct(client_v, quic_client_t, &quic_client_data_type, c);
150
- return c->conn;
151
- }
152
-
153
- static ngtcp2_tstamp
154
- quic_now(void)
155
- {
156
- struct timespec ts;
157
- clock_gettime(CLOCK_MONOTONIC, &ts);
158
- return (ngtcp2_tstamp)ts.tv_sec * NGTCP2_SECONDS + (ngtcp2_tstamp)ts.tv_nsec;
159
- }
160
-
161
- static void
162
- quic_rand_cb(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx)
163
- {
164
- (void)rand_ctx;
165
- RAND_bytes(dest, (int)destlen);
166
- }
167
-
168
- static int
169
- quic_get_new_connection_id_cb(ngtcp2_conn *conn, ngtcp2_cid *cid,
170
- uint8_t *token, size_t cidlen, void *user_data)
171
- {
172
- (void)conn;
173
- (void)user_data;
174
- if (RAND_bytes(cid->data, (int)cidlen) != 1) {
175
- return NGTCP2_ERR_CALLBACK_FAILURE;
176
- }
177
- cid->datalen = cidlen;
178
- if (RAND_bytes(token, NGTCP2_STATELESS_RESET_TOKENLEN) != 1) {
179
- return NGTCP2_ERR_CALLBACK_FAILURE;
180
- }
181
- return 0;
182
- }
183
-
184
- static ngtcp2_conn *
185
- quic_client_get_conn(ngtcp2_crypto_conn_ref *conn_ref)
186
- {
187
- return ((quic_client_t *)conn_ref->user_data)->conn;
188
- }
189
-
190
- static void
191
- quic_require_binary(VALUE str, const char *name)
192
- {
193
- Check_Type(str, T_STRING);
194
- if (rb_enc_get_index(str) != rb_ascii8bit_encindex()) {
195
- rb_raise(rb_eArgError, "%s must be ASCII-8BIT (binary) encoding", name);
196
- }
197
- }
198
-
199
- static uint64_t
200
- quic_get_uint64_field(VALUE obj, const char *name)
201
- {
202
- VALUE v = rb_funcall(obj, rb_intern(name), 0);
203
- return (uint64_t)NUM2ULL(v);
204
- }
205
-
206
- static ngtcp2_cc_algo
207
- quic_cc_algo_from_sym(VALUE sym)
208
- {
209
- Check_Type(sym, T_SYMBOL);
210
- ID id = SYM2ID(sym);
211
- if (id == rb_intern("reno")) return NGTCP2_CC_ALGO_RENO;
212
- if (id == rb_intern("cubic")) return NGTCP2_CC_ALGO_CUBIC;
213
- if (id == rb_intern("bbr")) return NGTCP2_CC_ALGO_BBR;
214
- rb_raise(rb_eArgError, "unknown cc_algo: %"PRIsVALUE" (expected :reno, :cubic, or :bbr)", sym);
215
- }
216
-
217
- static void
218
- quic_fill_transport_params(ngtcp2_transport_params *params, VALUE tp)
219
- {
220
- ngtcp2_transport_params_default(params);
221
- params->initial_max_stream_data_bidi_local =
222
- quic_get_uint64_field(tp, "initial_max_stream_data_bidi_local");
223
- params->initial_max_stream_data_bidi_remote =
224
- quic_get_uint64_field(tp, "initial_max_stream_data_bidi_remote");
225
- params->initial_max_stream_data_uni =
226
- quic_get_uint64_field(tp, "initial_max_stream_data_uni");
227
- params->initial_max_data =
228
- quic_get_uint64_field(tp, "initial_max_data");
229
- params->initial_max_streams_bidi =
230
- quic_get_uint64_field(tp, "initial_max_streams_bidi");
231
- params->initial_max_streams_uni =
232
- quic_get_uint64_field(tp, "initial_max_streams_uni");
233
- params->max_idle_timeout =
234
- (ngtcp2_duration)quic_get_uint64_field(tp, "max_idle_timeout");
235
- params->active_connection_id_limit =
236
- quic_get_uint64_field(tp, "active_connection_id_limit");
237
- }
238
-
239
- static void
240
- quic_fill_settings(ngtcp2_settings *settings, VALUE st)
241
- {
242
- ngtcp2_settings_default(settings);
243
- settings->initial_ts = quic_now();
244
- settings->cc_algo = quic_cc_algo_from_sym(rb_funcall(st, rb_intern("cc_algo"), 0));
245
- settings->initial_rtt =
246
- (ngtcp2_duration)quic_get_uint64_field(st, "initial_rtt");
247
- settings->max_window =
248
- quic_get_uint64_field(st, "max_window");
249
- settings->max_stream_window =
250
- quic_get_uint64_field(st, "max_stream_window");
251
- settings->handshake_timeout =
252
- (ngtcp2_duration)quic_get_uint64_field(st, "handshake_timeout");
253
- VALUE no_pmtud = rb_funcall(st, rb_intern("no_pmtud"), 0);
254
- settings->no_pmtud = RTEST(no_pmtud) ? 1 : 0;
255
- }
256
-
257
- /* Shared helper: look up the QUIC::Stream registered for stream_id in the
258
- owner Client's @streams Hash. Returns Qnil if not found (which can happen
259
- if the stream was already removed by a previous stream_close callback). */
260
- static VALUE
261
- quic_client_lookup_stream(quic_client_t *c, int64_t stream_id)
262
- {
263
- VALUE streams = rb_ivar_get(c->owner, rb_intern("@streams"));
264
- return rb_hash_aref(streams, LL2NUM(stream_id));
265
- }
266
-
267
- /* ngtcp2 stream callbacks. Each returns 0 on success or
268
- NGTCP2_ERR_CALLBACK_FAILURE on Ruby-side errors. We assume rb_str_buf_cat /
269
- rb_ary_* won't raise in normal operation (allocation failures aside, which
270
- would abort the process anyway), so we don't wrap in rb_protect for now. */
271
-
272
- static int
273
- quic_stream_open_cb(ngtcp2_conn *conn, int64_t stream_id, void *user_data)
274
- {
275
- (void)conn;
276
- quic_client_t *c = (quic_client_t *)user_data;
277
- VALUE streams = rb_ivar_get(c->owner, rb_intern("@streams"));
278
- if (!NIL_P(rb_hash_aref(streams, LL2NUM(stream_id)))) {
279
- return 0; /* already known (client-initiated) */
280
- }
281
- VALUE stream = quic_stream_new(stream_id, c->owner);
282
- rb_hash_aset(streams, LL2NUM(stream_id), stream);
283
- return 0;
284
- }
285
-
286
- static int
287
- quic_recv_stream_data_cb(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
288
- uint64_t offset, const uint8_t *data, size_t datalen,
289
- void *user_data, void *stream_user_data)
290
- {
291
- (void)conn;
292
- (void)offset;
293
- (void)stream_user_data;
294
- quic_client_t *c = (quic_client_t *)user_data;
295
- VALUE stream = quic_client_lookup_stream(c, stream_id);
296
- if (NIL_P(stream)) return 0;
297
-
298
- quic_stream_t *s;
299
- TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
300
-
301
- /* Surface peer-initiated (server) streams to #accept_stream. For a client
302
- connection the low stream-id bit marks the initiator: 1 == server. Push
303
- once, on first data arrival, then mark so later data does not re-enqueue. */
304
- if (!s->accept_queued && (stream_id & 0x01)) {
305
- VALUE accept_queue = rb_ivar_get(c->owner, rb_intern("@accept_queue"));
306
- rb_ary_push(accept_queue, stream);
307
- s->accept_queued = true;
308
- }
309
-
310
- if (datalen > 0) {
311
- VALUE recv_buffer = rb_ivar_get(stream, rb_intern("@recv_buffer"));
312
- rb_str_buf_cat(recv_buffer, (const char *)data, (long)datalen);
313
- }
314
- if (flags & NGTCP2_STREAM_DATA_FLAG_FIN) {
315
- s->fin_received = true;
316
- }
317
- return 0;
318
- }
319
-
320
- static int
321
- quic_acked_stream_data_offset_cb(ngtcp2_conn *conn, int64_t stream_id,
322
- uint64_t offset, uint64_t datalen,
323
- void *user_data, void *stream_user_data)
324
- {
325
- (void)conn;
326
- (void)offset;
327
- (void)stream_user_data;
328
- quic_client_t *c = (quic_client_t *)user_data;
329
- VALUE stream = quic_client_lookup_stream(c, stream_id);
330
- if (NIL_P(stream)) return 0;
331
-
332
- quic_stream_t *s;
333
- TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
334
- s->acked_offset += datalen;
335
-
336
- /* Shift any head Chunks of @pending_chunks whose end-offset is fully
337
- covered by acked_offset. Each Chunk's start-offset in the stream is
338
- pending_shifted (after previous shifts); its end-offset is
339
- pending_shifted + RSTRING_LEN(head). */
340
- VALUE pending = rb_ivar_get(stream, rb_intern("@pending_chunks"));
341
- while (RARRAY_LEN(pending) > 0) {
342
- VALUE head = RARRAY_AREF(pending, 0);
343
- uint64_t head_end = s->pending_shifted + (uint64_t)RSTRING_LEN(head);
344
- if (head_end <= s->acked_offset) {
345
- rb_ary_shift(pending);
346
- s->pending_shifted = head_end;
347
- } else {
348
- break;
349
- }
350
- }
351
- return 0;
352
- }
353
-
354
- static int
355
- quic_stream_close_cb(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
356
- uint64_t app_error_code, void *user_data,
357
- void *stream_user_data)
358
- {
359
- (void)conn;
360
- (void)stream_user_data;
361
- quic_client_t *c = (quic_client_t *)user_data;
362
- VALUE streams = rb_ivar_get(c->owner, rb_intern("@streams"));
363
- VALUE stream = rb_hash_aref(streams, LL2NUM(stream_id));
364
- if (NIL_P(stream)) return 0;
365
-
366
- quic_stream_t *s;
367
- TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
368
- s->closed = true;
369
- if (flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET) {
370
- s->close_has_app_error_code = true;
371
- s->close_app_error_code = app_error_code;
372
- }
373
- rb_hash_delete(streams, LL2NUM(stream_id));
374
- return 0;
375
- }
376
-
377
- static int
378
- quic_stream_reset_cb(ngtcp2_conn *conn, int64_t stream_id, uint64_t final_size,
379
- uint64_t app_error_code, void *user_data,
380
- void *stream_user_data)
381
- {
382
- (void)conn;
383
- (void)final_size;
384
- (void)stream_user_data;
385
- quic_client_t *c = (quic_client_t *)user_data;
386
- VALUE stream = quic_client_lookup_stream(c, stream_id);
387
- if (NIL_P(stream)) return 0;
388
-
389
- quic_stream_t *s;
390
- TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
391
- s->reset = true;
392
- s->close_app_error_code = app_error_code;
393
- s->close_has_app_error_code = true;
394
- return 0;
395
- }
396
-
397
27
  /* Record the X509 verification result so read_pkt can raise a descriptive
398
28
  error. The verdict itself is left to picotls: ret is returned unchanged.
399
29
  Runs synchronously inside read_pkt with the GVL held; it only writes to the
@@ -407,7 +37,7 @@ quic_override_verify_cb(ptls_openssl_override_verify_certificate_t *self,
407
37
  (void)cert;
408
38
  (void)chain;
409
39
  ngtcp2_crypto_conn_ref *ref = *ptls_get_data_ptr(tls);
410
- quic_client_t *c = (quic_client_t *)ref->user_data;
40
+ quic_conn_t *c = (quic_conn_t *)ref->user_data;
411
41
  if (ret != 0) {
412
42
  c->verify_failed = true;
413
43
  c->verify_result = ossl_ret;
@@ -419,34 +49,11 @@ static ptls_openssl_override_verify_certificate_t quic_override_verify = {
419
49
  quic_override_verify_cb,
420
50
  };
421
51
 
422
- /* Validate a ca_file / ca_path setting and return it as a String, or Qnil.
423
- Accepts a String or anything responding to #to_path. Every misconfiguration
424
- of the verification settings raises ArgumentError, including a wrong type. */
425
- static VALUE
426
- quic_verify_path_value(VALUE value, const char *name)
427
- {
428
- if (NIL_P(value)) return Qnil;
429
-
430
- VALUE path = value;
431
- if (!RB_TYPE_P(path, T_STRING)) {
432
- if (rb_respond_to(path, rb_intern("to_path"))) {
433
- path = rb_funcall(path, rb_intern("to_path"), 0);
434
- }
435
- if (!RB_TYPE_P(path, T_STRING)) {
436
- rb_raise(rb_eArgError, "%s must be a String or Pathname (got %"PRIsVALUE")",
437
- name, rb_obj_class(value));
438
- }
439
- }
440
- /* Raises ArgumentError when the path contains a NUL byte. */
441
- StringValueCStr(path);
442
- return path;
443
- }
444
-
445
52
  /* Install picotls's certificate verifier on c->tls_ctx according to the
446
53
  verify_mode / ca_file / ca_path settings. Leaves verify_certificate NULL
447
54
  for verify_mode :none, which skips verification altogether. */
448
55
  static void
449
- quic_client_setup_verify(quic_client_t *c, VALUE settings_v, VALUE server_name)
56
+ quic_client_setup_verify(quic_conn_t *c, VALUE settings_v, VALUE server_name)
450
57
  {
451
58
  VALUE mode = rb_funcall(settings_v, rb_intern("verify_mode"), 0);
452
59
  /* ca_file and ca_path are not even type-checked under :none. */
@@ -455,8 +62,8 @@ quic_client_setup_verify(quic_client_t *c, VALUE settings_v, VALUE server_name)
455
62
  rb_raise(rb_eArgError, "verify_mode must be :peer or :none (got %+"PRIsVALUE")", mode);
456
63
  }
457
64
 
458
- VALUE ca_file = quic_verify_path_value(rb_funcall(settings_v, rb_intern("ca_file"), 0), "ca_file");
459
- VALUE ca_path = quic_verify_path_value(rb_funcall(settings_v, rb_intern("ca_path"), 0), "ca_path");
65
+ VALUE ca_file = quic_path_value(rb_funcall(settings_v, rb_intern("ca_file"), 0), "ca_file");
66
+ VALUE ca_path = quic_path_value(rb_funcall(settings_v, rb_intern("ca_path"), 0), "ca_path");
460
67
 
461
68
  /* An empty name makes OpenSSL clear its host list and skip the name check,
462
69
  and both SNI and the check would silently stop at an embedded NUL. */
@@ -527,48 +134,22 @@ quic_client_open(int argc, VALUE *argv, VALUE klass)
527
134
  "all keywords required: local_sockaddr, remote_sockaddr, server_name, transport_params, settings");
528
135
  }
529
136
 
530
- quic_require_binary(local_sockaddr, "local_sockaddr");
531
- quic_require_binary(remote_sockaddr, "remote_sockaddr");
137
+ quic_require_sockaddr(local_sockaddr, "local_sockaddr");
138
+ quic_require_sockaddr(remote_sockaddr, "remote_sockaddr");
532
139
  Check_Type(server_name, T_STRING);
533
140
 
534
- VALUE self = quic_client_alloc(klass);
141
+ VALUE self = quic_conn_alloc(klass);
535
142
  rb_ivar_set(self, rb_intern("@server_name"), server_name);
536
143
 
537
- quic_client_t *c;
538
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
144
+ quic_conn_t *c = quic_conn_get(self);
539
145
  c->owner = self;
146
+ c->is_server = false;
540
147
 
541
- /* Copy the ALPN list into memory owned by c: picotls keeps the pointers
542
- for the whole handshake, so ALLOCA_N would not survive. An empty list
543
- means no ALPN extension, matching the old skip-SSL_set_alpn_protos path. */
544
- VALUE alpn_ary = rb_funcall(settings_v, rb_intern("alpn"), 0);
545
- Check_Type(alpn_ary, T_ARRAY);
546
- long n_alpn = RARRAY_LEN(alpn_ary);
547
- if (n_alpn > 0) {
548
- size_t total = 0;
549
- for (long i = 0; i < n_alpn; i++) {
550
- VALUE entry = RARRAY_AREF(alpn_ary, i);
551
- Check_Type(entry, T_STRING);
552
- long len = RSTRING_LEN(entry);
553
- if (len < 1 || len > 255) {
554
- rb_raise(rb_eArgError, "alpn entry must be 1-255 bytes (got %ld)", len);
555
- }
556
- total += (size_t)len;
557
- }
558
- c->alpn_buf = ALLOC_N(uint8_t, total);
559
- c->alpn = ALLOC_N(ptls_iovec_t, n_alpn);
560
- uint8_t *p = c->alpn_buf;
561
- for (long i = 0; i < n_alpn; i++) {
562
- VALUE entry = RARRAY_AREF(alpn_ary, i);
563
- size_t len = (size_t)RSTRING_LEN(entry);
564
- memcpy(p, RSTRING_PTR(entry), len);
565
- c->alpn[i] = ptls_iovec_init(p, len);
566
- p += len;
567
- }
568
- c->alpn_count = (size_t)n_alpn;
569
- }
148
+ /* An empty list means no ALPN extension, matching the old
149
+ skip-SSL_set_alpn_protos path. */
150
+ quic_conn_copy_alpn(c, settings_v);
570
151
 
571
- /* quic_client_alloc already zeroed the struct, but calling ctx_init keeps
152
+ /* quic_conn_alloc already zeroed the struct, but calling ctx_init keeps
572
153
  the ngtcp2-side initialization explicit and the TLS setup in one place. */
573
154
  ngtcp2_crypto_picotls_ctx_init(&c->cptls);
574
155
  c->tls_ctx = (ptls_context_t){
@@ -589,11 +170,11 @@ quic_client_open(int argc, VALUE *argv, VALUE klass)
589
170
  place before it runs. */
590
171
  quic_client_setup_verify(c, settings_v, server_name);
591
172
 
592
- c->conn_ref.get_conn = quic_client_get_conn;
173
+ c->conn_ref.get_conn = quic_conn_get_conn;
593
174
  c->conn_ref.user_data = c;
594
175
 
595
- c->scid.datalen = 8;
596
- if (RAND_bytes(c->scid.data, 8) != 1) {
176
+ c->scid.datalen = QUIC_SCID_LEN;
177
+ if (RAND_bytes(c->scid.data, QUIC_SCID_LEN) != 1) {
597
178
  rb_raise(rb_eRuntimeError, "RAND_bytes(scid) failed");
598
179
  }
599
180
  c->dcid.datalen = 8;
@@ -602,28 +183,13 @@ quic_client_open(int argc, VALUE *argv, VALUE klass)
602
183
  }
603
184
 
604
185
  ngtcp2_callbacks callbacks;
605
- memset(&callbacks, 0, sizeof(callbacks));
186
+ quic_conn_fill_common_callbacks(&callbacks);
606
187
  callbacks.client_initial = ngtcp2_crypto_client_initial_cb;
607
- callbacks.recv_crypto_data = ngtcp2_crypto_recv_crypto_data_cb;
608
- callbacks.encrypt = ngtcp2_crypto_encrypt_cb;
609
- callbacks.decrypt = ngtcp2_crypto_decrypt_cb;
610
- callbacks.hp_mask = ngtcp2_crypto_hp_mask_cb;
611
188
  callbacks.recv_retry = ngtcp2_crypto_recv_retry_cb;
612
- callbacks.update_key = ngtcp2_crypto_update_key_cb;
613
- callbacks.delete_crypto_aead_ctx = ngtcp2_crypto_delete_crypto_aead_ctx_cb;
614
- callbacks.delete_crypto_cipher_ctx = ngtcp2_crypto_delete_crypto_cipher_ctx_cb;
615
- callbacks.get_path_challenge_data = ngtcp2_crypto_get_path_challenge_data_cb;
616
- callbacks.version_negotiation = ngtcp2_crypto_version_negotiation_cb;
617
- callbacks.rand = quic_rand_cb;
618
- callbacks.get_new_connection_id = quic_get_new_connection_id_cb;
619
- callbacks.stream_open = quic_stream_open_cb;
620
- callbacks.recv_stream_data = quic_recv_stream_data_cb;
621
- callbacks.acked_stream_data_offset = quic_acked_stream_data_offset_cb;
622
- callbacks.stream_close = quic_stream_close_cb;
623
- callbacks.stream_reset = quic_stream_reset_cb;
624
189
 
625
190
  ngtcp2_settings settings;
626
191
  quic_fill_settings(&settings, settings_v);
192
+ quic_conn_open_qlog(c, settings_v, &settings);
627
193
 
628
194
  ngtcp2_transport_params params;
629
195
  quic_fill_transport_params(&params, transport_params);
@@ -673,441 +239,18 @@ quic_client_open(int argc, VALUE *argv, VALUE klass)
673
239
  callback. Entries are removed in the stream_close callback. */
674
240
  rb_ivar_set(self, rb_intern("@streams"), rb_hash_new());
675
241
 
676
- /* FIFO of peer-initiated (server) streams awaiting #accept_stream. The
677
- recv_stream_data callback pushes each new server stream here once. */
242
+ /* FIFO of peer-initiated streams awaiting #accept_stream. The
243
+ recv_stream_data callback pushes each new peer stream here once. */
678
244
  rb_ivar_set(self, rb_intern("@accept_queue"), rb_ary_new());
679
245
 
680
246
  return self;
681
247
  }
682
248
 
683
- /* Find the first stream in @streams that has either pending bytes or a
684
- pending FIN to flush. Phase 4 minimum: linear scan, take the first match
685
- (no round-robin). Sets *out_datav to the byte slice to send, *out_datavcnt
686
- to 0 or 1, *out_flags to NGTCP2_WRITE_STREAM_FLAG_FIN when applicable.
687
- Returns the stream's Ruby VALUE (or Qnil if no candidate). */
688
- static VALUE
689
- quic_client_pick_send_stream(VALUE self, ngtcp2_vec *out_datav,
690
- size_t *out_datavcnt, uint32_t *out_flags,
691
- int64_t *out_stream_id)
692
- {
693
- VALUE streams = rb_ivar_get(self, rb_intern("@streams"));
694
- VALUE stream_values = rb_funcall(streams, rb_intern("values"), 0);
695
-
696
- for (long i = 0; i < RARRAY_LEN(stream_values); i++) {
697
- VALUE st = RARRAY_AREF(stream_values, i);
698
- quic_stream_t *s;
699
- TypedData_Get_Struct(st, quic_stream_t, &quic_stream_data_type, s);
700
- if (s->fin_flushed) continue;
701
-
702
- VALUE pending = rb_ivar_get(st, rb_intern("@pending_chunks"));
703
- uint64_t position = s->sent_offset - s->pending_shifted;
704
- long chunks_count = RARRAY_LEN(pending);
705
-
706
- /* Walk pending Chunks to find the one containing the next unsent byte. */
707
- long chunk_idx = -1;
708
- long offset_in_chunk = 0;
709
- for (long j = 0; j < chunks_count; j++) {
710
- VALUE chunk = RARRAY_AREF(pending, j);
711
- long clen = RSTRING_LEN(chunk);
712
- if (position < (uint64_t)clen) {
713
- chunk_idx = j;
714
- offset_in_chunk = (long)position;
715
- break;
716
- }
717
- position -= (uint64_t)clen;
718
- }
719
-
720
- if (chunk_idx >= 0) {
721
- VALUE chunk = RARRAY_AREF(pending, chunk_idx);
722
- out_datav->base = (uint8_t *)RSTRING_PTR(chunk) + offset_in_chunk;
723
- out_datav->len = (size_t)(RSTRING_LEN(chunk) - offset_in_chunk);
724
- *out_datavcnt = 1;
725
- *out_stream_id = s->stream_id;
726
- /* Attach FIN if this is the LAST chunk and we're going to send all
727
- remaining bytes of it (ngtcp2 may encode less, in which case FIN
728
- won't actually be flushed and we'll retry next call). */
729
- *out_flags = (s->fin_sent && chunk_idx == chunks_count - 1)
730
- ? NGTCP2_WRITE_STREAM_FLAG_FIN
731
- : 0;
732
- return st;
733
- }
734
-
735
- if (s->fin_sent && !s->fin_flushed) {
736
- /* No data to send but a pending FIN-only frame. */
737
- out_datav->base = NULL;
738
- out_datav->len = 0;
739
- *out_datavcnt = 0;
740
- *out_stream_id = s->stream_id;
741
- *out_flags = NGTCP2_WRITE_STREAM_FLAG_FIN;
742
- return st;
743
- }
744
- }
745
-
746
- return Qnil;
747
- }
748
-
749
- static VALUE
750
- quic_client_write_pkt(int argc, VALUE *argv, VALUE self)
751
- {
752
- VALUE buffer = Qnil;
753
- rb_scan_args(argc, argv, "01", &buffer);
754
-
755
- quic_client_t *c;
756
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
757
-
758
- if (NIL_P(buffer)) {
759
- /* rb_str_buf_new returns an ASCII-8BIT (binary) String per CRuby spec,
760
- so no explicit rb_enc_associate is required. */
761
- buffer = rb_str_buf_new(QUIC_WRITE_PKT_BUFLEN);
762
- } else {
763
- Check_Type(buffer, T_STRING);
764
- if (rb_enc_get_index(buffer) != rb_ascii8bit_encindex()) {
765
- rb_raise(rb_eArgError, "buffer must be ASCII-8BIT (binary) encoding");
766
- }
767
- if (RSTRING_LEN(buffer) < (long)QUIC_WRITE_PKT_BUFLEN) {
768
- rb_str_modify_expand(buffer, (long)QUIC_WRITE_PKT_BUFLEN - RSTRING_LEN(buffer));
769
- }
770
- }
771
-
772
- ngtcp2_path_storage path_storage;
773
- ngtcp2_path_storage_zero(&path_storage);
774
-
775
- ngtcp2_pkt_info pi;
776
- memset(&pi, 0, sizeof(pi));
777
-
778
- uint8_t *dest = (uint8_t *)RSTRING_PTR(buffer);
779
- size_t destlen = (size_t)QUIC_WRITE_PKT_BUFLEN;
780
-
781
- /* Stream-aware path: if any stream has pending bytes or a pending FIN,
782
- route the packet through ngtcp2_conn_writev_stream so the stream data
783
- gets framed alongside connection-level frames. Falls back to conn-only
784
- when no stream is queued. */
785
- ngtcp2_vec datav;
786
- size_t datavcnt = 0;
787
- uint32_t writev_flags = 0;
788
- int64_t send_stream_id = -1;
789
- VALUE selected_stream = quic_client_pick_send_stream(
790
- self, &datav, &datavcnt, &writev_flags, &send_stream_id);
791
-
792
- ngtcp2_ssize pdatalen = -1;
793
- ngtcp2_ssize n = ngtcp2_conn_writev_stream(
794
- c->conn, &path_storage.path, &pi, dest, destlen, &pdatalen,
795
- writev_flags, send_stream_id, &datav, datavcnt, quic_now());
796
-
797
- if (n < 0) {
798
- /* ngtcp2 does not partially write on failure, but truncate explicitly so
799
- that a rescued caller cannot accidentally transmit stale bytes. */
800
- rb_str_set_len(buffer, 0);
801
- quic_raise_ngtcp2_error((int)n);
802
- }
803
-
804
- /* Update the selected stream's accounting only when we actually sent
805
- bytes. pdatalen >= 0 means ngtcp2 framed that many data bytes into the
806
- packet; if 0, no data was framed (but the packet may still contain ACK
807
- or other frames). */
808
- if (!NIL_P(selected_stream) && pdatalen > 0) {
809
- quic_stream_t *s;
810
- TypedData_Get_Struct(selected_stream, quic_stream_t,
811
- &quic_stream_data_type, s);
812
- s->sent_offset += (uint64_t)pdatalen;
813
- /* FIN is only actually written when all requested data fits in the
814
- frame (see ngtcp2 docs on NGTCP2_WRITE_STREAM_FLAG_FIN). */
815
- if ((writev_flags & NGTCP2_WRITE_STREAM_FLAG_FIN) &&
816
- (size_t)pdatalen == datav.len) {
817
- s->fin_flushed = true;
818
- }
819
- } else if (!NIL_P(selected_stream) && datavcnt == 0 && n > 0 &&
820
- (writev_flags & NGTCP2_WRITE_STREAM_FLAG_FIN)) {
821
- /* FIN-only packet (no data) was successfully written. */
822
- quic_stream_t *s;
823
- TypedData_Get_Struct(selected_stream, quic_stream_t,
824
- &quic_stream_data_type, s);
825
- s->fin_flushed = true;
826
- }
827
-
828
- if (n == 0) {
829
- rb_str_set_len(buffer, 0);
830
- return Qnil;
831
- }
832
- rb_str_set_len(buffer, n);
833
- return buffer;
834
- }
835
-
836
- NORETURN(static void quic_client_raise_error(quic_client_t *c, int rv));
837
-
838
- /* Like quic_raise_ngtcp2_error, but for errors from read_pkt, where the TLS
839
- handshake runs: NGTCP2_ERR_CRYPTO carries the TLS alert this client sent,
840
- and becomes CertificateVerifyFailed when the verifier rejected the peer. */
841
- static void
842
- quic_client_raise_error(quic_client_t *c, int rv)
843
- {
844
- if (rv != NGTCP2_ERR_CRYPTO) quic_raise_ngtcp2_error(rv);
845
-
846
- /* 0 means "not set" to ngtcp2; close_notify (0) is never sent for a
847
- verification failure, so report it as nil. */
848
- uint8_t alert = ngtcp2_conn_get_tls_alert(c->conn);
849
- VALUE exc;
850
- if (c->verify_failed) {
851
- VALUE msg = c->verify_result
852
- ? rb_sprintf("certificate verify failed (%s)",
853
- X509_verify_cert_error_string(c->verify_result))
854
- : rb_str_new_cstr("certificate verify failed (server sent no certificate)");
855
- exc = rb_exc_new_str(rb_eQUICErrorCertificateVerifyFailed, msg);
856
- rb_ivar_set(exc, rb_intern("@verify_result"),
857
- c->verify_result ? INT2NUM(c->verify_result) : Qnil);
858
- } else {
859
- exc = rb_exc_new_cstr(rb_eQUICErrorCryptoError, ngtcp2_strerror(rv));
860
- }
861
- rb_ivar_set(exc, rb_intern("@code"), INT2NUM(rv));
862
- rb_ivar_set(exc, rb_intern("@tls_alert"), alert ? INT2NUM(alert) : Qnil);
863
- rb_exc_raise(exc);
864
- }
865
-
866
- struct quic_read_pkt_args {
867
- quic_client_t *c;
868
- VALUE packet;
869
- ngtcp2_path path;
870
- ngtcp2_pkt_info pi;
871
- };
872
-
873
- static VALUE
874
- quic_read_pkt_body(VALUE arg)
875
- {
876
- struct quic_read_pkt_args *a = (struct quic_read_pkt_args *)arg;
877
- int rv = ngtcp2_conn_read_pkt(a->c->conn, &a->path, &a->pi,
878
- (const uint8_t *)RSTRING_PTR(a->packet),
879
- (size_t)RSTRING_LEN(a->packet),
880
- quic_now());
881
- if (rv != 0) {
882
- /* NORETURN: longjmp passes through rb_ensure so unlock still fires. */
883
- quic_client_raise_error(a->c, rv);
884
- }
885
- return Qnil;
886
- }
887
-
888
- static VALUE
889
- quic_read_pkt_unlock(VALUE arg)
890
- {
891
- rb_str_unlocktmp(((struct quic_read_pkt_args *)arg)->packet);
892
- return Qnil;
893
- }
894
-
895
- static VALUE
896
- quic_client_read_pkt(int argc, VALUE *argv, VALUE self)
897
- {
898
- VALUE packet = Qnil;
899
- VALUE opts = Qnil;
900
- rb_scan_args(argc, argv, "1:", &packet, &opts);
901
-
902
- if (NIL_P(opts)) {
903
- rb_raise(rb_eArgError, "missing keywords: local_sockaddr, remote_sockaddr");
904
- }
905
-
906
- VALUE local_sockaddr = rb_hash_aref(opts, ID2SYM(rb_intern("local_sockaddr")));
907
- VALUE remote_sockaddr = rb_hash_aref(opts, ID2SYM(rb_intern("remote_sockaddr")));
908
-
909
- if (NIL_P(local_sockaddr) || NIL_P(remote_sockaddr)) {
910
- rb_raise(rb_eArgError, "missing keywords: local_sockaddr, remote_sockaddr");
911
- }
912
-
913
- quic_require_binary(packet, "packet");
914
- quic_require_binary(local_sockaddr, "local_sockaddr");
915
- quic_require_binary(remote_sockaddr, "remote_sockaddr");
916
-
917
- quic_client_t *c;
918
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
919
-
920
- struct quic_read_pkt_args args;
921
- args.c = c;
922
- args.packet = packet;
923
- args.path.local.addr = (struct sockaddr *)RSTRING_PTR(local_sockaddr);
924
- args.path.local.addrlen = (ngtcp2_socklen)RSTRING_LEN(local_sockaddr);
925
- args.path.remote.addr = (struct sockaddr *)RSTRING_PTR(remote_sockaddr);
926
- args.path.remote.addrlen = (ngtcp2_socklen)RSTRING_LEN(remote_sockaddr);
927
- args.path.user_data = NULL;
928
- memset(&args.pi, 0, sizeof(args.pi));
929
-
930
- rb_str_locktmp(packet);
931
- return rb_ensure(quic_read_pkt_body, (VALUE)&args,
932
- quic_read_pkt_unlock, (VALUE)&args);
933
- }
934
-
935
- static VALUE
936
- quic_client_handshake_completed_p(VALUE self)
937
- {
938
- quic_client_t *c;
939
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
940
- return ngtcp2_conn_get_handshake_completed(c->conn) ? Qtrue : Qfalse;
941
- }
942
-
943
- static VALUE
944
- quic_client_in_closing_period_p(VALUE self)
945
- {
946
- quic_client_t *c;
947
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
948
- return ngtcp2_conn_in_closing_period(c->conn) ? Qtrue : Qfalse;
949
- }
950
-
951
- static VALUE
952
- quic_client_in_draining_period_p(VALUE self)
953
- {
954
- quic_client_t *c;
955
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
956
- return ngtcp2_conn_in_draining_period(c->conn) ? Qtrue : Qfalse;
957
- }
958
-
959
- static VALUE
960
- quic_client_expiry(VALUE self)
961
- {
962
- quic_client_t *c;
963
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
964
- ngtcp2_tstamp t = ngtcp2_conn_get_expiry(c->conn);
965
- if (t == UINT64_MAX) return Qnil;
966
- return ULL2NUM((unsigned long long)t);
967
- }
968
-
969
- /* Caller is responsible for ordering with #read_pkt / #write_pkt:
970
- read_pkt or handle_expiry to advance ngtcp2 state, then write_pkt to flush. */
971
- static VALUE
972
- quic_client_handle_expiry(VALUE self)
973
- {
974
- quic_client_t *c;
975
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
976
- int rv = ngtcp2_conn_handle_expiry(c->conn, quic_now());
977
- if (rv != 0) quic_raise_ngtcp2_error(rv);
978
- return Qnil;
979
- }
980
-
981
- static VALUE
982
- quic_client_open_bidi_stream(VALUE self)
983
- {
984
- quic_client_t *c;
985
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
986
-
987
- int64_t stream_id;
988
- int rv = ngtcp2_conn_open_bidi_stream(c->conn, &stream_id, NULL);
989
- if (rv != 0) quic_raise_ngtcp2_error(rv);
990
-
991
- VALUE stream = quic_stream_new(stream_id, self);
992
- VALUE streams = rb_ivar_get(self, rb_intern("@streams"));
993
- rb_hash_aset(streams, LL2NUM(stream_id), stream);
994
- return stream;
995
- }
996
-
997
- static VALUE
998
- quic_client_open_uni_stream(VALUE self)
999
- {
1000
- quic_client_t *c;
1001
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
1002
-
1003
- int64_t stream_id;
1004
- int rv = ngtcp2_conn_open_uni_stream(c->conn, &stream_id, NULL);
1005
- if (rv != 0) quic_raise_ngtcp2_error(rv);
1006
-
1007
- VALUE stream = quic_stream_new(stream_id, self);
1008
- VALUE streams = rb_ivar_get(self, rb_intern("@streams"));
1009
- rb_hash_aset(streams, LL2NUM(stream_id), stream);
1010
- return stream;
1011
- }
1012
-
1013
- /* Send an application CONNECTION_CLOSE (frame type 0x1d) to the peer and
1014
- transition the underlying ngtcp2_conn to the closing period. One UDP
1015
- datagram is written to the bound socket; ngtcp2 handles closing-period
1016
- retransmits via the caller's pump loop. Calling #close on a connection
1017
- that is already in the closing or draining period is a no-op (= returns
1018
- nil without doing any I/O). */
1019
- static VALUE
1020
- quic_client_close_m(int argc, VALUE *argv, VALUE self)
1021
- {
1022
- VALUE opts = Qnil;
1023
- rb_scan_args(argc, argv, "0:", &opts);
1024
-
1025
- uint64_t error_code = 0;
1026
- const uint8_t *reason_ptr = (const uint8_t *)"";
1027
- size_t reason_len = 0;
1028
-
1029
- if (!NIL_P(opts)) {
1030
- VALUE error_code_v = rb_hash_aref(opts, ID2SYM(rb_intern("error_code")));
1031
- VALUE reason_v = rb_hash_aref(opts, ID2SYM(rb_intern("reason")));
1032
- if (!NIL_P(error_code_v)) {
1033
- error_code = NUM2ULL(error_code_v);
1034
- }
1035
- if (!NIL_P(reason_v)) {
1036
- Check_Type(reason_v, T_STRING);
1037
- reason_ptr = (const uint8_t *)RSTRING_PTR(reason_v);
1038
- reason_len = (size_t)RSTRING_LEN(reason_v);
1039
- }
1040
- }
1041
-
1042
- quic_client_t *c;
1043
- TypedData_Get_Struct(self, quic_client_t, &quic_client_data_type, c);
1044
-
1045
- /* Idempotent: a subsequent #close after the conn already entered the
1046
- closing/draining period does not emit another CONNECTION_CLOSE. */
1047
- if (ngtcp2_conn_in_closing_period(c->conn) ||
1048
- ngtcp2_conn_in_draining_period(c->conn)) {
1049
- return Qnil;
1050
- }
1051
-
1052
- VALUE sock = rb_ivar_get(self, rb_intern("@sock"));
1053
- if (NIL_P(sock)) {
1054
- rb_raise(rb_eQUICErrorNotBound,
1055
- "QUIC::Connection::Client#bind(sock) has not been called");
1056
- }
1057
-
1058
- ngtcp2_ccerr ccerr;
1059
- ngtcp2_ccerr_set_application_error(&ccerr, error_code,
1060
- reason_len > 0 ? reason_ptr : NULL,
1061
- reason_len);
1062
-
1063
- ngtcp2_path_storage path_storage;
1064
- ngtcp2_path_storage_zero(&path_storage);
1065
- ngtcp2_pkt_info pi;
1066
- memset(&pi, 0, sizeof(pi));
1067
-
1068
- VALUE buffer = rb_str_buf_new(QUIC_WRITE_PKT_BUFLEN);
1069
- uint8_t *dest = (uint8_t *)RSTRING_PTR(buffer);
1070
-
1071
- ngtcp2_ssize n = ngtcp2_conn_write_connection_close(
1072
- c->conn, &path_storage.path, &pi, dest,
1073
- (size_t)QUIC_WRITE_PKT_BUFLEN, &ccerr, quic_now());
1074
-
1075
- /* Pre-handshake or otherwise not in a state where CONNECTION_CLOSE can be
1076
- emitted (ngtcp2 returns NGTCP2_ERR_INVALID_STATE). Spec treats this as
1077
- best-effort: silently no-op rather than surface an error. The Ruby
1078
- Client is effectively dead from here. */
1079
- if (n == NGTCP2_ERR_INVALID_STATE) {
1080
- return Qnil;
1081
- }
1082
-
1083
- if (n < 0) {
1084
- rb_str_set_len(buffer, 0);
1085
- quic_raise_ngtcp2_error((int)n);
1086
- }
1087
-
1088
- if (n == 0) {
1089
- return Qnil;
1090
- }
1091
-
1092
- rb_str_set_len(buffer, n);
1093
- rb_funcall(sock, rb_intern("send"), 2, buffer, INT2NUM(0));
1094
- return Qnil;
1095
- }
1096
-
1097
249
  void
1098
250
  Init_quic_connection_client(VALUE rb_mQUICConnectionArg)
1099
251
  {
1100
252
  rb_cQUICConnectionClient = rb_define_class_under(rb_mQUICConnectionArg, "Client", rb_cObject);
1101
- rb_define_alloc_func(rb_cQUICConnectionClient, quic_client_alloc);
253
+ rb_define_alloc_func(rb_cQUICConnectionClient, quic_conn_alloc);
1102
254
  rb_define_singleton_method(rb_cQUICConnectionClient, "_open", quic_client_open, -1);
1103
- rb_define_method(rb_cQUICConnectionClient, "write_pkt", quic_client_write_pkt, -1);
1104
- rb_define_method(rb_cQUICConnectionClient, "read_pkt", quic_client_read_pkt, -1);
1105
- rb_define_method(rb_cQUICConnectionClient, "expiry", quic_client_expiry, 0);
1106
- rb_define_method(rb_cQUICConnectionClient, "handle_expiry", quic_client_handle_expiry, 0);
1107
- rb_define_method(rb_cQUICConnectionClient, "handshake_completed?", quic_client_handshake_completed_p, 0);
1108
- rb_define_method(rb_cQUICConnectionClient, "in_closing_period?", quic_client_in_closing_period_p, 0);
1109
- rb_define_method(rb_cQUICConnectionClient, "in_draining_period?", quic_client_in_draining_period_p, 0);
1110
- rb_define_method(rb_cQUICConnectionClient, "open_bidi_stream", quic_client_open_bidi_stream, 0);
1111
- rb_define_method(rb_cQUICConnectionClient, "open_uni_stream", quic_client_open_uni_stream, 0);
1112
- rb_define_method(rb_cQUICConnectionClient, "close", quic_client_close_m, -1);
255
+ quic_conn_define_shared_methods(rb_cQUICConnectionClient);
1113
256
  }