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.
@@ -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
- /* Run body(arg) under rb_protect. An exception is kept in c->pending_exc
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
- /* Shared helper: look up the QUIC::Stream registered for stream_id in the
377
- owner connection's @streams Hash. Returns Qnil if not found (which can
378
- happen if the stream was already removed by a previous stream_close
379
- callback). */
380
- static VALUE quic_conn_lookup_stream(quic_conn_t *c, int64_t stream_id) {
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. The Ruby-touching part of each is a *_body
386
- function run under rb_protect by quic_conn_protect, so that an exception
387
- (a frozen @streams, an allocation failure, ...) never unwinds through
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 VALUE quic_stream_open_body(VALUE arg) {
404
- struct quic_stream_cb_args *a = (struct quic_stream_cb_args *)arg;
405
- VALUE streams = rb_ivar_get(a->c->owner, rb_intern("@streams"));
406
- if (!NIL_P(rb_hash_aref(streams, LL2NUM(a->stream_id)))) {
407
- return Qnil; /* already known (opened by this endpoint) */
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
- VALUE stream = quic_stream_new(a->stream_id, a->c->owner);
410
- rb_hash_aset(streams, LL2NUM(a->stream_id), stream);
411
- return Qnil;
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
- 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)) {
417
- ngtcp2_conn_extend_max_streams_bidi(conn, 1);
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
- struct quic_stream_cb_args a = {.c = c, .stream_id = stream_id};
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
- size_t consumed = 0;
472
- int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) != 0;
473
- if (c->handler->recv_stream_data(c->handler_ud, stream_id, data, datalen,
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 c->handler->acked_stream_data(c->handler_ud, stream_id, datalen) < 0
538
- ? NGTCP2_ERR_CALLBACK_FAILURE
539
- : 0;
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
- /* The handler keeps no stream waiting for the application, so the peer
583
- gets its stream back at once. */
584
- quic_conn_give_back_peer_stream(conn, stream_id);
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 c->handler->stream_reset(c->handler_ud, stream_id, final_size,
622
- app_error_code) < 0
623
- ? NGTCP2_ERR_CALLBACK_FAILURE
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. A stream handler learns of it when
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 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);
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
- : quic_conn_write_pkt_streams(self, c, path, &pi, dest,
957
- QUIC_WRITE_PKT_BUFLEN);
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
- VALUE stream = quic_stream_new(stream_id, self);
1184
- VALUE streams = rb_ivar_get(self, rb_intern("@streams"));
1185
- rb_hash_aset(streams, LL2NUM(stream_id), stream);
1186
- return stream;
1187
- }
1188
-
1189
- /* Send an application CONNECTION_CLOSE (frame type 0x1d) to the peer and
1190
- transition the underlying ngtcp2_conn to the closing period. One UDP
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) {
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
- if (!NIL_P(opts)) {
1205
- VALUE error_code_v = rb_hash_aref(opts, ID2SYM(rb_intern("error_code")));
1206
- VALUE reason_v = rb_hash_aref(opts, ID2SYM(rb_intern("reason")));
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
- VALUE sock = rb_ivar_get(self, rb_intern("@sock"));
1240
- if (NIL_P(sock)) {
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
- rb_funcall(sock, rb_intern("send"), 2, buffer, INT2NUM(0));
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
- rb_define_method(klass, "close", quic_conn_close_m, -1);
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
  }