quic 0.0.3 → 0.0.5
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 +15 -0
- data/README.md +76 -2
- data/examples/demo_certificates.rb +67 -0
- data/examples/echo_endpoint_demo.rb +170 -0
- data/examples/echo_server_demo.rb +2 -58
- data/ext/quic/connection_client.c +77 -59
- data/ext/quic/connection_common.c +423 -377
- data/ext/quic/connection_common.h +20 -11
- data/ext/quic/connection_server.c +67 -48
- data/ext/quic/endpoint.c +206 -200
- data/ext/quic/endpoint.h +8 -5
- data/ext/quic/ext_api.c +41 -47
- data/ext/quic/include/quic/ext.h +26 -19
- data/ext/quic/quic.c +75 -67
- data/ext/quic/quic.h +4 -2
- data/ext/quic/stream.c +80 -80
- data/ext/quic/stream.h +11 -5
- data/ext/quic/tls_context.c +79 -70
- data/ext/quic/tls_context.h +2 -1
- data/lib/quic/version.rb +1 -1
- data/sig/quic.rbs +8 -4
- metadata +5 -1
|
@@ -13,31 +13,30 @@
|
|
|
13
13
|
in supported_groups and reached via HelloRetryRequest. X25519 is first so
|
|
14
14
|
the common case stays 1-RTT. */
|
|
15
15
|
ptls_key_exchange_algorithm_t *quic_key_exchanges[] = {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
&ptls_openssl_x25519,
|
|
17
|
+
&ptls_openssl_secp256r1,
|
|
18
18
|
#if PTLS_OPENSSL_HAVE_SECP384R1
|
|
19
|
-
|
|
19
|
+
&ptls_openssl_secp384r1,
|
|
20
20
|
#endif
|
|
21
|
-
|
|
21
|
+
NULL,
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
ptls_cipher_suite_t *quic_cipher_suites[] = {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
&ptls_openssl_aes128gcmsha256,
|
|
26
|
+
&ptls_openssl_aes256gcmsha384,
|
|
27
27
|
#if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
|
|
28
|
-
|
|
28
|
+
&ptls_openssl_chacha20poly1305sha256,
|
|
29
29
|
#endif
|
|
30
|
-
|
|
30
|
+
NULL,
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
static void
|
|
34
|
-
quic_conn_free(void *ptr)
|
|
35
|
-
{
|
|
33
|
+
static void quic_conn_free(void *ptr) {
|
|
36
34
|
quic_conn_t *c = (quic_conn_t *)ptr;
|
|
37
35
|
/* The Endpoint may be swept in the same GC cycle and freed first; nothing
|
|
38
36
|
from here on may reach its CID table. */
|
|
39
37
|
c->endpoint = NULL;
|
|
40
|
-
if (c->conn)
|
|
38
|
+
if (c->conn)
|
|
39
|
+
ngtcp2_conn_del(c->conn);
|
|
41
40
|
if (c->cptls.ptls) {
|
|
42
41
|
/* Safe even when configure_client_session never ran or failed midway. */
|
|
43
42
|
ngtcp2_crypto_picotls_deconfigure_session(&c->cptls);
|
|
@@ -45,26 +44,25 @@ quic_conn_free(void *ptr)
|
|
|
45
44
|
}
|
|
46
45
|
/* After ptls_free: the ptls_t references verify_cert through tls_ctx, or
|
|
47
46
|
the shared context on a server. */
|
|
48
|
-
if (c->verify_cert_initialized)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
if (c->verify_cert_initialized)
|
|
48
|
+
ptls_openssl_dispose_verify_certificate(&c->verify_cert);
|
|
49
|
+
if (c->tls != NULL)
|
|
50
|
+
quic_tls_ctx_unref(c->tls);
|
|
51
|
+
/* Still open only if no ngtcp2_conn was made to finish it; the
|
|
52
|
+
constructors close it before raising, so this is a safety net. */
|
|
53
|
+
quic_conn_close_qlog(c);
|
|
52
54
|
xfree(c->alpn);
|
|
53
55
|
xfree(c->alpn_buf);
|
|
54
56
|
xfree(c);
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
static size_t
|
|
58
|
-
quic_conn_size(const void *ptr)
|
|
59
|
-
{
|
|
59
|
+
static size_t quic_conn_size(const void *ptr) {
|
|
60
60
|
(void)ptr;
|
|
61
61
|
return sizeof(quic_conn_t);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/* owner is the object wrapping this struct, so it needs no mark. */
|
|
65
|
-
static void
|
|
66
|
-
quic_conn_mark(void *ptr)
|
|
67
|
-
{
|
|
65
|
+
static void quic_conn_mark(void *ptr) {
|
|
68
66
|
quic_conn_t *c = (quic_conn_t *)ptr;
|
|
69
67
|
rb_gc_mark_movable(c->pending_exc);
|
|
70
68
|
rb_gc_mark_movable(c->handler_owner);
|
|
@@ -77,9 +75,7 @@ quic_conn_mark(void *ptr)
|
|
|
77
75
|
void* user_data ngtcp2 callbacks receive (which equals this struct,
|
|
78
76
|
stored inside owner) keeps resolving @streams / @accept_queue. The
|
|
79
77
|
raw ngtcp2/picotls pointers are outside Ruby's heap and are left alone. */
|
|
80
|
-
static void
|
|
81
|
-
quic_conn_compact(void *ptr)
|
|
82
|
-
{
|
|
78
|
+
static void quic_conn_compact(void *ptr) {
|
|
83
79
|
quic_conn_t *c = (quic_conn_t *)ptr;
|
|
84
80
|
c->owner = rb_gc_location(c->owner);
|
|
85
81
|
c->pending_exc = rb_gc_location(c->pending_exc);
|
|
@@ -90,15 +86,20 @@ quic_conn_compact(void *ptr)
|
|
|
90
86
|
}
|
|
91
87
|
|
|
92
88
|
const rb_data_type_t quic_conn_data_type = {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
"QUIC::Connection",
|
|
90
|
+
{
|
|
91
|
+
quic_conn_mark,
|
|
92
|
+
quic_conn_free,
|
|
93
|
+
quic_conn_size,
|
|
94
|
+
quic_conn_compact,
|
|
95
|
+
},
|
|
96
|
+
NULL,
|
|
97
|
+
NULL,
|
|
98
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
97
99
|
};
|
|
98
100
|
|
|
99
101
|
VALUE
|
|
100
|
-
quic_conn_alloc(VALUE klass)
|
|
101
|
-
{
|
|
102
|
+
quic_conn_alloc(VALUE klass) {
|
|
102
103
|
quic_conn_t *c = ALLOC(quic_conn_t);
|
|
103
104
|
memset(c, 0, sizeof(*c));
|
|
104
105
|
c->pending_exc = Qnil;
|
|
@@ -112,10 +113,9 @@ quic_conn_alloc(VALUE klass)
|
|
|
112
113
|
/* Re-raise the exception a callback left in c->pending_exc, if any. Called
|
|
113
114
|
right after ngtcp2 returns, before its return value is looked at: the
|
|
114
115
|
NGTCP2_ERR_CALLBACK_FAILURE it reports is the result of that exception. */
|
|
115
|
-
static void
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (NIL_P(c->pending_exc)) return;
|
|
116
|
+
static void quic_conn_raise_pending(quic_conn_t *c) {
|
|
117
|
+
if (NIL_P(c->pending_exc))
|
|
118
|
+
return;
|
|
119
119
|
VALUE exc = c->pending_exc;
|
|
120
120
|
c->pending_exc = Qnil;
|
|
121
121
|
rb_exc_raise(exc);
|
|
@@ -125,42 +125,33 @@ quic_conn_raise_pending(quic_conn_t *c)
|
|
|
125
125
|
(the first one wins; ngtcp2 stops at the first failing callback anyway)
|
|
126
126
|
and turned into NGTCP2_ERR_CALLBACK_FAILURE, so that it never unwinds
|
|
127
127
|
through ngtcp2. */
|
|
128
|
-
static int
|
|
129
|
-
quic_conn_protect(quic_conn_t *c, VALUE (*body)(VALUE), void *arg)
|
|
130
|
-
{
|
|
128
|
+
static int quic_conn_protect(quic_conn_t *c, VALUE (*body)(VALUE), void *arg) {
|
|
131
129
|
int state = 0;
|
|
132
130
|
rb_protect(body, (VALUE)arg, &state);
|
|
133
|
-
if (state == 0)
|
|
134
|
-
|
|
131
|
+
if (state == 0)
|
|
132
|
+
return 0;
|
|
133
|
+
if (NIL_P(c->pending_exc))
|
|
134
|
+
c->pending_exc = rb_errinfo();
|
|
135
135
|
rb_set_errinfo(Qnil);
|
|
136
136
|
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
quic_conn_t *
|
|
140
|
-
quic_conn_get(VALUE self)
|
|
141
|
-
{
|
|
139
|
+
quic_conn_t *quic_conn_get(VALUE self) {
|
|
142
140
|
quic_conn_t *c;
|
|
143
141
|
TypedData_Get_Struct(self, quic_conn_t, &quic_conn_data_type, c);
|
|
144
142
|
return c;
|
|
145
143
|
}
|
|
146
144
|
|
|
147
|
-
ngtcp2_conn *
|
|
148
|
-
quic_conn_ptr(VALUE owner)
|
|
149
|
-
{
|
|
150
|
-
return quic_conn_get(owner)->conn;
|
|
151
|
-
}
|
|
145
|
+
ngtcp2_conn *quic_conn_ptr(VALUE owner) { return quic_conn_get(owner)->conn; }
|
|
152
146
|
|
|
153
|
-
ngtcp2_tstamp
|
|
154
|
-
quic_now(void)
|
|
155
|
-
{
|
|
147
|
+
ngtcp2_tstamp quic_now(void) {
|
|
156
148
|
struct timespec ts;
|
|
157
149
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
158
150
|
return (ngtcp2_tstamp)ts.tv_sec * NGTCP2_SECONDS + (ngtcp2_tstamp)ts.tv_nsec;
|
|
159
151
|
}
|
|
160
152
|
|
|
161
|
-
static void
|
|
162
|
-
|
|
163
|
-
{
|
|
153
|
+
static void quic_rand_cb(uint8_t *dest, size_t destlen,
|
|
154
|
+
const ngtcp2_rand_ctx *rand_ctx) {
|
|
164
155
|
(void)rand_ctx;
|
|
165
156
|
RAND_bytes(dest, (int)destlen);
|
|
166
157
|
}
|
|
@@ -168,11 +159,9 @@ quic_rand_cb(uint8_t *dest, size_t destlen, const ngtcp2_rand_ctx *rand_ctx)
|
|
|
168
159
|
/* A connection an Endpoint holds derives the token from the Endpoint's
|
|
169
160
|
secret and enters the new CID in its table. Any other connection never
|
|
170
161
|
sends a Stateless Reset, so a random token does. */
|
|
171
|
-
static int
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
void *user_data)
|
|
175
|
-
{
|
|
162
|
+
static int quic_get_new_connection_id_cb(ngtcp2_conn *conn, ngtcp2_cid *cid,
|
|
163
|
+
ngtcp2_stateless_reset_token *token,
|
|
164
|
+
size_t cidlen, void *user_data) {
|
|
176
165
|
(void)conn;
|
|
177
166
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
178
167
|
if (RAND_bytes(cid->data, (int)cidlen) != 1) {
|
|
@@ -181,7 +170,8 @@ quic_get_new_connection_id_cb(ngtcp2_conn *conn, ngtcp2_cid *cid,
|
|
|
181
170
|
cid->datalen = cidlen;
|
|
182
171
|
if (c->endpoint == NULL) {
|
|
183
172
|
return RAND_bytes(token->data, NGTCP2_STATELESS_RESET_TOKENLEN) == 1
|
|
184
|
-
|
|
173
|
+
? 0
|
|
174
|
+
: NGTCP2_ERR_CALLBACK_FAILURE;
|
|
185
175
|
}
|
|
186
176
|
if (quic_endpoint_generate_reset_token(c->endpoint, token->data, cid) != 0) {
|
|
187
177
|
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
@@ -190,142 +180,142 @@ quic_get_new_connection_id_cb(ngtcp2_conn *conn, ngtcp2_cid *cid,
|
|
|
190
180
|
return 0;
|
|
191
181
|
}
|
|
192
182
|
|
|
193
|
-
static int
|
|
194
|
-
|
|
195
|
-
{
|
|
183
|
+
static int quic_remove_connection_id_cb(ngtcp2_conn *conn,
|
|
184
|
+
const ngtcp2_cid *cid,
|
|
185
|
+
void *user_data) {
|
|
196
186
|
(void)conn;
|
|
197
187
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
198
|
-
if (c->endpoint != NULL)
|
|
188
|
+
if (c->endpoint != NULL)
|
|
189
|
+
quic_endpoint_dissociate_cid(c->endpoint, cid, c->owner);
|
|
199
190
|
return 0;
|
|
200
191
|
}
|
|
201
192
|
|
|
202
|
-
ngtcp2_conn *
|
|
203
|
-
quic_conn_get_conn(ngtcp2_crypto_conn_ref *conn_ref)
|
|
204
|
-
{
|
|
193
|
+
ngtcp2_conn *quic_conn_get_conn(ngtcp2_crypto_conn_ref *conn_ref) {
|
|
205
194
|
return ((quic_conn_t *)conn_ref->user_data)->conn;
|
|
206
195
|
}
|
|
207
196
|
|
|
208
|
-
void
|
|
209
|
-
quic_require_binary(VALUE str, const char *name)
|
|
210
|
-
{
|
|
197
|
+
void quic_require_binary(VALUE str, const char *name) {
|
|
211
198
|
Check_Type(str, T_STRING);
|
|
212
199
|
if (rb_enc_get_index(str) != rb_ascii8bit_encindex()) {
|
|
213
200
|
rb_raise(rb_eArgError, "%s must be ASCII-8BIT (binary) encoding", name);
|
|
214
201
|
}
|
|
215
202
|
}
|
|
216
203
|
|
|
217
|
-
void
|
|
218
|
-
quic_require_sockaddr(VALUE str, const char *name)
|
|
219
|
-
{
|
|
204
|
+
void quic_require_sockaddr(VALUE str, const char *name) {
|
|
220
205
|
quic_require_binary(str, name);
|
|
221
206
|
|
|
222
207
|
long len = RSTRING_LEN(str);
|
|
223
208
|
sa_family_t family = AF_UNSPEC;
|
|
224
209
|
if (len >= (long)(offsetof(struct sockaddr, sa_family) + sizeof(family))) {
|
|
225
|
-
memcpy(&family, RSTRING_PTR(str) + offsetof(struct sockaddr, sa_family),
|
|
210
|
+
memcpy(&family, RSTRING_PTR(str) + offsetof(struct sockaddr, sa_family),
|
|
211
|
+
sizeof(family));
|
|
226
212
|
}
|
|
227
213
|
if ((family == AF_INET && len == (long)sizeof(struct sockaddr_in)) ||
|
|
228
214
|
(family == AF_INET6 && len == (long)sizeof(struct sockaddr_in6))) {
|
|
229
215
|
return;
|
|
230
216
|
}
|
|
231
|
-
rb_raise(rb_eArgError,
|
|
217
|
+
rb_raise(rb_eArgError,
|
|
218
|
+
"%s must be an AF_INET or AF_INET6 sockaddr (Addrinfo#to_sockaddr)",
|
|
219
|
+
name);
|
|
232
220
|
}
|
|
233
221
|
|
|
234
|
-
static uint64_t
|
|
235
|
-
quic_get_uint64_field(VALUE obj, const char *name)
|
|
236
|
-
{
|
|
222
|
+
static uint64_t quic_get_uint64_field(VALUE obj, const char *name) {
|
|
237
223
|
VALUE v = rb_funcall(obj, rb_intern(name), 0);
|
|
238
224
|
return (uint64_t)NUM2ULL(v);
|
|
239
225
|
}
|
|
240
226
|
|
|
241
|
-
static ngtcp2_cc_algo
|
|
242
|
-
quic_cc_algo_from_sym(VALUE sym)
|
|
243
|
-
{
|
|
227
|
+
static ngtcp2_cc_algo quic_cc_algo_from_sym(VALUE sym) {
|
|
244
228
|
Check_Type(sym, T_SYMBOL);
|
|
245
229
|
ID id = SYM2ID(sym);
|
|
246
|
-
if (id == rb_intern("reno"))
|
|
247
|
-
|
|
248
|
-
if (id == rb_intern("
|
|
249
|
-
|
|
230
|
+
if (id == rb_intern("reno"))
|
|
231
|
+
return NGTCP2_CC_ALGO_RENO;
|
|
232
|
+
if (id == rb_intern("cubic"))
|
|
233
|
+
return NGTCP2_CC_ALGO_CUBIC;
|
|
234
|
+
if (id == rb_intern("bbr"))
|
|
235
|
+
return NGTCP2_CC_ALGO_BBR;
|
|
236
|
+
rb_raise(rb_eArgError,
|
|
237
|
+
"unknown cc_algo: %" PRIsVALUE " (expected :reno, :cubic, or :bbr)",
|
|
238
|
+
sym);
|
|
250
239
|
}
|
|
251
240
|
|
|
252
|
-
void
|
|
253
|
-
quic_fill_transport_params(ngtcp2_transport_params *params, VALUE tp)
|
|
254
|
-
{
|
|
241
|
+
void quic_fill_transport_params(ngtcp2_transport_params *params, VALUE tp) {
|
|
255
242
|
ngtcp2_transport_params_default(params);
|
|
256
243
|
params->initial_max_stream_data_bidi_local =
|
|
257
|
-
|
|
244
|
+
quic_get_uint64_field(tp, "initial_max_stream_data_bidi_local");
|
|
258
245
|
params->initial_max_stream_data_bidi_remote =
|
|
259
|
-
|
|
246
|
+
quic_get_uint64_field(tp, "initial_max_stream_data_bidi_remote");
|
|
260
247
|
params->initial_max_stream_data_uni =
|
|
261
|
-
|
|
262
|
-
params->initial_max_data =
|
|
263
|
-
quic_get_uint64_field(tp, "initial_max_data");
|
|
248
|
+
quic_get_uint64_field(tp, "initial_max_stream_data_uni");
|
|
249
|
+
params->initial_max_data = quic_get_uint64_field(tp, "initial_max_data");
|
|
264
250
|
params->initial_max_streams_bidi =
|
|
265
|
-
|
|
251
|
+
quic_get_uint64_field(tp, "initial_max_streams_bidi");
|
|
266
252
|
params->initial_max_streams_uni =
|
|
267
|
-
|
|
253
|
+
quic_get_uint64_field(tp, "initial_max_streams_uni");
|
|
268
254
|
params->max_idle_timeout =
|
|
269
|
-
|
|
255
|
+
(ngtcp2_duration)quic_get_uint64_field(tp, "max_idle_timeout");
|
|
270
256
|
params->active_connection_id_limit =
|
|
271
|
-
|
|
257
|
+
quic_get_uint64_field(tp, "active_connection_id_limit");
|
|
272
258
|
}
|
|
273
259
|
|
|
274
|
-
void
|
|
275
|
-
quic_fill_settings(ngtcp2_settings *settings, VALUE st)
|
|
276
|
-
{
|
|
260
|
+
void quic_fill_settings(ngtcp2_settings *settings, VALUE st) {
|
|
277
261
|
ngtcp2_settings_default(settings);
|
|
278
262
|
settings->initial_ts = quic_now();
|
|
279
|
-
settings->cc_algo =
|
|
263
|
+
settings->cc_algo =
|
|
264
|
+
quic_cc_algo_from_sym(rb_funcall(st, rb_intern("cc_algo"), 0));
|
|
280
265
|
settings->initial_rtt =
|
|
281
|
-
|
|
282
|
-
settings->max_window =
|
|
283
|
-
|
|
284
|
-
settings->max_stream_window =
|
|
285
|
-
quic_get_uint64_field(st, "max_stream_window");
|
|
266
|
+
(ngtcp2_duration)quic_get_uint64_field(st, "initial_rtt");
|
|
267
|
+
settings->max_window = quic_get_uint64_field(st, "max_window");
|
|
268
|
+
settings->max_stream_window = quic_get_uint64_field(st, "max_stream_window");
|
|
286
269
|
settings->handshake_timeout =
|
|
287
|
-
|
|
270
|
+
(ngtcp2_duration)quic_get_uint64_field(st, "handshake_timeout");
|
|
288
271
|
VALUE no_pmtud = rb_funcall(st, rb_intern("no_pmtud"), 0);
|
|
289
272
|
settings->no_pmtud = RTEST(no_pmtud) ? 1 : 0;
|
|
290
273
|
}
|
|
291
274
|
|
|
292
275
|
/* ngtcp2_settings.qlog_write. Touches no Ruby object, so it needs no
|
|
293
276
|
rb_protect. A write error only costs qlog. */
|
|
294
|
-
static void
|
|
295
|
-
|
|
296
|
-
{
|
|
277
|
+
static void quic_qlog_write_cb(void *user_data, uint32_t flags,
|
|
278
|
+
const void *data, size_t datalen) {
|
|
297
279
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
298
|
-
if (c->qlog == NULL)
|
|
280
|
+
if (c->qlog == NULL)
|
|
281
|
+
return;
|
|
299
282
|
fwrite(data, 1, datalen, c->qlog);
|
|
300
|
-
if (flags & NGTCP2_QLOG_WRITE_FLAG_FIN)
|
|
301
|
-
|
|
302
|
-
c->qlog = NULL;
|
|
303
|
-
}
|
|
283
|
+
if (flags & NGTCP2_QLOG_WRITE_FLAG_FIN)
|
|
284
|
+
quic_conn_close_qlog(c);
|
|
304
285
|
}
|
|
305
286
|
|
|
306
|
-
void
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
287
|
+
void quic_conn_close_qlog(quic_conn_t *c) {
|
|
288
|
+
if (c->qlog == NULL)
|
|
289
|
+
return;
|
|
290
|
+
fclose(c->qlog);
|
|
291
|
+
c->qlog = NULL;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
void quic_conn_open_qlog(quic_conn_t *c, VALUE settings_v,
|
|
295
|
+
ngtcp2_settings *settings) {
|
|
296
|
+
VALUE dir = quic_path_value(rb_funcall(settings_v, rb_intern("qlog_dir"), 0),
|
|
297
|
+
"qlog_dir");
|
|
298
|
+
if (NIL_P(dir))
|
|
299
|
+
return;
|
|
311
300
|
|
|
312
301
|
char hex[NGTCP2_MAX_CIDLEN * 2 + 1];
|
|
313
302
|
for (size_t i = 0; i < c->scid.datalen; i++) {
|
|
314
303
|
snprintf(hex + i * 2, 3, "%02x", c->scid.data[i]);
|
|
315
304
|
}
|
|
316
305
|
hex[c->scid.datalen * 2] = '\0';
|
|
317
|
-
VALUE path = rb_sprintf("%"PRIsVALUE"/%s.sqlog", dir, hex);
|
|
306
|
+
VALUE path = rb_sprintf("%" PRIsVALUE "/%s.sqlog", dir, hex);
|
|
318
307
|
c->qlog = fopen(StringValueCStr(path), "w");
|
|
319
|
-
if (c->qlog != NULL)
|
|
308
|
+
if (c->qlog != NULL)
|
|
309
|
+
settings->qlog_write = quic_qlog_write_cb;
|
|
320
310
|
}
|
|
321
311
|
|
|
322
312
|
/* Validate a path setting (ca_file, ca_path, ...) and return it as a String,
|
|
323
313
|
or Qnil. Accepts a String or anything responding to #to_path. Every
|
|
324
314
|
misconfiguration raises ArgumentError, including a wrong type. */
|
|
325
315
|
VALUE
|
|
326
|
-
quic_path_value(VALUE value, const char *name)
|
|
327
|
-
|
|
328
|
-
|
|
316
|
+
quic_path_value(VALUE value, const char *name) {
|
|
317
|
+
if (NIL_P(value))
|
|
318
|
+
return Qnil;
|
|
329
319
|
|
|
330
320
|
VALUE path = value;
|
|
331
321
|
if (!RB_TYPE_P(path, T_STRING)) {
|
|
@@ -333,8 +323,9 @@ quic_path_value(VALUE value, const char *name)
|
|
|
333
323
|
path = rb_funcall(path, rb_intern("to_path"), 0);
|
|
334
324
|
}
|
|
335
325
|
if (!RB_TYPE_P(path, T_STRING)) {
|
|
336
|
-
rb_raise(rb_eArgError,
|
|
337
|
-
|
|
326
|
+
rb_raise(rb_eArgError,
|
|
327
|
+
"%s must be a String or Pathname (got %" PRIsVALUE ")", name,
|
|
328
|
+
rb_obj_class(value));
|
|
338
329
|
}
|
|
339
330
|
}
|
|
340
331
|
/* Raises ArgumentError when the path contains a NUL byte. */
|
|
@@ -344,13 +335,13 @@ quic_path_value(VALUE value, const char *name)
|
|
|
344
335
|
|
|
345
336
|
/* picotls keeps the pointers of an ALPN list for the whole handshake, so
|
|
346
337
|
ALLOCA_N would not survive. */
|
|
347
|
-
void
|
|
348
|
-
|
|
349
|
-
{
|
|
338
|
+
void quic_copy_alpn(VALUE alpn_ary, ptls_iovec_t **list, size_t *count,
|
|
339
|
+
uint8_t **buf) {
|
|
350
340
|
Check_Type(alpn_ary, T_ARRAY);
|
|
351
341
|
long n_alpn = RARRAY_LEN(alpn_ary);
|
|
352
342
|
*count = 0;
|
|
353
|
-
if (n_alpn <= 0)
|
|
343
|
+
if (n_alpn <= 0)
|
|
344
|
+
return;
|
|
354
345
|
|
|
355
346
|
size_t total = 0;
|
|
356
347
|
for (long i = 0; i < n_alpn; i++) {
|
|
@@ -377,20 +368,16 @@ quic_copy_alpn(VALUE alpn_ary, ptls_iovec_t **list, size_t *count, uint8_t **buf
|
|
|
377
368
|
|
|
378
369
|
/* An empty list leaves c->alpn NULL, which a client sends as no ALPN
|
|
379
370
|
extension at all. */
|
|
380
|
-
void
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
quic_copy_alpn(rb_funcall(settings_v, rb_intern("alpn"), 0), &c->alpn, &c->alpn_count,
|
|
384
|
-
&c->alpn_buf);
|
|
371
|
+
void quic_conn_copy_alpn(quic_conn_t *c, VALUE settings_v) {
|
|
372
|
+
quic_copy_alpn(rb_funcall(settings_v, rb_intern("alpn"), 0), &c->alpn,
|
|
373
|
+
&c->alpn_count, &c->alpn_buf);
|
|
385
374
|
}
|
|
386
375
|
|
|
387
376
|
/* Shared helper: look up the QUIC::Stream registered for stream_id in the
|
|
388
377
|
owner connection's @streams Hash. Returns Qnil if not found (which can
|
|
389
378
|
happen if the stream was already removed by a previous stream_close
|
|
390
379
|
callback). */
|
|
391
|
-
static VALUE
|
|
392
|
-
quic_conn_lookup_stream(quic_conn_t *c, int64_t stream_id)
|
|
393
|
-
{
|
|
380
|
+
static VALUE quic_conn_lookup_stream(quic_conn_t *c, int64_t stream_id) {
|
|
394
381
|
VALUE streams = rb_ivar_get(c->owner, rb_intern("@streams"));
|
|
395
382
|
return rb_hash_aref(streams, LL2NUM(stream_id));
|
|
396
383
|
}
|
|
@@ -413,44 +400,40 @@ struct quic_stream_cb_args {
|
|
|
413
400
|
uint64_t app_error_code;
|
|
414
401
|
};
|
|
415
402
|
|
|
416
|
-
static VALUE
|
|
417
|
-
quic_stream_open_body(VALUE arg)
|
|
418
|
-
{
|
|
403
|
+
static VALUE quic_stream_open_body(VALUE arg) {
|
|
419
404
|
struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
|
|
420
405
|
VALUE streams = rb_ivar_get(a->c->owner, rb_intern("@streams"));
|
|
421
406
|
if (!NIL_P(rb_hash_aref(streams, LL2NUM(a->stream_id)))) {
|
|
422
|
-
return Qnil;
|
|
407
|
+
return Qnil; /* already known (opened by this endpoint) */
|
|
423
408
|
}
|
|
424
409
|
VALUE stream = quic_stream_new(a->stream_id, a->c->owner);
|
|
425
410
|
rb_hash_aset(streams, LL2NUM(a->stream_id), stream);
|
|
426
411
|
return Qnil;
|
|
427
412
|
}
|
|
428
413
|
|
|
429
|
-
void
|
|
430
|
-
|
|
431
|
-
{
|
|
432
|
-
if (!ngtcp2_conn_is_local_stream2(conn, stream_id) && ngtcp2_is_bidi_stream(stream_id)) {
|
|
414
|
+
void quic_conn_give_back_peer_stream(ngtcp2_conn *conn, int64_t stream_id) {
|
|
415
|
+
if (!ngtcp2_conn_is_local_stream2(conn, stream_id) &&
|
|
416
|
+
ngtcp2_is_bidi_stream(stream_id)) {
|
|
433
417
|
ngtcp2_conn_extend_max_streams_bidi(conn, 1);
|
|
434
418
|
}
|
|
435
419
|
}
|
|
436
420
|
|
|
437
|
-
static int
|
|
438
|
-
|
|
439
|
-
{
|
|
421
|
+
static int quic_stream_open_cb(ngtcp2_conn *conn, int64_t stream_id,
|
|
422
|
+
void *user_data) {
|
|
440
423
|
(void)conn;
|
|
441
424
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
442
|
-
if (c->handler)
|
|
425
|
+
if (c->handler)
|
|
426
|
+
return 0; /* the handler keeps its own stream state */
|
|
443
427
|
struct quic_stream_cb_args a = {.c = c, .stream_id = stream_id};
|
|
444
428
|
return quic_conn_protect(c, quic_stream_open_body, &a);
|
|
445
429
|
}
|
|
446
430
|
|
|
447
|
-
static VALUE
|
|
448
|
-
quic_recv_stream_data_body(VALUE arg)
|
|
449
|
-
{
|
|
431
|
+
static VALUE quic_recv_stream_data_body(VALUE arg) {
|
|
450
432
|
struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
|
|
451
433
|
quic_conn_t *c = a->c;
|
|
452
434
|
VALUE stream = quic_conn_lookup_stream(c, a->stream_id);
|
|
453
|
-
if (NIL_P(stream))
|
|
435
|
+
if (NIL_P(stream))
|
|
436
|
+
return Qnil;
|
|
454
437
|
|
|
455
438
|
quic_stream_t *s;
|
|
456
439
|
TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
|
|
@@ -459,7 +442,8 @@ quic_recv_stream_data_body(VALUE arg)
|
|
|
459
442
|
marks the initiator (0 == client, 1 == server), so a client queues the
|
|
460
443
|
odd ids and a server the even ones. Push once, on first data arrival,
|
|
461
444
|
then mark so later data does not re-enqueue. */
|
|
462
|
-
bool peer_initiated =
|
|
445
|
+
bool peer_initiated =
|
|
446
|
+
c->is_server ? !(a->stream_id & 0x01) : (a->stream_id & 0x01);
|
|
463
447
|
if (!s->accept_queued && peer_initiated) {
|
|
464
448
|
VALUE accept_queue = rb_ivar_get(c->owner, rb_intern("@accept_queue"));
|
|
465
449
|
rb_ary_push(accept_queue, stream);
|
|
@@ -476,25 +460,25 @@ quic_recv_stream_data_body(VALUE arg)
|
|
|
476
460
|
return Qnil;
|
|
477
461
|
}
|
|
478
462
|
|
|
479
|
-
static int
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
{
|
|
463
|
+
static int quic_recv_stream_data_cb(ngtcp2_conn *conn, uint32_t flags,
|
|
464
|
+
int64_t stream_id, uint64_t offset,
|
|
465
|
+
const uint8_t *data, size_t datalen,
|
|
466
|
+
void *user_data, void *stream_user_data) {
|
|
484
467
|
(void)offset;
|
|
485
468
|
(void)stream_user_data;
|
|
486
469
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
487
470
|
if (c->handler) {
|
|
488
471
|
size_t consumed = 0;
|
|
489
472
|
int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) != 0;
|
|
490
|
-
if (c->handler->recv_stream_data(c->handler_ud, stream_id, data, datalen,
|
|
491
|
-
c->now, &consumed) < 0) {
|
|
473
|
+
if (c->handler->recv_stream_data(c->handler_ud, stream_id, data, datalen,
|
|
474
|
+
fin, c->now, &consumed) < 0) {
|
|
492
475
|
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
493
476
|
}
|
|
494
477
|
/* What the handler consumed outright (for HTTP/3, everything but the
|
|
495
478
|
request body) goes straight back to the peer as credit. */
|
|
496
479
|
if (consumed > 0) {
|
|
497
|
-
if (ngtcp2_conn_extend_max_stream_offset(conn, stream_id, consumed) !=
|
|
480
|
+
if (ngtcp2_conn_extend_max_stream_offset(conn, stream_id, consumed) !=
|
|
481
|
+
0) {
|
|
498
482
|
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
499
483
|
}
|
|
500
484
|
ngtcp2_conn_extend_max_offset(conn, consumed);
|
|
@@ -502,17 +486,20 @@ quic_recv_stream_data_cb(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
|
|
|
502
486
|
return 0;
|
|
503
487
|
}
|
|
504
488
|
struct quic_stream_cb_args a = {
|
|
505
|
-
|
|
489
|
+
.c = c,
|
|
490
|
+
.stream_id = stream_id,
|
|
491
|
+
.flags = flags,
|
|
492
|
+
.data = data,
|
|
493
|
+
.datalen = datalen,
|
|
506
494
|
};
|
|
507
495
|
return quic_conn_protect(c, quic_recv_stream_data_body, &a);
|
|
508
496
|
}
|
|
509
497
|
|
|
510
|
-
static VALUE
|
|
511
|
-
quic_acked_stream_data_offset_body(VALUE arg)
|
|
512
|
-
{
|
|
498
|
+
static VALUE quic_acked_stream_data_offset_body(VALUE arg) {
|
|
513
499
|
struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
|
|
514
500
|
VALUE stream = quic_conn_lookup_stream(a->c, a->stream_id);
|
|
515
|
-
if (NIL_P(stream))
|
|
501
|
+
if (NIL_P(stream))
|
|
502
|
+
return Qnil;
|
|
516
503
|
|
|
517
504
|
quic_stream_t *s;
|
|
518
505
|
TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
|
|
@@ -521,7 +508,9 @@ quic_acked_stream_data_offset_body(VALUE arg)
|
|
|
521
508
|
/* Shift any head Chunks of @pending_chunks whose end-offset is fully
|
|
522
509
|
covered by acked_offset. Each Chunk's start-offset in the stream is
|
|
523
510
|
pending_shifted (after previous shifts); its end-offset is
|
|
524
|
-
pending_shifted + RSTRING_LEN(head).
|
|
511
|
+
pending_shifted + RSTRING_LEN(head). A stopped stream's
|
|
512
|
+
@pending_chunks is empty, and acks for what it sent before shift
|
|
513
|
+
nothing. */
|
|
525
514
|
VALUE pending = rb_ivar_get(stream, rb_intern("@pending_chunks"));
|
|
526
515
|
while (RARRAY_LEN(pending) > 0) {
|
|
527
516
|
VALUE head = RARRAY_AREF(pending, 0);
|
|
@@ -536,26 +525,25 @@ quic_acked_stream_data_offset_body(VALUE arg)
|
|
|
536
525
|
return Qnil;
|
|
537
526
|
}
|
|
538
527
|
|
|
539
|
-
static int
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
{
|
|
528
|
+
static int quic_acked_stream_data_offset_cb(ngtcp2_conn *conn,
|
|
529
|
+
int64_t stream_id, uint64_t offset,
|
|
530
|
+
uint64_t datalen, void *user_data,
|
|
531
|
+
void *stream_user_data) {
|
|
544
532
|
(void)conn;
|
|
545
533
|
(void)offset;
|
|
546
534
|
(void)stream_user_data;
|
|
547
535
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
548
536
|
if (c->handler) {
|
|
549
537
|
return c->handler->acked_stream_data(c->handler_ud, stream_id, datalen) < 0
|
|
550
|
-
|
|
538
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
539
|
+
: 0;
|
|
551
540
|
}
|
|
552
|
-
struct quic_stream_cb_args a = {
|
|
541
|
+
struct quic_stream_cb_args a = {
|
|
542
|
+
.c = c, .stream_id = stream_id, .acked = datalen};
|
|
553
543
|
return quic_conn_protect(c, quic_acked_stream_data_offset_body, &a);
|
|
554
544
|
}
|
|
555
545
|
|
|
556
|
-
static VALUE
|
|
557
|
-
quic_stream_close_body(VALUE arg)
|
|
558
|
-
{
|
|
546
|
+
static VALUE quic_stream_close_body(VALUE arg) {
|
|
559
547
|
struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
|
|
560
548
|
VALUE streams = rb_ivar_get(a->c->owner, rb_intern("@streams"));
|
|
561
549
|
VALUE stream = rb_hash_aref(streams, LL2NUM(a->stream_id));
|
|
@@ -585,11 +573,9 @@ quic_stream_close_body(VALUE arg)
|
|
|
585
573
|
return Qnil;
|
|
586
574
|
}
|
|
587
575
|
|
|
588
|
-
static int
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
void *stream_user_data)
|
|
592
|
-
{
|
|
576
|
+
static int quic_stream_close_cb(ngtcp2_conn *conn, uint32_t flags,
|
|
577
|
+
int64_t stream_id, uint64_t app_error_code,
|
|
578
|
+
void *user_data, void *stream_user_data) {
|
|
593
579
|
(void)stream_user_data;
|
|
594
580
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
595
581
|
if (c->handler) {
|
|
@@ -597,21 +583,25 @@ quic_stream_close_cb(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id,
|
|
|
597
583
|
gets its stream back at once. */
|
|
598
584
|
quic_conn_give_back_peer_stream(conn, stream_id);
|
|
599
585
|
int has_code = (flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET) != 0;
|
|
600
|
-
return c->handler->stream_close(c->handler_ud, stream_id, has_code,
|
|
601
|
-
|
|
586
|
+
return c->handler->stream_close(c->handler_ud, stream_id, has_code,
|
|
587
|
+
app_error_code) < 0
|
|
588
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
589
|
+
: 0;
|
|
602
590
|
}
|
|
603
591
|
struct quic_stream_cb_args a = {
|
|
604
|
-
|
|
592
|
+
.c = c,
|
|
593
|
+
.stream_id = stream_id,
|
|
594
|
+
.flags = flags,
|
|
595
|
+
.app_error_code = app_error_code,
|
|
605
596
|
};
|
|
606
597
|
return quic_conn_protect(c, quic_stream_close_body, &a);
|
|
607
598
|
}
|
|
608
599
|
|
|
609
|
-
static VALUE
|
|
610
|
-
quic_stream_reset_body(VALUE arg)
|
|
611
|
-
{
|
|
600
|
+
static VALUE quic_stream_reset_body(VALUE arg) {
|
|
612
601
|
struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
|
|
613
602
|
VALUE stream = quic_conn_lookup_stream(a->c, a->stream_id);
|
|
614
|
-
if (NIL_P(stream))
|
|
603
|
+
if (NIL_P(stream))
|
|
604
|
+
return Qnil;
|
|
615
605
|
|
|
616
606
|
quic_stream_t *s;
|
|
617
607
|
TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
|
|
@@ -621,76 +611,120 @@ quic_stream_reset_body(VALUE arg)
|
|
|
621
611
|
return Qnil;
|
|
622
612
|
}
|
|
623
613
|
|
|
624
|
-
static int
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
void *stream_user_data)
|
|
628
|
-
{
|
|
614
|
+
static int quic_stream_reset_cb(ngtcp2_conn *conn, int64_t stream_id,
|
|
615
|
+
uint64_t final_size, uint64_t app_error_code,
|
|
616
|
+
void *user_data, void *stream_user_data) {
|
|
629
617
|
(void)conn;
|
|
630
618
|
(void)stream_user_data;
|
|
631
619
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
632
620
|
if (c->handler) {
|
|
633
|
-
return c->handler->stream_reset(c->handler_ud, stream_id, final_size,
|
|
634
|
-
|
|
621
|
+
return c->handler->stream_reset(c->handler_ud, stream_id, final_size,
|
|
622
|
+
app_error_code) < 0
|
|
623
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
624
|
+
: 0;
|
|
635
625
|
}
|
|
636
|
-
struct quic_stream_cb_args a = {
|
|
626
|
+
struct quic_stream_cb_args a = {
|
|
627
|
+
.c = c, .stream_id = stream_id, .app_error_code = app_error_code};
|
|
637
628
|
return quic_conn_protect(c, quic_stream_reset_body, &a);
|
|
638
629
|
}
|
|
639
630
|
|
|
631
|
+
/* Nothing queued on stream can go out any more: drop it all, and have
|
|
632
|
+
#write_pkt skip the stream from now on. ngtcp2 retransmits no STREAM
|
|
633
|
+
frame of a stream it has reset, so it no longer reads the bytes it was
|
|
634
|
+
given either. */
|
|
635
|
+
static void quic_stream_stop(VALUE stream) {
|
|
636
|
+
quic_stream_t *s;
|
|
637
|
+
TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
|
|
638
|
+
s->stopped = true;
|
|
639
|
+
rb_ary_clear(rb_ivar_get(stream, rb_intern("@pending_chunks")));
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
static VALUE quic_recv_stop_sending_body(VALUE arg) {
|
|
643
|
+
struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
|
|
644
|
+
VALUE stream = quic_conn_lookup_stream(a->c, a->stream_id);
|
|
645
|
+
if (NIL_P(stream))
|
|
646
|
+
return Qnil;
|
|
647
|
+
|
|
648
|
+
quic_stream_t *s;
|
|
649
|
+
TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
|
|
650
|
+
s->stop_app_error_code = a->app_error_code;
|
|
651
|
+
quic_stream_stop(stream);
|
|
652
|
+
return Qnil;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/* The peer sent STOP_SENDING. ngtcp2 shuts the send side and sends
|
|
656
|
+
RESET_STREAM itself once this returns. A stream handler learns of it when
|
|
657
|
+
#write_pkt finds the send side shut (stream_shut_write). */
|
|
658
|
+
static int quic_recv_stop_sending_cb(ngtcp2_conn *conn, int64_t stream_id,
|
|
659
|
+
uint64_t app_error_code, void *user_data,
|
|
660
|
+
void *stream_user_data) {
|
|
661
|
+
(void)conn;
|
|
662
|
+
(void)stream_user_data;
|
|
663
|
+
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
664
|
+
if (c->handler)
|
|
665
|
+
return 0;
|
|
666
|
+
struct quic_stream_cb_args a = {
|
|
667
|
+
.c = c, .stream_id = stream_id, .app_error_code = app_error_code};
|
|
668
|
+
return quic_conn_protect(c, quic_recv_stop_sending_body, &a);
|
|
669
|
+
}
|
|
670
|
+
|
|
640
671
|
/* The callbacks below matter only to a stream handler. Without one, the
|
|
641
672
|
QUIC::Stream mode needs none of them. */
|
|
642
673
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
{
|
|
674
|
+
/* This endpoint stopped reading the stream, and STOP_SENDING is going out.
|
|
675
|
+
Not to be confused with recv_stop_sending above. */
|
|
676
|
+
static int quic_stream_stop_sending_cb(ngtcp2_conn *conn, int64_t stream_id,
|
|
677
|
+
uint64_t app_error_code, void *user_data,
|
|
678
|
+
void *stream_user_data) {
|
|
648
679
|
(void)conn;
|
|
649
680
|
(void)stream_user_data;
|
|
650
681
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
651
|
-
if (!c->handler)
|
|
652
|
-
|
|
653
|
-
|
|
682
|
+
if (!c->handler)
|
|
683
|
+
return 0;
|
|
684
|
+
return c->handler->stream_stop_sending(c->handler_ud, stream_id,
|
|
685
|
+
app_error_code) < 0
|
|
686
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
687
|
+
: 0;
|
|
654
688
|
}
|
|
655
689
|
|
|
656
|
-
static int
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
void *stream_user_data)
|
|
660
|
-
{
|
|
690
|
+
static int quic_extend_max_stream_data_cb(ngtcp2_conn *conn, int64_t stream_id,
|
|
691
|
+
uint64_t max_data, void *user_data,
|
|
692
|
+
void *stream_user_data) {
|
|
661
693
|
(void)conn;
|
|
662
694
|
(void)max_data;
|
|
663
695
|
(void)stream_user_data;
|
|
664
696
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
665
|
-
if (!c->handler)
|
|
697
|
+
if (!c->handler)
|
|
698
|
+
return 0;
|
|
666
699
|
return c->handler->extend_max_stream_data(c->handler_ud, stream_id) < 0
|
|
667
|
-
|
|
700
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
701
|
+
: 0;
|
|
668
702
|
}
|
|
669
703
|
|
|
670
|
-
static int
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
{
|
|
704
|
+
static int quic_extend_max_remote_streams_bidi_cb(ngtcp2_conn *conn,
|
|
705
|
+
uint64_t max_streams,
|
|
706
|
+
void *user_data) {
|
|
674
707
|
(void)conn;
|
|
675
708
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
676
|
-
if (!c->handler)
|
|
677
|
-
|
|
678
|
-
|
|
709
|
+
if (!c->handler)
|
|
710
|
+
return 0;
|
|
711
|
+
return c->handler->extend_max_remote_streams_bidi(c->handler_ud,
|
|
712
|
+
max_streams) < 0
|
|
713
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
714
|
+
: 0;
|
|
679
715
|
}
|
|
680
716
|
|
|
681
|
-
static int
|
|
682
|
-
quic_handshake_completed_cb(ngtcp2_conn *conn, void *user_data)
|
|
683
|
-
{
|
|
717
|
+
static int quic_handshake_completed_cb(ngtcp2_conn *conn, void *user_data) {
|
|
684
718
|
(void)conn;
|
|
685
719
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
686
|
-
if (!c->handler)
|
|
720
|
+
if (!c->handler)
|
|
721
|
+
return 0;
|
|
687
722
|
return c->handler->handshake_completed(c->handler_ud) < 0
|
|
688
|
-
|
|
723
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
724
|
+
: 0;
|
|
689
725
|
}
|
|
690
726
|
|
|
691
|
-
void
|
|
692
|
-
quic_conn_fill_common_callbacks(ngtcp2_callbacks *callbacks)
|
|
693
|
-
{
|
|
727
|
+
void quic_conn_fill_common_callbacks(ngtcp2_callbacks *callbacks) {
|
|
694
728
|
memset(callbacks, 0, sizeof(*callbacks));
|
|
695
729
|
callbacks->recv_crypto_data = ngtcp2_crypto_recv_crypto_data_cb;
|
|
696
730
|
callbacks->encrypt = ngtcp2_crypto_encrypt_cb;
|
|
@@ -698,8 +732,10 @@ quic_conn_fill_common_callbacks(ngtcp2_callbacks *callbacks)
|
|
|
698
732
|
callbacks->hp_mask = ngtcp2_crypto_hp_mask_cb;
|
|
699
733
|
callbacks->update_key = ngtcp2_crypto_update_key_cb;
|
|
700
734
|
callbacks->delete_crypto_aead_ctx = ngtcp2_crypto_delete_crypto_aead_ctx_cb;
|
|
701
|
-
callbacks->delete_crypto_cipher_ctx =
|
|
702
|
-
|
|
735
|
+
callbacks->delete_crypto_cipher_ctx =
|
|
736
|
+
ngtcp2_crypto_delete_crypto_cipher_ctx_cb;
|
|
737
|
+
callbacks->get_path_challenge_data2 =
|
|
738
|
+
ngtcp2_crypto_get_path_challenge_data2_cb;
|
|
703
739
|
callbacks->version_negotiation = ngtcp2_crypto_version_negotiation_cb;
|
|
704
740
|
callbacks->rand = quic_rand_cb;
|
|
705
741
|
callbacks->get_new_connection_id2 = quic_get_new_connection_id_cb;
|
|
@@ -709,22 +745,24 @@ quic_conn_fill_common_callbacks(ngtcp2_callbacks *callbacks)
|
|
|
709
745
|
callbacks->acked_stream_data_offset = quic_acked_stream_data_offset_cb;
|
|
710
746
|
callbacks->stream_close = quic_stream_close_cb;
|
|
711
747
|
callbacks->stream_reset = quic_stream_reset_cb;
|
|
748
|
+
callbacks->recv_stop_sending = quic_recv_stop_sending_cb;
|
|
712
749
|
callbacks->stream_stop_sending = quic_stream_stop_sending_cb;
|
|
713
750
|
callbacks->extend_max_stream_data = quic_extend_max_stream_data_cb;
|
|
714
|
-
callbacks->extend_max_remote_streams_bidi =
|
|
751
|
+
callbacks->extend_max_remote_streams_bidi =
|
|
752
|
+
quic_extend_max_remote_streams_bidi_cb;
|
|
715
753
|
callbacks->handshake_completed = quic_handshake_completed_cb;
|
|
716
754
|
}
|
|
717
755
|
|
|
718
|
-
/* Find the first stream in @streams that has either
|
|
719
|
-
pending FIN to flush. Phase 4 minimum: linear scan,
|
|
720
|
-
(no round-robin). Sets *out_datav to the byte slice
|
|
721
|
-
to 0 or 1, *out_flags to
|
|
722
|
-
Returns the stream's Ruby
|
|
723
|
-
|
|
724
|
-
quic_conn_pick_send_stream(VALUE self, ngtcp2_vec *out_datav,
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
{
|
|
756
|
+
/* Find the first stream in @streams, stopped ones aside, that has either
|
|
757
|
+
pending bytes or a pending FIN to flush. Phase 4 minimum: linear scan,
|
|
758
|
+
take the first match (no round-robin). Sets *out_datav to the byte slice
|
|
759
|
+
to send, *out_datavcnt to 0 or 1, *out_flags to
|
|
760
|
+
NGTCP2_WRITE_STREAM_FLAG_FIN when applicable. Returns the stream's Ruby
|
|
761
|
+
VALUE (or Qnil if no candidate). */
|
|
762
|
+
static VALUE quic_conn_pick_send_stream(VALUE self, ngtcp2_vec *out_datav,
|
|
763
|
+
size_t *out_datavcnt,
|
|
764
|
+
uint32_t *out_flags,
|
|
765
|
+
int64_t *out_stream_id) {
|
|
728
766
|
VALUE streams = rb_ivar_get(self, rb_intern("@streams"));
|
|
729
767
|
VALUE stream_values = rb_funcall(streams, rb_intern("values"), 0);
|
|
730
768
|
|
|
@@ -732,7 +770,8 @@ quic_conn_pick_send_stream(VALUE self, ngtcp2_vec *out_datav,
|
|
|
732
770
|
VALUE st = RARRAY_AREF(stream_values, i);
|
|
733
771
|
quic_stream_t *s;
|
|
734
772
|
TypedData_Get_Struct(st, quic_stream_t, &quic_stream_data_type, s);
|
|
735
|
-
if (s->fin_flushed)
|
|
773
|
+
if (s->fin_flushed || s->stopped)
|
|
774
|
+
continue;
|
|
736
775
|
|
|
737
776
|
VALUE pending = rb_ivar_get(st, rb_intern("@pending_chunks"));
|
|
738
777
|
uint64_t position = s->sent_offset - s->pending_shifted;
|
|
@@ -762,8 +801,8 @@ quic_conn_pick_send_stream(VALUE self, ngtcp2_vec *out_datav,
|
|
|
762
801
|
remaining bytes of it (ngtcp2 may encode less, in which case FIN
|
|
763
802
|
won't actually be flushed and we'll retry next call). */
|
|
764
803
|
*out_flags = (s->fin_sent && chunk_idx == chunks_count - 1)
|
|
765
|
-
|
|
766
|
-
|
|
804
|
+
? NGTCP2_WRITE_STREAM_FLAG_FIN
|
|
805
|
+
: 0;
|
|
767
806
|
return st;
|
|
768
807
|
}
|
|
769
808
|
|
|
@@ -785,27 +824,34 @@ quic_conn_pick_send_stream(VALUE self, ngtcp2_vec *out_datav,
|
|
|
785
824
|
a pending FIN, route the packet through ngtcp2_conn_writev_stream so the
|
|
786
825
|
stream data gets framed alongside connection-level frames. Falls back to
|
|
787
826
|
conn-only when no stream is queued. Returns what ngtcp2 returned. */
|
|
788
|
-
static ngtcp2_ssize
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
{
|
|
827
|
+
static ngtcp2_ssize quic_conn_write_pkt_streams(VALUE self, quic_conn_t *c,
|
|
828
|
+
ngtcp2_path *path,
|
|
829
|
+
ngtcp2_pkt_info *pi,
|
|
830
|
+
uint8_t *dest, size_t destlen) {
|
|
792
831
|
ngtcp2_vec datav;
|
|
793
832
|
size_t datavcnt = 0;
|
|
794
833
|
uint32_t writev_flags = 0;
|
|
795
834
|
int64_t send_stream_id = -1;
|
|
796
835
|
VALUE selected_stream = quic_conn_pick_send_stream(
|
|
797
|
-
|
|
836
|
+
self, &datav, &datavcnt, &writev_flags, &send_stream_id);
|
|
798
837
|
|
|
799
838
|
ngtcp2_ssize pdatalen = -1;
|
|
800
839
|
ngtcp2_ssize n = ngtcp2_conn_writev_stream(
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
if (n == NGTCP2_ERR_STREAM_DATA_BLOCKED
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
peer extends its window (MAX_STREAM_DATA / MAX_DATA).
|
|
840
|
+
c->conn, path, pi, dest, destlen, &pdatalen, writev_flags, send_stream_id,
|
|
841
|
+
&datav, datavcnt, c->now);
|
|
842
|
+
|
|
843
|
+
if ((n == NGTCP2_ERR_STREAM_DATA_BLOCKED || n == NGTCP2_ERR_STREAM_SHUT_WR) &&
|
|
844
|
+
NIL_P(c->pending_exc)) {
|
|
845
|
+
/* DATA_BLOCKED: the stream's queued data is beyond what the peer's flow
|
|
846
|
+
control lets through right now. Keep it queued; it follows once the
|
|
847
|
+
peer extends its window (MAX_STREAM_DATA / MAX_DATA).
|
|
848
|
+
SHUT_WR: ngtcp2 shut the stream's send side without recv_stop_sending
|
|
849
|
+
marking the stream, as #reset does. What is queued can never go out,
|
|
850
|
+
so drop it.
|
|
851
|
+
Either way, build the packet without the stream's data, so that ACKs
|
|
852
|
+
and other frames still go out. */
|
|
853
|
+
if (n == NGTCP2_ERR_STREAM_SHUT_WR)
|
|
854
|
+
quic_stream_stop(selected_stream);
|
|
809
855
|
selected_stream = Qnil;
|
|
810
856
|
datavcnt = 0;
|
|
811
857
|
writev_flags = 0;
|
|
@@ -814,7 +860,8 @@ quic_conn_write_pkt_streams(VALUE self, quic_conn_t *c, ngtcp2_path *path,
|
|
|
814
860
|
0, -1, NULL, 0, c->now);
|
|
815
861
|
}
|
|
816
862
|
|
|
817
|
-
if (n < 0 || !NIL_P(c->pending_exc))
|
|
863
|
+
if (n < 0 || !NIL_P(c->pending_exc))
|
|
864
|
+
return n;
|
|
818
865
|
|
|
819
866
|
/* Update the selected stream's accounting only when we actually sent
|
|
820
867
|
bytes. pdatalen >= 0 means ngtcp2 framed that many data bytes into the
|
|
@@ -822,8 +869,8 @@ quic_conn_write_pkt_streams(VALUE self, quic_conn_t *c, ngtcp2_path *path,
|
|
|
822
869
|
or other frames). */
|
|
823
870
|
if (!NIL_P(selected_stream) && pdatalen > 0) {
|
|
824
871
|
quic_stream_t *s;
|
|
825
|
-
TypedData_Get_Struct(selected_stream, quic_stream_t,
|
|
826
|
-
|
|
872
|
+
TypedData_Get_Struct(selected_stream, quic_stream_t, &quic_stream_data_type,
|
|
873
|
+
s);
|
|
827
874
|
s->sent_offset += (uint64_t)pdatalen;
|
|
828
875
|
/* FIN is only actually written when all requested data fits in the
|
|
829
876
|
frame (see ngtcp2 docs on NGTCP2_WRITE_STREAM_FLAG_FIN). */
|
|
@@ -835,8 +882,8 @@ quic_conn_write_pkt_streams(VALUE self, quic_conn_t *c, ngtcp2_path *path,
|
|
|
835
882
|
(writev_flags & NGTCP2_WRITE_STREAM_FLAG_FIN)) {
|
|
836
883
|
/* FIN-only packet (no data) was successfully written. */
|
|
837
884
|
quic_stream_t *s;
|
|
838
|
-
TypedData_Get_Struct(selected_stream, quic_stream_t,
|
|
839
|
-
|
|
885
|
+
TypedData_Get_Struct(selected_stream, quic_stream_t, &quic_stream_data_type,
|
|
886
|
+
s);
|
|
840
887
|
s->fin_flushed = true;
|
|
841
888
|
}
|
|
842
889
|
return n;
|
|
@@ -857,10 +904,10 @@ _Static_assert(offsetof(quic_ext_vec, len) == offsetof(ngtcp2_vec, len),
|
|
|
857
904
|
nghttp3. NGTCP2_WRITE_STREAM_FLAG_MORE is not used, so a packet carries
|
|
858
905
|
data of one stream at most. A handler failure is reported as
|
|
859
906
|
NGTCP2_ERR_CALLBACK_FAILURE. */
|
|
860
|
-
static ngtcp2_ssize
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
{
|
|
907
|
+
static ngtcp2_ssize quic_conn_write_pkt_handler(quic_conn_t *c,
|
|
908
|
+
ngtcp2_path *path,
|
|
909
|
+
ngtcp2_pkt_info *pi,
|
|
910
|
+
uint8_t *dest, size_t destlen) {
|
|
864
911
|
const quic_ext_stream_handler *h = c->handler;
|
|
865
912
|
for (;;) {
|
|
866
913
|
int64_t sid = -1;
|
|
@@ -868,46 +915,49 @@ quic_conn_write_pkt_handler(quic_conn_t *c, ngtcp2_path *path, ngtcp2_pkt_info *
|
|
|
868
915
|
quic_ext_vec vec[QUIC_HANDLER_VECCNT];
|
|
869
916
|
size_t nvec = 0;
|
|
870
917
|
if (ngtcp2_conn_get_max_data_left2(c->conn) &&
|
|
871
|
-
h->next_stream_data(c->handler_ud, &sid, &fin, vec, QUIC_HANDLER_VECCNT,
|
|
918
|
+
h->next_stream_data(c->handler_ud, &sid, &fin, vec, QUIC_HANDLER_VECCNT,
|
|
919
|
+
&nvec) < 0) {
|
|
872
920
|
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
873
921
|
}
|
|
874
922
|
|
|
875
923
|
ngtcp2_ssize ndatalen = -1;
|
|
876
|
-
ngtcp2_ssize n =
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
924
|
+
ngtcp2_ssize n =
|
|
925
|
+
ngtcp2_conn_writev_stream(c->conn, path, pi, dest, destlen, &ndatalen,
|
|
926
|
+
fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0, sid,
|
|
927
|
+
(const ngtcp2_vec *)vec, nvec, c->now);
|
|
880
928
|
if (n == NGTCP2_ERR_STREAM_DATA_BLOCKED) {
|
|
881
|
-
if (h->stream_blocked(c->handler_ud, sid) < 0)
|
|
929
|
+
if (h->stream_blocked(c->handler_ud, sid) < 0)
|
|
930
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
882
931
|
continue;
|
|
883
932
|
}
|
|
884
933
|
if (n == NGTCP2_ERR_STREAM_SHUT_WR) {
|
|
885
|
-
if (h->stream_shut_write(c->handler_ud, sid) < 0)
|
|
934
|
+
if (h->stream_shut_write(c->handler_ud, sid) < 0)
|
|
935
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
886
936
|
continue;
|
|
887
937
|
}
|
|
888
|
-
if (n < 0)
|
|
889
|
-
|
|
938
|
+
if (n < 0)
|
|
939
|
+
return n;
|
|
940
|
+
if (ndatalen >= 0 &&
|
|
941
|
+
h->stream_data_written(c->handler_ud, sid, (size_t)ndatalen) < 0) {
|
|
890
942
|
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
891
943
|
}
|
|
892
|
-
return n;
|
|
944
|
+
return n; /* 0: nothing to send */
|
|
893
945
|
}
|
|
894
946
|
}
|
|
895
947
|
|
|
896
|
-
ngtcp2_ssize
|
|
897
|
-
|
|
898
|
-
{
|
|
948
|
+
ngtcp2_ssize quic_conn_write_pkt_raw(VALUE self, quic_conn_t *c,
|
|
949
|
+
ngtcp2_path *path, uint8_t *dest) {
|
|
899
950
|
ngtcp2_pkt_info pi;
|
|
900
951
|
memset(&pi, 0, sizeof(pi));
|
|
901
952
|
|
|
902
953
|
c->now = quic_now();
|
|
903
|
-
return c->handler
|
|
904
|
-
|
|
905
|
-
|
|
954
|
+
return c->handler ? quic_conn_write_pkt_handler(c, path, &pi, dest,
|
|
955
|
+
QUIC_WRITE_PKT_BUFLEN)
|
|
956
|
+
: quic_conn_write_pkt_streams(self, c, path, &pi, dest,
|
|
957
|
+
QUIC_WRITE_PKT_BUFLEN);
|
|
906
958
|
}
|
|
907
959
|
|
|
908
|
-
static VALUE
|
|
909
|
-
quic_conn_write_pkt(int argc, VALUE *argv, VALUE self)
|
|
910
|
-
{
|
|
960
|
+
static VALUE quic_conn_write_pkt(int argc, VALUE *argv, VALUE self) {
|
|
911
961
|
VALUE buffer = Qnil;
|
|
912
962
|
rb_scan_args(argc, argv, "01", &buffer);
|
|
913
963
|
|
|
@@ -923,15 +973,16 @@ quic_conn_write_pkt(int argc, VALUE *argv, VALUE self)
|
|
|
923
973
|
rb_raise(rb_eArgError, "buffer must be ASCII-8BIT (binary) encoding");
|
|
924
974
|
}
|
|
925
975
|
if (RSTRING_LEN(buffer) < (long)QUIC_WRITE_PKT_BUFLEN) {
|
|
926
|
-
rb_str_modify_expand(buffer,
|
|
976
|
+
rb_str_modify_expand(buffer,
|
|
977
|
+
(long)QUIC_WRITE_PKT_BUFLEN - RSTRING_LEN(buffer));
|
|
927
978
|
}
|
|
928
979
|
}
|
|
929
980
|
|
|
930
981
|
ngtcp2_path_storage path_storage;
|
|
931
982
|
ngtcp2_path_storage_zero(&path_storage);
|
|
932
983
|
|
|
933
|
-
ngtcp2_ssize n =
|
|
934
|
-
|
|
984
|
+
ngtcp2_ssize n = quic_conn_write_pkt_raw(self, c, &path_storage.path,
|
|
985
|
+
(uint8_t *)RSTRING_PTR(buffer));
|
|
935
986
|
|
|
936
987
|
if (!NIL_P(c->pending_exc)) {
|
|
937
988
|
rb_str_set_len(buffer, 0);
|
|
@@ -958,19 +1009,21 @@ quic_conn_write_pkt(int argc, VALUE *argv, VALUE self)
|
|
|
958
1009
|
sent, and becomes CertificateVerifyFailed when the verifier rejected the
|
|
959
1010
|
peer. */
|
|
960
1011
|
VALUE
|
|
961
|
-
quic_conn_error_new(quic_conn_t *c, int rv)
|
|
962
|
-
|
|
963
|
-
|
|
1012
|
+
quic_conn_error_new(quic_conn_t *c, int rv) {
|
|
1013
|
+
if (rv != NGTCP2_ERR_CRYPTO)
|
|
1014
|
+
return quic_ngtcp2_error_new(rv);
|
|
964
1015
|
|
|
965
1016
|
/* 0 means "not set" to ngtcp2; close_notify (0) is never sent for a
|
|
966
1017
|
verification failure, so report it as nil. */
|
|
967
1018
|
uint8_t alert = ngtcp2_conn_get_tls_alert2(c->conn);
|
|
968
1019
|
VALUE exc;
|
|
969
1020
|
if (c->verify_failed) {
|
|
970
|
-
VALUE msg =
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1021
|
+
VALUE msg =
|
|
1022
|
+
c->verify_result
|
|
1023
|
+
? rb_sprintf("certificate verify failed (%s)",
|
|
1024
|
+
X509_verify_cert_error_string(c->verify_result))
|
|
1025
|
+
: rb_str_new_cstr(
|
|
1026
|
+
"certificate verify failed (server sent no certificate)");
|
|
974
1027
|
exc = rb_exc_new_str(rb_eQUICErrorCertificateVerifyFailed, msg);
|
|
975
1028
|
rb_ivar_set(exc, rb_intern("@verify_result"),
|
|
976
1029
|
c->verify_result ? INT2NUM(c->verify_result) : Qnil);
|
|
@@ -982,10 +1035,8 @@ quic_conn_error_new(quic_conn_t *c, int rv)
|
|
|
982
1035
|
return exc;
|
|
983
1036
|
}
|
|
984
1037
|
|
|
985
|
-
int
|
|
986
|
-
|
|
987
|
-
size_t datalen)
|
|
988
|
-
{
|
|
1038
|
+
int quic_conn_read_pkt_raw(quic_conn_t *c, const ngtcp2_path *path,
|
|
1039
|
+
const uint8_t *data, size_t datalen) {
|
|
989
1040
|
ngtcp2_pkt_info pi;
|
|
990
1041
|
memset(&pi, 0, sizeof(pi));
|
|
991
1042
|
|
|
@@ -999,12 +1050,14 @@ struct quic_read_pkt_args {
|
|
|
999
1050
|
ngtcp2_path path;
|
|
1000
1051
|
};
|
|
1001
1052
|
|
|
1002
|
-
static VALUE
|
|
1003
|
-
quic_read_pkt_body(VALUE arg)
|
|
1004
|
-
{
|
|
1053
|
+
static VALUE quic_read_pkt_body(VALUE arg) {
|
|
1005
1054
|
struct quic_read_pkt_args *a = (struct quic_read_pkt_args *)arg;
|
|
1006
|
-
int rv = quic_conn_read_pkt_raw(a->c, &a->path,
|
|
1055
|
+
int rv = quic_conn_read_pkt_raw(a->c, &a->path,
|
|
1056
|
+
(const uint8_t *)RSTRING_PTR(a->packet),
|
|
1007
1057
|
(size_t)RSTRING_LEN(a->packet));
|
|
1058
|
+
/* The peer closed or reset the connection; ngtcp2 logs nothing more. */
|
|
1059
|
+
if (ngtcp2_conn_in_draining_period2(a->c->conn))
|
|
1060
|
+
quic_conn_close_qlog(a->c);
|
|
1008
1061
|
quic_conn_raise_pending(a->c);
|
|
1009
1062
|
if (rv != 0) {
|
|
1010
1063
|
/* longjmp passes through rb_ensure so unlock still fires. */
|
|
@@ -1013,16 +1066,12 @@ quic_read_pkt_body(VALUE arg)
|
|
|
1013
1066
|
return Qnil;
|
|
1014
1067
|
}
|
|
1015
1068
|
|
|
1016
|
-
static VALUE
|
|
1017
|
-
quic_read_pkt_unlock(VALUE arg)
|
|
1018
|
-
{
|
|
1069
|
+
static VALUE quic_read_pkt_unlock(VALUE arg) {
|
|
1019
1070
|
rb_str_unlocktmp(((struct quic_read_pkt_args *)arg)->packet);
|
|
1020
1071
|
return Qnil;
|
|
1021
1072
|
}
|
|
1022
1073
|
|
|
1023
|
-
static VALUE
|
|
1024
|
-
quic_conn_read_pkt(int argc, VALUE *argv, VALUE self)
|
|
1025
|
-
{
|
|
1074
|
+
static VALUE quic_conn_read_pkt(int argc, VALUE *argv, VALUE self) {
|
|
1026
1075
|
VALUE packet = Qnil;
|
|
1027
1076
|
VALUE opts = Qnil;
|
|
1028
1077
|
rb_scan_args(argc, argv, "1:", &packet, &opts);
|
|
@@ -1031,15 +1080,17 @@ quic_conn_read_pkt(int argc, VALUE *argv, VALUE self)
|
|
|
1031
1080
|
rb_raise(rb_eArgError, "missing keywords: local_sockaddr, remote_sockaddr");
|
|
1032
1081
|
}
|
|
1033
1082
|
|
|
1034
|
-
VALUE local_sockaddr
|
|
1035
|
-
|
|
1083
|
+
VALUE local_sockaddr =
|
|
1084
|
+
rb_hash_aref(opts, ID2SYM(rb_intern("local_sockaddr")));
|
|
1085
|
+
VALUE remote_sockaddr =
|
|
1086
|
+
rb_hash_aref(opts, ID2SYM(rb_intern("remote_sockaddr")));
|
|
1036
1087
|
|
|
1037
1088
|
if (NIL_P(local_sockaddr) || NIL_P(remote_sockaddr)) {
|
|
1038
1089
|
rb_raise(rb_eArgError, "missing keywords: local_sockaddr, remote_sockaddr");
|
|
1039
1090
|
}
|
|
1040
1091
|
|
|
1041
|
-
quic_require_binary(packet,
|
|
1042
|
-
quic_require_sockaddr(local_sockaddr,
|
|
1092
|
+
quic_require_binary(packet, "packet");
|
|
1093
|
+
quic_require_sockaddr(local_sockaddr, "local_sockaddr");
|
|
1043
1094
|
quic_require_sockaddr(remote_sockaddr, "remote_sockaddr");
|
|
1044
1095
|
|
|
1045
1096
|
quic_conn_t *c = quic_conn_get(self);
|
|
@@ -1047,78 +1098,72 @@ quic_conn_read_pkt(int argc, VALUE *argv, VALUE self)
|
|
|
1047
1098
|
struct quic_read_pkt_args args;
|
|
1048
1099
|
args.c = c;
|
|
1049
1100
|
args.packet = packet;
|
|
1050
|
-
args.path.local.addr
|
|
1051
|
-
args.path.local.addrlen
|
|
1052
|
-
args.path.remote.addr
|
|
1101
|
+
args.path.local.addr = (struct sockaddr *)RSTRING_PTR(local_sockaddr);
|
|
1102
|
+
args.path.local.addrlen = (ngtcp2_socklen)RSTRING_LEN(local_sockaddr);
|
|
1103
|
+
args.path.remote.addr = (struct sockaddr *)RSTRING_PTR(remote_sockaddr);
|
|
1053
1104
|
args.path.remote.addrlen = (ngtcp2_socklen)RSTRING_LEN(remote_sockaddr);
|
|
1054
|
-
args.path.user_data
|
|
1105
|
+
args.path.user_data = NULL;
|
|
1055
1106
|
|
|
1056
1107
|
rb_str_locktmp(packet);
|
|
1057
|
-
return rb_ensure(quic_read_pkt_body, (VALUE)&args,
|
|
1058
|
-
|
|
1108
|
+
return rb_ensure(quic_read_pkt_body, (VALUE)&args, quic_read_pkt_unlock,
|
|
1109
|
+
(VALUE)&args);
|
|
1059
1110
|
}
|
|
1060
1111
|
|
|
1061
|
-
static VALUE
|
|
1062
|
-
quic_conn_handshake_completed_p(VALUE self)
|
|
1063
|
-
{
|
|
1112
|
+
static VALUE quic_conn_handshake_completed_p(VALUE self) {
|
|
1064
1113
|
quic_conn_t *c = quic_conn_get(self);
|
|
1065
1114
|
return ngtcp2_conn_get_handshake_completed2(c->conn) ? Qtrue : Qfalse;
|
|
1066
1115
|
}
|
|
1067
1116
|
|
|
1068
|
-
static VALUE
|
|
1069
|
-
quic_conn_in_closing_period_p(VALUE self)
|
|
1070
|
-
{
|
|
1117
|
+
static VALUE quic_conn_in_closing_period_p(VALUE self) {
|
|
1071
1118
|
quic_conn_t *c = quic_conn_get(self);
|
|
1072
1119
|
return ngtcp2_conn_in_closing_period2(c->conn) ? Qtrue : Qfalse;
|
|
1073
1120
|
}
|
|
1074
1121
|
|
|
1075
|
-
static VALUE
|
|
1076
|
-
quic_conn_in_draining_period_p(VALUE self)
|
|
1077
|
-
{
|
|
1122
|
+
static VALUE quic_conn_in_draining_period_p(VALUE self) {
|
|
1078
1123
|
quic_conn_t *c = quic_conn_get(self);
|
|
1079
1124
|
return ngtcp2_conn_in_draining_period2(c->conn) ? Qtrue : Qfalse;
|
|
1080
1125
|
}
|
|
1081
1126
|
|
|
1082
|
-
static VALUE
|
|
1083
|
-
quic_conn_expiry(VALUE self)
|
|
1084
|
-
{
|
|
1127
|
+
static VALUE quic_conn_expiry(VALUE self) {
|
|
1085
1128
|
quic_conn_t *c = quic_conn_get(self);
|
|
1086
1129
|
ngtcp2_tstamp t = ngtcp2_conn_get_expiry2(c->conn);
|
|
1087
|
-
if (t == UINT64_MAX)
|
|
1130
|
+
if (t == UINT64_MAX)
|
|
1131
|
+
return Qnil;
|
|
1088
1132
|
return ULL2NUM((unsigned long long)t);
|
|
1089
1133
|
}
|
|
1090
1134
|
|
|
1091
1135
|
/* Caller is responsible for ordering with #read_pkt / #write_pkt:
|
|
1092
|
-
read_pkt or handle_expiry to advance ngtcp2 state, then write_pkt to flush.
|
|
1093
|
-
|
|
1094
|
-
quic_conn_handle_expiry(VALUE self)
|
|
1095
|
-
{
|
|
1136
|
+
read_pkt or handle_expiry to advance ngtcp2 state, then write_pkt to flush.
|
|
1137
|
+
*/
|
|
1138
|
+
static VALUE quic_conn_handle_expiry(VALUE self) {
|
|
1096
1139
|
quic_conn_t *c = quic_conn_get(self);
|
|
1097
1140
|
c->now = quic_now();
|
|
1098
1141
|
int rv = ngtcp2_conn_handle_expiry(c->conn, c->now);
|
|
1142
|
+
/* Timed out idle: the connection ends without a CONNECTION_CLOSE. */
|
|
1143
|
+
if (rv == NGTCP2_ERR_IDLE_CLOSE)
|
|
1144
|
+
quic_conn_close_qlog(c);
|
|
1099
1145
|
quic_conn_raise_pending(c);
|
|
1100
|
-
if (rv != 0)
|
|
1146
|
+
if (rv != 0)
|
|
1147
|
+
quic_raise_ngtcp2_error(rv);
|
|
1101
1148
|
return Qnil;
|
|
1102
1149
|
}
|
|
1103
1150
|
|
|
1104
1151
|
/* QUIC::Stream objects exist only without a stream handler. */
|
|
1105
|
-
static void
|
|
1106
|
-
quic_conn_require_stream_mode(quic_conn_t *c)
|
|
1107
|
-
{
|
|
1152
|
+
static void quic_conn_require_stream_mode(quic_conn_t *c) {
|
|
1108
1153
|
if (c->handler) {
|
|
1109
|
-
rb_raise(rb_eQUICErrorInvalidState,
|
|
1154
|
+
rb_raise(rb_eQUICErrorInvalidState,
|
|
1155
|
+
"the connection's streams belong to a C stream handler");
|
|
1110
1156
|
}
|
|
1111
1157
|
}
|
|
1112
1158
|
|
|
1113
|
-
static VALUE
|
|
1114
|
-
quic_conn_open_bidi_stream(VALUE self)
|
|
1115
|
-
{
|
|
1159
|
+
static VALUE quic_conn_open_bidi_stream(VALUE self) {
|
|
1116
1160
|
quic_conn_t *c = quic_conn_get(self);
|
|
1117
1161
|
quic_conn_require_stream_mode(c);
|
|
1118
1162
|
|
|
1119
1163
|
int64_t stream_id;
|
|
1120
1164
|
int rv = ngtcp2_conn_open_bidi_stream(c->conn, &stream_id, NULL);
|
|
1121
|
-
if (rv != 0)
|
|
1165
|
+
if (rv != 0)
|
|
1166
|
+
quic_raise_ngtcp2_error(rv);
|
|
1122
1167
|
|
|
1123
1168
|
VALUE stream = quic_stream_new(stream_id, self);
|
|
1124
1169
|
VALUE streams = rb_ivar_get(self, rb_intern("@streams"));
|
|
@@ -1126,15 +1171,14 @@ quic_conn_open_bidi_stream(VALUE self)
|
|
|
1126
1171
|
return stream;
|
|
1127
1172
|
}
|
|
1128
1173
|
|
|
1129
|
-
static VALUE
|
|
1130
|
-
quic_conn_open_uni_stream(VALUE self)
|
|
1131
|
-
{
|
|
1174
|
+
static VALUE quic_conn_open_uni_stream(VALUE self) {
|
|
1132
1175
|
quic_conn_t *c = quic_conn_get(self);
|
|
1133
1176
|
quic_conn_require_stream_mode(c);
|
|
1134
1177
|
|
|
1135
1178
|
int64_t stream_id;
|
|
1136
1179
|
int rv = ngtcp2_conn_open_uni_stream(c->conn, &stream_id, NULL);
|
|
1137
|
-
if (rv != 0)
|
|
1180
|
+
if (rv != 0)
|
|
1181
|
+
quic_raise_ngtcp2_error(rv);
|
|
1138
1182
|
|
|
1139
1183
|
VALUE stream = quic_stream_new(stream_id, self);
|
|
1140
1184
|
VALUE streams = rb_ivar_get(self, rb_intern("@streams"));
|
|
@@ -1144,13 +1188,12 @@ quic_conn_open_uni_stream(VALUE self)
|
|
|
1144
1188
|
|
|
1145
1189
|
/* Send an application CONNECTION_CLOSE (frame type 0x1d) to the peer and
|
|
1146
1190
|
transition the underlying ngtcp2_conn to the closing period. One UDP
|
|
1147
|
-
datagram is written to the bound socket
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
quic_conn_close_m(int argc, VALUE *argv, VALUE self)
|
|
1153
|
-
{
|
|
1191
|
+
datagram is written to the bound socket, once: nothing sends it again
|
|
1192
|
+
for packets the peer keeps sending (an Endpoint does, for its own
|
|
1193
|
+
connections), and #write_pkt raises QUIC::Error::Closed from here on.
|
|
1194
|
+
Calling #close on a connection that is already in the closing or
|
|
1195
|
+
draining period is a no-op (= returns nil without doing any I/O). */
|
|
1196
|
+
static VALUE quic_conn_close_m(int argc, VALUE *argv, VALUE self) {
|
|
1154
1197
|
VALUE opts = Qnil;
|
|
1155
1198
|
rb_scan_args(argc, argv, "0:", &opts);
|
|
1156
1199
|
|
|
@@ -1184,24 +1227,25 @@ quic_conn_close_m(int argc, VALUE *argv, VALUE self)
|
|
|
1184
1227
|
out through Endpoint#write_pkt. Once it has left the Endpoint's table
|
|
1185
1228
|
it is finished, and #close does nothing. */
|
|
1186
1229
|
if (c->from_endpoint) {
|
|
1187
|
-
if (c->endpoint == NULL)
|
|
1230
|
+
if (c->endpoint == NULL)
|
|
1231
|
+
return Qnil;
|
|
1188
1232
|
ngtcp2_ccerr ccerr;
|
|
1189
|
-
ngtcp2_ccerr_set_application_error(
|
|
1190
|
-
|
|
1233
|
+
ngtcp2_ccerr_set_application_error(
|
|
1234
|
+
&ccerr, error_code, reason_len > 0 ? reason_ptr : NULL, reason_len);
|
|
1191
1235
|
quic_endpoint_close_conn(c->endpoint, c, &ccerr);
|
|
1192
1236
|
return Qnil;
|
|
1193
1237
|
}
|
|
1194
1238
|
|
|
1195
1239
|
VALUE sock = rb_ivar_get(self, rb_intern("@sock"));
|
|
1196
1240
|
if (NIL_P(sock)) {
|
|
1197
|
-
rb_raise(rb_eQUICErrorNotBound,
|
|
1241
|
+
rb_raise(rb_eQUICErrorNotBound,
|
|
1242
|
+
"%" PRIsVALUE "#bind(sock) has not been called",
|
|
1198
1243
|
rb_obj_class(self));
|
|
1199
1244
|
}
|
|
1200
1245
|
|
|
1201
1246
|
ngtcp2_ccerr ccerr;
|
|
1202
|
-
ngtcp2_ccerr_set_application_error(
|
|
1203
|
-
|
|
1204
|
-
reason_len);
|
|
1247
|
+
ngtcp2_ccerr_set_application_error(
|
|
1248
|
+
&ccerr, error_code, reason_len > 0 ? reason_ptr : NULL, reason_len);
|
|
1205
1249
|
|
|
1206
1250
|
ngtcp2_path_storage path_storage;
|
|
1207
1251
|
ngtcp2_path_storage_zero(&path_storage);
|
|
@@ -1212,8 +1256,11 @@ quic_conn_close_m(int argc, VALUE *argv, VALUE self)
|
|
|
1212
1256
|
uint8_t *dest = (uint8_t *)RSTRING_PTR(buffer);
|
|
1213
1257
|
|
|
1214
1258
|
ngtcp2_ssize n = ngtcp2_conn_write_connection_close(
|
|
1215
|
-
c->conn, &path_storage.path, &pi, dest,
|
|
1216
|
-
|
|
1259
|
+
c->conn, &path_storage.path, &pi, dest, (size_t)QUIC_WRITE_PKT_BUFLEN,
|
|
1260
|
+
&ccerr, quic_now());
|
|
1261
|
+
/* The connection ends here whatever ngtcp2 returned, and the
|
|
1262
|
+
CONNECTION_CLOSE, if one was built, is in the qlog already. */
|
|
1263
|
+
quic_conn_close_qlog(c);
|
|
1217
1264
|
|
|
1218
1265
|
/* Pre-handshake or otherwise not in a state where CONNECTION_CLOSE can be
|
|
1219
1266
|
emitted (ngtcp2 returns NGTCP2_ERR_INVALID_STATE). Spec treats this as
|
|
@@ -1239,22 +1286,21 @@ quic_conn_close_m(int argc, VALUE *argv, VALUE self)
|
|
|
1239
1286
|
|
|
1240
1287
|
/* Why an Endpoint closed this connection; see quic_conn_t.error. Always nil
|
|
1241
1288
|
for a connection no Endpoint made. */
|
|
1242
|
-
static VALUE
|
|
1243
|
-
quic_conn_error_m(VALUE self)
|
|
1244
|
-
{
|
|
1289
|
+
static VALUE quic_conn_error_m(VALUE self) {
|
|
1245
1290
|
return quic_conn_get(self)->error;
|
|
1246
1291
|
}
|
|
1247
1292
|
|
|
1248
|
-
void
|
|
1249
|
-
quic_conn_define_shared_methods(VALUE klass)
|
|
1250
|
-
{
|
|
1293
|
+
void quic_conn_define_shared_methods(VALUE klass) {
|
|
1251
1294
|
rb_define_method(klass, "write_pkt", quic_conn_write_pkt, -1);
|
|
1252
|
-
rb_define_method(klass, "read_pkt",
|
|
1295
|
+
rb_define_method(klass, "read_pkt", quic_conn_read_pkt, -1);
|
|
1253
1296
|
rb_define_method(klass, "expiry", quic_conn_expiry, 0);
|
|
1254
1297
|
rb_define_method(klass, "handle_expiry", quic_conn_handle_expiry, 0);
|
|
1255
|
-
rb_define_method(klass, "handshake_completed?",
|
|
1256
|
-
|
|
1257
|
-
rb_define_method(klass, "
|
|
1298
|
+
rb_define_method(klass, "handshake_completed?",
|
|
1299
|
+
quic_conn_handshake_completed_p, 0);
|
|
1300
|
+
rb_define_method(klass, "in_closing_period?", quic_conn_in_closing_period_p,
|
|
1301
|
+
0);
|
|
1302
|
+
rb_define_method(klass, "in_draining_period?", quic_conn_in_draining_period_p,
|
|
1303
|
+
0);
|
|
1258
1304
|
rb_define_method(klass, "open_bidi_stream", quic_conn_open_bidi_stream, 0);
|
|
1259
1305
|
rb_define_method(klass, "open_uni_stream", quic_conn_open_uni_stream, 0);
|
|
1260
1306
|
rb_define_method(klass, "close", quic_conn_close_m, -1);
|