changeset 8698:9ed95726b99b quic

QUIC: send PING frames on PTO expiration. Two PING frames are sent per level that generate two UDP datagrams.
author Roman Arutyunyan <arut@nginx.com>
date Fri, 12 Feb 2021 14:51:53 +0300
parents faa3201ff351
children e24e5650d7b4
files src/event/quic/ngx_event_quic.c src/event/quic/ngx_event_quic_transport.c src/event/quic/ngx_event_quic_transport.h
diffstat 3 files changed, 98 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/quic/ngx_event_quic.c
+++ b/src/event/quic/ngx_event_quic.c
@@ -5099,6 +5099,10 @@ ngx_quic_output_packet(ngx_connection_t 
         f->plen = 0;
 
         nframes++;
+
+        if (f->flush) {
+            break;
+        }
     }
 
     if (nframes == 0) {
@@ -5346,9 +5350,10 @@ static void
 ngx_quic_pto_handler(ngx_event_t *ev)
 {
     ngx_uint_t              i;
-    ngx_queue_t            *q;
+    ngx_msec_t              now;
+    ngx_queue_t            *q, *next;
     ngx_connection_t       *c;
-    ngx_quic_frame_t       *start;
+    ngx_quic_frame_t       *f;
     ngx_quic_send_ctx_t    *ctx;
     ngx_quic_connection_t  *qc;
 
@@ -5356,8 +5361,7 @@ ngx_quic_pto_handler(ngx_event_t *ev)
 
     c = ev->data;
     qc = ngx_quic_get_connection(c);
-
-    qc->pto_count++;
+    now = ngx_current_msec;
 
     for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
 
@@ -5368,20 +5372,78 @@ ngx_quic_pto_handler(ngx_event_t *ev)
         }
 
         q = ngx_queue_head(&ctx->sent);
-        start = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
-        if (start->pnum <= ctx->largest_ack
+        f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+        if (f->pnum <= ctx->largest_ack
             && ctx->largest_ack != NGX_QUIC_UNSET_PN)
         {
             continue;
         }
 
-        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
-                       "quic pto pnum:%uL pto_count:%ui level:%d",
-                       start->pnum, qc->pto_count, start->level);
-
-        ngx_quic_resend_frames(c, ctx);
-    }
+        if ((ngx_msec_int_t) (f->last + ngx_quic_pto(c, ctx) - now) > 0) {
+            continue;
+        }
+
+        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "quic pto %s pto_count:%ui",
+                       ngx_quic_level_name(ctx->level), qc->pto_count);
+
+        for (q = ngx_queue_head(&ctx->frames);
+             q != ngx_queue_sentinel(&ctx->frames);
+             /* void */)
+        {
+            next = ngx_queue_next(q);
+            f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+            if (f->type == NGX_QUIC_FT_PING) {
+                ngx_queue_remove(q);
+                ngx_quic_free_frame(c, f);
+            }
+
+            q = next;
+        }
+
+        for (q = ngx_queue_head(&ctx->sent);
+             q != ngx_queue_sentinel(&ctx->sent);
+             /* void */)
+        {
+            next = ngx_queue_next(q);
+            f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+            if (f->type == NGX_QUIC_FT_PING) {
+                ngx_quic_congestion_lost(c, f);
+                ngx_queue_remove(q);
+                ngx_quic_free_frame(c, f);
+            }
+
+            q = next;
+        }
+
+        /* enforce 2 udp datagrams */
+
+        f = ngx_quic_alloc_frame(c);
+        if (f == NULL) {
+            break;
+        }
+
+        f->level = ctx->level;
+        f->type = NGX_QUIC_FT_PING;
+        f->flush = 1;
+
+        ngx_quic_queue_frame(qc, f);
+
+        f = ngx_quic_alloc_frame(c);
+        if (f == NULL) {
+            break;
+        }
+
+        f->level = ctx->level;
+        f->type = NGX_QUIC_FT_PING;
+
+        ngx_quic_queue_frame(qc, f);
+    }
+
+    qc->pto_count++;
 
     ngx_quic_connstate_dbg(c);
 }
--- a/src/event/quic/ngx_event_quic_transport.c
+++ b/src/event/quic/ngx_event_quic_transport.c
@@ -93,6 +93,7 @@ static size_t ngx_quic_create_short_head
 
 static ngx_int_t ngx_quic_frame_allowed(ngx_quic_header_t *pkt,
     ngx_uint_t frame_type);
+static size_t ngx_quic_create_ping(u_char *p);
 static size_t ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack,
     ngx_chain_t *ranges);
 static size_t ngx_quic_create_stop_sending(u_char *p,
@@ -1220,6 +1221,9 @@ ngx_quic_create_frame(u_char *p, ngx_qui
     f->need_ack = 1;
 
     switch (f->type) {
+    case NGX_QUIC_FT_PING:
+        return ngx_quic_create_ping(p);
+
     case NGX_QUIC_FT_ACK:
         f->need_ack = 0;
         return ngx_quic_create_ack(p, &f->u.ack, f->data);
@@ -1277,6 +1281,23 @@ ngx_quic_create_frame(u_char *p, ngx_qui
 
 
 static size_t
+ngx_quic_create_ping(u_char *p)
+{
+    u_char  *start;
+
+    if (p == NULL) {
+        return ngx_quic_varint_len(NGX_QUIC_FT_PING);
+    }
+
+    start = p;
+
+    ngx_quic_build_int(&p, NGX_QUIC_FT_PING);
+
+    return p - start;
+}
+
+
+static size_t
 ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack, ngx_chain_t *ranges)
 {
     size_t      len;
--- a/src/event/quic/ngx_event_quic_transport.h
+++ b/src/event/quic/ngx_event_quic_transport.h
@@ -263,8 +263,8 @@ struct ngx_quic_frame_s {
     ngx_msec_t                                  first;
     ngx_msec_t                                  last;
     ssize_t                                     len;
-    ngx_uint_t                                  need_ack;
-                                                    /* unsigned need_ack:1; */
+    unsigned                                    need_ack:1;
+    unsigned                                    flush:1;
 
     ngx_chain_t                                *data;
     union {