quic 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/LICENSE-DEPENDENCIES.txt +2 -2
- data/README.md +1 -1
- data/ext/quic/connection_common.c +116 -425
- data/ext/quic/connection_common.h +31 -5
- data/ext/quic/ext_api.c +7 -5
- data/ext/quic/extconf.rb +5 -5
- data/ext/quic/include/quic/ext.h +22 -1
- data/ext/quic/stream.c +434 -45
- data/ext/quic/stream.h +68 -22
- data/lib/quic/connection.rb +11 -0
- data/lib/quic/version.rb +1 -1
- data/sig/quic.rbs +14 -12
- metadata +3 -3
|
@@ -121,11 +121,7 @@ static void quic_conn_raise_pending(quic_conn_t *c) {
|
|
|
121
121
|
rb_exc_raise(exc);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
(the first one wins; ngtcp2 stops at the first failing callback anyway)
|
|
126
|
-
and turned into NGTCP2_ERR_CALLBACK_FAILURE, so that it never unwinds
|
|
127
|
-
through ngtcp2. */
|
|
128
|
-
static int quic_conn_protect(quic_conn_t *c, VALUE (*body)(VALUE), void *arg) {
|
|
124
|
+
int quic_conn_protect(quic_conn_t *c, VALUE (*body)(VALUE), void *arg) {
|
|
129
125
|
int state = 0;
|
|
130
126
|
rb_protect(body, (VALUE)arg, &state);
|
|
131
127
|
if (state == 0)
|
|
@@ -373,91 +369,87 @@ void quic_conn_copy_alpn(quic_conn_t *c, VALUE settings_v) {
|
|
|
373
369
|
&c->alpn_count, &c->alpn_buf);
|
|
374
370
|
}
|
|
375
371
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
VALUE streams = rb_ivar_get(c->owner, rb_intern("@streams"));
|
|
382
|
-
return rb_hash_aref(streams, LL2NUM(stream_id));
|
|
372
|
+
void quic_conn_give_back_peer_stream(ngtcp2_conn *conn, int64_t stream_id) {
|
|
373
|
+
if (!ngtcp2_conn_is_local_stream2(conn, stream_id) &&
|
|
374
|
+
ngtcp2_is_bidi_stream(stream_id)) {
|
|
375
|
+
ngtcp2_conn_extend_max_streams_bidi(conn, 1);
|
|
376
|
+
}
|
|
383
377
|
}
|
|
384
378
|
|
|
385
|
-
/* ngtcp2 stream callbacks
|
|
386
|
-
|
|
387
|
-
(
|
|
388
|
-
ngtcp2. The callback then returns NGTCP2_ERR_CALLBACK_FAILURE and the
|
|
389
|
-
exception is re-raised once ngtcp2 has returned. */
|
|
390
|
-
|
|
391
|
-
/* The arguments of a stream callback, handed to its body through
|
|
392
|
-
rb_protect's single VALUE. Each body reads only the fields it needs. */
|
|
393
|
-
struct quic_stream_cb_args {
|
|
394
|
-
quic_conn_t *c;
|
|
395
|
-
int64_t stream_id;
|
|
396
|
-
uint32_t flags;
|
|
397
|
-
const uint8_t *data;
|
|
398
|
-
size_t datalen;
|
|
399
|
-
uint64_t acked;
|
|
400
|
-
uint64_t app_error_code;
|
|
401
|
-
};
|
|
379
|
+
/* The ngtcp2 stream callbacks hand each stream to the stream handler (the
|
|
380
|
+
quic_handler_* functions below) or, without one, to its QUIC::Stream
|
|
381
|
+
(quic_stream_on_* in stream.c). */
|
|
402
382
|
|
|
403
|
-
static
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
383
|
+
static int quic_handler_recv_stream_data(quic_conn_t *c, ngtcp2_conn *conn,
|
|
384
|
+
uint32_t flags, int64_t stream_id,
|
|
385
|
+
const uint8_t *data, size_t datalen) {
|
|
386
|
+
size_t consumed = 0;
|
|
387
|
+
int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) != 0;
|
|
388
|
+
if (c->handler->recv_stream_data(c->handler_ud, stream_id, data, datalen, fin,
|
|
389
|
+
c->now, &consumed) < 0) {
|
|
390
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
408
391
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
392
|
+
/* What the handler consumed outright (for HTTP/3, everything but the
|
|
393
|
+
request body) goes straight back to the peer as credit. */
|
|
394
|
+
if (consumed > 0) {
|
|
395
|
+
if (ngtcp2_conn_extend_max_stream_offset(conn, stream_id, consumed) != 0) {
|
|
396
|
+
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
397
|
+
}
|
|
398
|
+
ngtcp2_conn_extend_max_offset(conn, consumed);
|
|
399
|
+
}
|
|
400
|
+
return 0;
|
|
412
401
|
}
|
|
413
402
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
403
|
+
static int quic_handler_acked_stream_data(quic_conn_t *c, int64_t stream_id,
|
|
404
|
+
uint64_t datalen) {
|
|
405
|
+
return c->handler->acked_stream_data(c->handler_ud, stream_id, datalen) < 0
|
|
406
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
407
|
+
: 0;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
static int quic_handler_stream_close(quic_conn_t *c, ngtcp2_conn *conn,
|
|
411
|
+
uint32_t flags, int64_t stream_id,
|
|
412
|
+
uint64_t app_error_code) {
|
|
413
|
+
/* The handler keeps no stream waiting for the application, so the peer
|
|
414
|
+
gets its stream back at once. */
|
|
415
|
+
quic_conn_give_back_peer_stream(conn, stream_id);
|
|
416
|
+
int has_code = (flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET) != 0;
|
|
417
|
+
return c->handler->stream_close(c->handler_ud, stream_id, has_code,
|
|
418
|
+
app_error_code) < 0
|
|
419
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
420
|
+
: 0;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
static int quic_handler_stream_reset(quic_conn_t *c, int64_t stream_id,
|
|
424
|
+
uint64_t final_size,
|
|
425
|
+
uint64_t app_error_code) {
|
|
426
|
+
return c->handler->stream_reset(c->handler_ud, stream_id, final_size,
|
|
427
|
+
app_error_code) < 0
|
|
428
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
429
|
+
: 0;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/* A handler built against ABI 1.0 lacks recv_stop_sending, and learns of
|
|
433
|
+
STOP_SENDING only when #write_pkt finds the send side shut
|
|
434
|
+
(stream_shut_write) or the stream closes. */
|
|
435
|
+
static int quic_handler_recv_stop_sending(quic_conn_t *c, int64_t stream_id,
|
|
436
|
+
uint64_t app_error_code) {
|
|
437
|
+
if (!QUIC_EXT_HANDLER_HAS(c->handler, recv_stop_sending))
|
|
438
|
+
return 0;
|
|
439
|
+
return c->handler->recv_stop_sending(c->handler_ud, stream_id,
|
|
440
|
+
app_error_code) < 0
|
|
441
|
+
? NGTCP2_ERR_CALLBACK_FAILURE
|
|
442
|
+
: 0;
|
|
419
443
|
}
|
|
420
444
|
|
|
445
|
+
/* Called only for a stream the peer opened. */
|
|
421
446
|
static int quic_stream_open_cb(ngtcp2_conn *conn, int64_t stream_id,
|
|
422
447
|
void *user_data) {
|
|
423
448
|
(void)conn;
|
|
424
449
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
425
450
|
if (c->handler)
|
|
426
451
|
return 0; /* the handler keeps its own stream state */
|
|
427
|
-
|
|
428
|
-
return quic_conn_protect(c, quic_stream_open_body, &a);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
static VALUE quic_recv_stream_data_body(VALUE arg) {
|
|
432
|
-
struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
|
|
433
|
-
quic_conn_t *c = a->c;
|
|
434
|
-
VALUE stream = quic_conn_lookup_stream(c, a->stream_id);
|
|
435
|
-
if (NIL_P(stream))
|
|
436
|
-
return Qnil;
|
|
437
|
-
|
|
438
|
-
quic_stream_t *s;
|
|
439
|
-
TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
|
|
440
|
-
|
|
441
|
-
/* Surface peer-initiated streams to #accept_stream. The low stream-id bit
|
|
442
|
-
marks the initiator (0 == client, 1 == server), so a client queues the
|
|
443
|
-
odd ids and a server the even ones. Push once, on first data arrival,
|
|
444
|
-
then mark so later data does not re-enqueue. */
|
|
445
|
-
bool peer_initiated =
|
|
446
|
-
c->is_server ? !(a->stream_id & 0x01) : (a->stream_id & 0x01);
|
|
447
|
-
if (!s->accept_queued && peer_initiated) {
|
|
448
|
-
VALUE accept_queue = rb_ivar_get(c->owner, rb_intern("@accept_queue"));
|
|
449
|
-
rb_ary_push(accept_queue, stream);
|
|
450
|
-
s->accept_queued = true;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
if (a->datalen > 0) {
|
|
454
|
-
VALUE recv_buffer = rb_ivar_get(stream, rb_intern("@recv_buffer"));
|
|
455
|
-
rb_str_buf_cat(recv_buffer, (const char *)a->data, (long)a->datalen);
|
|
456
|
-
}
|
|
457
|
-
if (a->flags & NGTCP2_STREAM_DATA_FLAG_FIN) {
|
|
458
|
-
s->fin_received = true;
|
|
459
|
-
}
|
|
460
|
-
return Qnil;
|
|
452
|
+
return quic_stream_on_open(c, stream_id);
|
|
461
453
|
}
|
|
462
454
|
|
|
463
455
|
static int quic_recv_stream_data_cb(ngtcp2_conn *conn, uint32_t flags,
|
|
@@ -465,64 +457,11 @@ static int quic_recv_stream_data_cb(ngtcp2_conn *conn, uint32_t flags,
|
|
|
465
457
|
const uint8_t *data, size_t datalen,
|
|
466
458
|
void *user_data, void *stream_user_data) {
|
|
467
459
|
(void)offset;
|
|
468
|
-
(void)stream_user_data;
|
|
469
460
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
470
|
-
if (c->handler)
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
fin, c->now, &consumed) < 0) {
|
|
475
|
-
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
476
|
-
}
|
|
477
|
-
/* What the handler consumed outright (for HTTP/3, everything but the
|
|
478
|
-
request body) goes straight back to the peer as credit. */
|
|
479
|
-
if (consumed > 0) {
|
|
480
|
-
if (ngtcp2_conn_extend_max_stream_offset(conn, stream_id, consumed) !=
|
|
481
|
-
0) {
|
|
482
|
-
return NGTCP2_ERR_CALLBACK_FAILURE;
|
|
483
|
-
}
|
|
484
|
-
ngtcp2_conn_extend_max_offset(conn, consumed);
|
|
485
|
-
}
|
|
486
|
-
return 0;
|
|
487
|
-
}
|
|
488
|
-
struct quic_stream_cb_args a = {
|
|
489
|
-
.c = c,
|
|
490
|
-
.stream_id = stream_id,
|
|
491
|
-
.flags = flags,
|
|
492
|
-
.data = data,
|
|
493
|
-
.datalen = datalen,
|
|
494
|
-
};
|
|
495
|
-
return quic_conn_protect(c, quic_recv_stream_data_body, &a);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
static VALUE quic_acked_stream_data_offset_body(VALUE arg) {
|
|
499
|
-
struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
|
|
500
|
-
VALUE stream = quic_conn_lookup_stream(a->c, a->stream_id);
|
|
501
|
-
if (NIL_P(stream))
|
|
502
|
-
return Qnil;
|
|
503
|
-
|
|
504
|
-
quic_stream_t *s;
|
|
505
|
-
TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
|
|
506
|
-
s->acked_offset += a->acked;
|
|
507
|
-
|
|
508
|
-
/* Shift any head Chunks of @pending_chunks whose end-offset is fully
|
|
509
|
-
covered by acked_offset. Each Chunk's start-offset in the stream is
|
|
510
|
-
pending_shifted (after previous shifts); its end-offset is
|
|
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. */
|
|
514
|
-
VALUE pending = rb_ivar_get(stream, rb_intern("@pending_chunks"));
|
|
515
|
-
while (RARRAY_LEN(pending) > 0) {
|
|
516
|
-
VALUE head = RARRAY_AREF(pending, 0);
|
|
517
|
-
uint64_t head_end = s->pending_shifted + (uint64_t)RSTRING_LEN(head);
|
|
518
|
-
if (head_end <= s->acked_offset) {
|
|
519
|
-
rb_ary_shift(pending);
|
|
520
|
-
s->pending_shifted = head_end;
|
|
521
|
-
} else {
|
|
522
|
-
break;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
return Qnil;
|
|
461
|
+
if (c->handler)
|
|
462
|
+
return quic_handler_recv_stream_data(c, conn, flags, stream_id, data,
|
|
463
|
+
datalen);
|
|
464
|
+
return quic_stream_on_recv_data(c, stream_user_data, flags, data, datalen);
|
|
526
465
|
}
|
|
527
466
|
|
|
528
467
|
static int quic_acked_stream_data_offset_cb(ngtcp2_conn *conn,
|
|
@@ -530,142 +469,45 @@ static int quic_acked_stream_data_offset_cb(ngtcp2_conn *conn,
|
|
|
530
469
|
uint64_t datalen, void *user_data,
|
|
531
470
|
void *stream_user_data) {
|
|
532
471
|
(void)conn;
|
|
533
|
-
(void)offset;
|
|
534
|
-
(void)stream_user_data;
|
|
535
472
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
536
|
-
if (c->handler)
|
|
537
|
-
return
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
struct quic_stream_cb_args a = {
|
|
542
|
-
.c = c, .stream_id = stream_id, .acked = datalen};
|
|
543
|
-
return quic_conn_protect(c, quic_acked_stream_data_offset_body, &a);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
static VALUE quic_stream_close_body(VALUE arg) {
|
|
547
|
-
struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
|
|
548
|
-
VALUE streams = rb_ivar_get(a->c->owner, rb_intern("@streams"));
|
|
549
|
-
VALUE stream = rb_hash_aref(streams, LL2NUM(a->stream_id));
|
|
550
|
-
if (NIL_P(stream)) {
|
|
551
|
-
quic_conn_give_back_peer_stream(a->c->conn, a->stream_id);
|
|
552
|
-
return Qnil;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
quic_stream_t *s;
|
|
556
|
-
TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
|
|
557
|
-
s->closed = true;
|
|
558
|
-
if (a->flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET) {
|
|
559
|
-
s->close_has_app_error_code = true;
|
|
560
|
-
s->close_app_error_code = a->app_error_code;
|
|
561
|
-
}
|
|
562
|
-
/* A stream still waiting in @accept_queue holds on to what it received.
|
|
563
|
-
Were the peer given its stream back now, it could open and close
|
|
564
|
-
streams until the queue had used up our memory, so it gets the stream
|
|
565
|
-
back only once the application has accepted this one
|
|
566
|
-
(QUIC::Stream#_accepted). */
|
|
567
|
-
if (s->accept_queued && !s->accepted) {
|
|
568
|
-
s->peer_stream_held = true;
|
|
569
|
-
} else {
|
|
570
|
-
quic_conn_give_back_peer_stream(a->c->conn, a->stream_id);
|
|
571
|
-
}
|
|
572
|
-
rb_hash_delete(streams, LL2NUM(a->stream_id));
|
|
573
|
-
return Qnil;
|
|
473
|
+
if (c->handler)
|
|
474
|
+
return quic_handler_acked_stream_data(c, stream_id, datalen);
|
|
475
|
+
quic_stream_on_acked(stream_user_data, offset, datalen);
|
|
476
|
+
return 0;
|
|
574
477
|
}
|
|
575
478
|
|
|
576
479
|
static int quic_stream_close_cb(ngtcp2_conn *conn, uint32_t flags,
|
|
577
480
|
int64_t stream_id, uint64_t app_error_code,
|
|
578
481
|
void *user_data, void *stream_user_data) {
|
|
579
|
-
(void)stream_user_data;
|
|
580
482
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
581
|
-
if (c->handler)
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
int has_code = (flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET) != 0;
|
|
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;
|
|
590
|
-
}
|
|
591
|
-
struct quic_stream_cb_args a = {
|
|
592
|
-
.c = c,
|
|
593
|
-
.stream_id = stream_id,
|
|
594
|
-
.flags = flags,
|
|
595
|
-
.app_error_code = app_error_code,
|
|
596
|
-
};
|
|
597
|
-
return quic_conn_protect(c, quic_stream_close_body, &a);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
static VALUE quic_stream_reset_body(VALUE arg) {
|
|
601
|
-
struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
|
|
602
|
-
VALUE stream = quic_conn_lookup_stream(a->c, a->stream_id);
|
|
603
|
-
if (NIL_P(stream))
|
|
604
|
-
return Qnil;
|
|
605
|
-
|
|
606
|
-
quic_stream_t *s;
|
|
607
|
-
TypedData_Get_Struct(stream, quic_stream_t, &quic_stream_data_type, s);
|
|
608
|
-
s->reset = true;
|
|
609
|
-
s->close_app_error_code = a->app_error_code;
|
|
610
|
-
s->close_has_app_error_code = true;
|
|
611
|
-
return Qnil;
|
|
483
|
+
if (c->handler)
|
|
484
|
+
return quic_handler_stream_close(c, conn, flags, stream_id, app_error_code);
|
|
485
|
+
return quic_stream_on_close(c, stream_user_data, stream_id, flags,
|
|
486
|
+
app_error_code);
|
|
612
487
|
}
|
|
613
488
|
|
|
614
489
|
static int quic_stream_reset_cb(ngtcp2_conn *conn, int64_t stream_id,
|
|
615
490
|
uint64_t final_size, uint64_t app_error_code,
|
|
616
491
|
void *user_data, void *stream_user_data) {
|
|
617
492
|
(void)conn;
|
|
618
|
-
(void)stream_user_data;
|
|
619
493
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
620
|
-
if (c->handler)
|
|
621
|
-
return
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
: 0;
|
|
625
|
-
}
|
|
626
|
-
struct quic_stream_cb_args a = {
|
|
627
|
-
.c = c, .stream_id = stream_id, .app_error_code = app_error_code};
|
|
628
|
-
return quic_conn_protect(c, quic_stream_reset_body, &a);
|
|
629
|
-
}
|
|
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;
|
|
494
|
+
if (c->handler)
|
|
495
|
+
return quic_handler_stream_reset(c, stream_id, final_size, app_error_code);
|
|
496
|
+
quic_stream_on_reset(stream_user_data, app_error_code);
|
|
497
|
+
return 0;
|
|
653
498
|
}
|
|
654
499
|
|
|
655
500
|
/* The peer sent STOP_SENDING. ngtcp2 shuts the send side and sends
|
|
656
|
-
RESET_STREAM itself once this returns.
|
|
657
|
-
#write_pkt finds the send side shut (stream_shut_write). */
|
|
501
|
+
RESET_STREAM itself once this returns. */
|
|
658
502
|
static int quic_recv_stop_sending_cb(ngtcp2_conn *conn, int64_t stream_id,
|
|
659
503
|
uint64_t app_error_code, void *user_data,
|
|
660
504
|
void *stream_user_data) {
|
|
661
505
|
(void)conn;
|
|
662
|
-
(void)stream_user_data;
|
|
663
506
|
quic_conn_t *c = (quic_conn_t *)user_data;
|
|
664
507
|
if (c->handler)
|
|
665
|
-
return
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
return quic_conn_protect(c, quic_recv_stop_sending_body, &a);
|
|
508
|
+
return quic_handler_recv_stop_sending(c, stream_id, app_error_code);
|
|
509
|
+
quic_stream_on_recv_stop_sending(c, stream_user_data, app_error_code);
|
|
510
|
+
return 0;
|
|
669
511
|
}
|
|
670
512
|
|
|
671
513
|
/* The callbacks below matter only to a stream handler. Without one, the
|
|
@@ -753,142 +595,6 @@ void quic_conn_fill_common_callbacks(ngtcp2_callbacks *callbacks) {
|
|
|
753
595
|
callbacks->handshake_completed = quic_handshake_completed_cb;
|
|
754
596
|
}
|
|
755
597
|
|
|
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) {
|
|
766
|
-
VALUE streams = rb_ivar_get(self, rb_intern("@streams"));
|
|
767
|
-
VALUE stream_values = rb_funcall(streams, rb_intern("values"), 0);
|
|
768
|
-
|
|
769
|
-
for (long i = 0; i < RARRAY_LEN(stream_values); i++) {
|
|
770
|
-
VALUE st = RARRAY_AREF(stream_values, i);
|
|
771
|
-
quic_stream_t *s;
|
|
772
|
-
TypedData_Get_Struct(st, quic_stream_t, &quic_stream_data_type, s);
|
|
773
|
-
if (s->fin_flushed || s->stopped)
|
|
774
|
-
continue;
|
|
775
|
-
|
|
776
|
-
VALUE pending = rb_ivar_get(st, rb_intern("@pending_chunks"));
|
|
777
|
-
uint64_t position = s->sent_offset - s->pending_shifted;
|
|
778
|
-
long chunks_count = RARRAY_LEN(pending);
|
|
779
|
-
|
|
780
|
-
/* Walk pending Chunks to find the one containing the next unsent byte. */
|
|
781
|
-
long chunk_idx = -1;
|
|
782
|
-
long offset_in_chunk = 0;
|
|
783
|
-
for (long j = 0; j < chunks_count; j++) {
|
|
784
|
-
VALUE chunk = RARRAY_AREF(pending, j);
|
|
785
|
-
long clen = RSTRING_LEN(chunk);
|
|
786
|
-
if (position < (uint64_t)clen) {
|
|
787
|
-
chunk_idx = j;
|
|
788
|
-
offset_in_chunk = (long)position;
|
|
789
|
-
break;
|
|
790
|
-
}
|
|
791
|
-
position -= (uint64_t)clen;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
if (chunk_idx >= 0) {
|
|
795
|
-
VALUE chunk = RARRAY_AREF(pending, chunk_idx);
|
|
796
|
-
out_datav->base = (uint8_t *)RSTRING_PTR(chunk) + offset_in_chunk;
|
|
797
|
-
out_datav->len = (size_t)(RSTRING_LEN(chunk) - offset_in_chunk);
|
|
798
|
-
*out_datavcnt = 1;
|
|
799
|
-
*out_stream_id = s->stream_id;
|
|
800
|
-
/* Attach FIN if this is the LAST chunk and we're going to send all
|
|
801
|
-
remaining bytes of it (ngtcp2 may encode less, in which case FIN
|
|
802
|
-
won't actually be flushed and we'll retry next call). */
|
|
803
|
-
*out_flags = (s->fin_sent && chunk_idx == chunks_count - 1)
|
|
804
|
-
? NGTCP2_WRITE_STREAM_FLAG_FIN
|
|
805
|
-
: 0;
|
|
806
|
-
return st;
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
if (s->fin_sent && !s->fin_flushed) {
|
|
810
|
-
/* No data to send but a pending FIN-only frame. */
|
|
811
|
-
out_datav->base = NULL;
|
|
812
|
-
out_datav->len = 0;
|
|
813
|
-
*out_datavcnt = 0;
|
|
814
|
-
*out_stream_id = s->stream_id;
|
|
815
|
-
*out_flags = NGTCP2_WRITE_STREAM_FLAG_FIN;
|
|
816
|
-
return st;
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
return Qnil;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
/* #write_pkt for the QUIC::Stream mode. If any stream has pending bytes or
|
|
824
|
-
a pending FIN, route the packet through ngtcp2_conn_writev_stream so the
|
|
825
|
-
stream data gets framed alongside connection-level frames. Falls back to
|
|
826
|
-
conn-only when no stream is queued. Returns what ngtcp2 returned. */
|
|
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) {
|
|
831
|
-
ngtcp2_vec datav;
|
|
832
|
-
size_t datavcnt = 0;
|
|
833
|
-
uint32_t writev_flags = 0;
|
|
834
|
-
int64_t send_stream_id = -1;
|
|
835
|
-
VALUE selected_stream = quic_conn_pick_send_stream(
|
|
836
|
-
self, &datav, &datavcnt, &writev_flags, &send_stream_id);
|
|
837
|
-
|
|
838
|
-
ngtcp2_ssize pdatalen = -1;
|
|
839
|
-
ngtcp2_ssize n = ngtcp2_conn_writev_stream(
|
|
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);
|
|
855
|
-
selected_stream = Qnil;
|
|
856
|
-
datavcnt = 0;
|
|
857
|
-
writev_flags = 0;
|
|
858
|
-
pdatalen = -1;
|
|
859
|
-
n = ngtcp2_conn_writev_stream(c->conn, path, pi, dest, destlen, &pdatalen,
|
|
860
|
-
0, -1, NULL, 0, c->now);
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
if (n < 0 || !NIL_P(c->pending_exc))
|
|
864
|
-
return n;
|
|
865
|
-
|
|
866
|
-
/* Update the selected stream's accounting only when we actually sent
|
|
867
|
-
bytes. pdatalen >= 0 means ngtcp2 framed that many data bytes into the
|
|
868
|
-
packet; if 0, no data was framed (but the packet may still contain ACK
|
|
869
|
-
or other frames). */
|
|
870
|
-
if (!NIL_P(selected_stream) && pdatalen > 0) {
|
|
871
|
-
quic_stream_t *s;
|
|
872
|
-
TypedData_Get_Struct(selected_stream, quic_stream_t, &quic_stream_data_type,
|
|
873
|
-
s);
|
|
874
|
-
s->sent_offset += (uint64_t)pdatalen;
|
|
875
|
-
/* FIN is only actually written when all requested data fits in the
|
|
876
|
-
frame (see ngtcp2 docs on NGTCP2_WRITE_STREAM_FLAG_FIN). */
|
|
877
|
-
if ((writev_flags & NGTCP2_WRITE_STREAM_FLAG_FIN) &&
|
|
878
|
-
(size_t)pdatalen == datav.len) {
|
|
879
|
-
s->fin_flushed = true;
|
|
880
|
-
}
|
|
881
|
-
} else if (!NIL_P(selected_stream) && datavcnt == 0 && n > 0 &&
|
|
882
|
-
(writev_flags & NGTCP2_WRITE_STREAM_FLAG_FIN)) {
|
|
883
|
-
/* FIN-only packet (no data) was successfully written. */
|
|
884
|
-
quic_stream_t *s;
|
|
885
|
-
TypedData_Get_Struct(selected_stream, quic_stream_t, &quic_stream_data_type,
|
|
886
|
-
s);
|
|
887
|
-
s->fin_flushed = true;
|
|
888
|
-
}
|
|
889
|
-
return n;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
598
|
/* The number of vectors #write_pkt asks a stream handler for per packet. */
|
|
893
599
|
#define QUIC_HANDLER_VECCNT 16
|
|
894
600
|
|
|
@@ -953,8 +659,8 @@ ngtcp2_ssize quic_conn_write_pkt_raw(VALUE self, quic_conn_t *c,
|
|
|
953
659
|
c->now = quic_now();
|
|
954
660
|
return c->handler ? quic_conn_write_pkt_handler(c, path, &pi, dest,
|
|
955
661
|
QUIC_WRITE_PKT_BUFLEN)
|
|
956
|
-
:
|
|
957
|
-
|
|
662
|
+
: quic_streams_write_pkt(self, c, path, &pi, dest,
|
|
663
|
+
QUIC_WRITE_PKT_BUFLEN);
|
|
958
664
|
}
|
|
959
665
|
|
|
960
666
|
static VALUE quic_conn_write_pkt(int argc, VALUE *argv, VALUE self) {
|
|
@@ -1164,11 +870,7 @@ static VALUE quic_conn_open_bidi_stream(VALUE self) {
|
|
|
1164
870
|
int rv = ngtcp2_conn_open_bidi_stream(c->conn, &stream_id, NULL);
|
|
1165
871
|
if (rv != 0)
|
|
1166
872
|
quic_raise_ngtcp2_error(rv);
|
|
1167
|
-
|
|
1168
|
-
VALUE stream = quic_stream_new(stream_id, self);
|
|
1169
|
-
VALUE streams = rb_ivar_get(self, rb_intern("@streams"));
|
|
1170
|
-
rb_hash_aset(streams, LL2NUM(stream_id), stream);
|
|
1171
|
-
return stream;
|
|
873
|
+
return quic_stream_open_local(self, c, stream_id);
|
|
1172
874
|
}
|
|
1173
875
|
|
|
1174
876
|
static VALUE quic_conn_open_uni_stream(VALUE self) {
|
|
@@ -1179,39 +881,26 @@ static VALUE quic_conn_open_uni_stream(VALUE self) {
|
|
|
1179
881
|
int rv = ngtcp2_conn_open_uni_stream(c->conn, &stream_id, NULL);
|
|
1180
882
|
if (rv != 0)
|
|
1181
883
|
quic_raise_ngtcp2_error(rv);
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
static VALUE quic_conn_close_m(int argc, VALUE *argv, VALUE self) {
|
|
1197
|
-
VALUE opts = Qnil;
|
|
1198
|
-
rb_scan_args(argc, argv, "0:", &opts);
|
|
1199
|
-
|
|
1200
|
-
uint64_t error_code = 0;
|
|
884
|
+
return quic_stream_open_local(self, c, stream_id);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/* #_close(error_code, reason), behind Pump#close: build an application
|
|
888
|
+
CONNECTION_CLOSE (frame type 0x1d) carrying error_code (nil: 0) and
|
|
889
|
+
reason (nil: none), and transition the underlying ngtcp2_conn to the
|
|
890
|
+
closing period. Returns the packet, for Pump#close to send through the
|
|
891
|
+
bound socket once: nothing sends it again for packets the peer keeps
|
|
892
|
+
sending (an Endpoint does, for its own connections), and #write_pkt
|
|
893
|
+
raises QUIC::Error::Closed from here on. Returns nil when there is
|
|
894
|
+
nothing to send: the connection is already in the closing or draining
|
|
895
|
+
period, it is an Endpoint's, or ngtcp2 could not build the packet. */
|
|
896
|
+
static VALUE quic_conn_close_m(VALUE self, VALUE error_code_v, VALUE reason_v) {
|
|
897
|
+
uint64_t error_code = NIL_P(error_code_v) ? 0 : NUM2ULL(error_code_v);
|
|
1201
898
|
const uint8_t *reason_ptr = (const uint8_t *)"";
|
|
1202
899
|
size_t reason_len = 0;
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
if (!NIL_P(error_code_v)) {
|
|
1208
|
-
error_code = NUM2ULL(error_code_v);
|
|
1209
|
-
}
|
|
1210
|
-
if (!NIL_P(reason_v)) {
|
|
1211
|
-
Check_Type(reason_v, T_STRING);
|
|
1212
|
-
reason_ptr = (const uint8_t *)RSTRING_PTR(reason_v);
|
|
1213
|
-
reason_len = (size_t)RSTRING_LEN(reason_v);
|
|
1214
|
-
}
|
|
900
|
+
if (!NIL_P(reason_v)) {
|
|
901
|
+
Check_Type(reason_v, T_STRING);
|
|
902
|
+
reason_ptr = (const uint8_t *)RSTRING_PTR(reason_v);
|
|
903
|
+
reason_len = (size_t)RSTRING_LEN(reason_v);
|
|
1215
904
|
}
|
|
1216
905
|
|
|
1217
906
|
quic_conn_t *c = quic_conn_get(self);
|
|
@@ -1236,8 +925,9 @@ static VALUE quic_conn_close_m(int argc, VALUE *argv, VALUE self) {
|
|
|
1236
925
|
return Qnil;
|
|
1237
926
|
}
|
|
1238
927
|
|
|
1239
|
-
|
|
1240
|
-
|
|
928
|
+
/* Checked here, not by Pump#close, so that it comes after the cases
|
|
929
|
+
above, which need no socket. */
|
|
930
|
+
if (NIL_P(rb_ivar_get(self, rb_intern("@sock")))) {
|
|
1241
931
|
rb_raise(rb_eQUICErrorNotBound,
|
|
1242
932
|
"%" PRIsVALUE "#bind(sock) has not been called",
|
|
1243
933
|
rb_obj_class(self));
|
|
@@ -1280,8 +970,7 @@ static VALUE quic_conn_close_m(int argc, VALUE *argv, VALUE self) {
|
|
|
1280
970
|
}
|
|
1281
971
|
|
|
1282
972
|
rb_str_set_len(buffer, n);
|
|
1283
|
-
|
|
1284
|
-
return Qnil;
|
|
973
|
+
return buffer;
|
|
1285
974
|
}
|
|
1286
975
|
|
|
1287
976
|
/* Why an Endpoint closed this connection; see quic_conn_t.error. Always nil
|
|
@@ -1303,6 +992,8 @@ void quic_conn_define_shared_methods(VALUE klass) {
|
|
|
1303
992
|
0);
|
|
1304
993
|
rb_define_method(klass, "open_bidi_stream", quic_conn_open_bidi_stream, 0);
|
|
1305
994
|
rb_define_method(klass, "open_uni_stream", quic_conn_open_uni_stream, 0);
|
|
1306
|
-
|
|
995
|
+
/* #close itself is Pump's (lib/quic/connection.rb), which sends what
|
|
996
|
+
#_close returns; defining it here would hide Pump's. */
|
|
997
|
+
rb_define_private_method(klass, "_close", quic_conn_close_m, 2);
|
|
1307
998
|
rb_define_method(klass, "error", quic_conn_error_m, 0);
|
|
1308
999
|
}
|