src/event/quic/ngx_event_quic_ack.c - nginx

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Nginx, Inc.
  3. */


  4. #include <ngx_config.h>
  5. #include <ngx_core.h>
  6. #include <ngx_event.h>
  7. #include <ngx_event_quic_connection.h>


  8. #define NGX_QUIC_MAX_ACK_GAP                 2

  9. /* RFC 9002, 6.1.1. Packet Threshold: kPacketThreshold */
  10. #define NGX_QUIC_PKT_THR                     3 /* packets */
  11. /* RFC 9002, 6.1.2. Time Threshold: kGranularity */
  12. #define NGX_QUIC_TIME_GRANULARITY            1 /* ms */

  13. /* RFC 9002, 7.6.1. Duration: kPersistentCongestionThreshold */
  14. #define NGX_QUIC_PERSISTENT_CONGESTION_THR   3

  15. /* CUBIC parameters x10 */
  16. #define NGX_QUIC_CUBIC_BETA                  7
  17. #define NGX_QUIC_CUBIC_C                     4


  18. /* send time of ACK'ed packets */
  19. typedef struct {
  20.     ngx_msec_t                               max_pn;
  21.     ngx_msec_t                               oldest;
  22.     ngx_msec_t                               newest;
  23. } ngx_quic_ack_stat_t;


  24. static ngx_inline ngx_msec_t ngx_quic_time_threshold(ngx_quic_connection_t *qc);
  25. static uint64_t ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx);
  26. static void ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
  27.     ngx_uint_t level, ngx_msec_t send_time);
  28. static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,
  29.     ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max,
  30.     ngx_quic_ack_stat_t *st);
  31. static size_t ngx_quic_congestion_cubic(ngx_connection_t *c);
  32. static void ngx_quic_drop_ack_ranges(ngx_connection_t *c,
  33.     ngx_quic_send_ctx_t *ctx, uint64_t pn);
  34. static ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c,
  35.     ngx_quic_ack_stat_t *st);
  36. static ngx_msec_t ngx_quic_congestion_cubic_time(ngx_connection_t *c);
  37. static ngx_msec_t ngx_quic_pcg_duration(ngx_connection_t *c);
  38. static void ngx_quic_persistent_congestion(ngx_connection_t *c);
  39. static ngx_msec_t ngx_quic_oldest_sent_packet(ngx_connection_t *c);
  40. static void ngx_quic_congestion_lost(ngx_connection_t *c,
  41.     ngx_quic_frame_t *frame);
  42. static void ngx_quic_lost_handler(ngx_event_t *ev);


  43. /* RFC 9002, 6.1.2. Time Threshold: kTimeThreshold, kGranularity */
  44. static ngx_inline ngx_msec_t
  45. ngx_quic_time_threshold(ngx_quic_connection_t *qc)
  46. {
  47.     ngx_msec_t  thr;

  48.     thr = ngx_max(qc->latest_rtt, qc->avg_rtt);
  49.     thr += thr >> 3;

  50.     return ngx_max(thr, NGX_QUIC_TIME_GRANULARITY);
  51. }


  52. static uint64_t
  53. ngx_quic_packet_threshold(ngx_quic_send_ctx_t *ctx)
  54. {
  55.     uint64_t           pkt_thr;
  56.     ngx_queue_t       *q;
  57.     ngx_quic_frame_t  *f;

  58.     if (ngx_queue_empty(&ctx->sent)) {
  59.         return NGX_QUIC_PKT_THR;
  60.     }

  61.     q = ngx_queue_head(&ctx->sent);
  62.     f = ngx_queue_data(q, ngx_quic_frame_t, queue);
  63.     pkt_thr = (ctx->pnum - f->pnum) / 2;

  64.     if (pkt_thr <= NGX_QUIC_PKT_THR) {
  65.         return NGX_QUIC_PKT_THR;
  66.     }

  67.     return pkt_thr;
  68. }


  69. ngx_int_t
  70. ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
  71.     ngx_quic_frame_t *f)
  72. {
  73.     ssize_t                 n;
  74.     u_char                 *pos, *end;
  75.     uint64_t                min, max, gap, range;
  76.     ngx_uint_t              i;
  77.     ngx_quic_ack_stat_t     send_time;
  78.     ngx_quic_send_ctx_t    *ctx;
  79.     ngx_quic_ack_frame_t   *ack;
  80.     ngx_quic_connection_t  *qc;

  81.     qc = ngx_quic_get_connection(c);

  82.     ctx = ngx_quic_get_send_ctx(qc, pkt->level);

  83.     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
  84.                    "quic ngx_quic_handle_ack_frame level:%ui", pkt->level);

  85.     ack = &f->u.ack;

  86.     /*
  87.      * RFC 9000, 19.3.1.  ACK Ranges
  88.      *
  89.      *  If any computed packet number is negative, an endpoint MUST
  90.      *  generate a connection error of type FRAME_ENCODING_ERROR.
  91.      */

  92.     if (ack->first_range > ack->largest) {
  93.         qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
  94.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  95.                       "quic invalid first range in ack frame");
  96.         return NGX_ERROR;
  97.     }

  98.     min = ack->largest - ack->first_range;
  99.     max = ack->largest;

  100.     send_time.oldest = NGX_TIMER_INFINITE;
  101.     send_time.newest = NGX_TIMER_INFINITE;

  102.     if (ngx_quic_handle_ack_frame_range(c, ctx, min, max, &send_time)
  103.         != NGX_OK)
  104.     {
  105.         return NGX_ERROR;
  106.     }

  107.     /* RFC 9000, 13.2.4.  Limiting Ranges by Tracking ACK Frames */
  108.     if (ctx->largest_ack < max || ctx->largest_ack == NGX_QUIC_UNSET_PN) {
  109.         ctx->largest_ack = max;
  110.         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
  111.                        "quic updated largest received ack:%uL", max);

  112.         /*
  113.          * RFC 9002, 5.1.  Generating RTT Samples
  114.          *
  115.          *  An endpoint generates an RTT sample on receiving an
  116.          *  ACK frame that meets the following two conditions:
  117.          *
  118.          *  - the largest acknowledged packet number is newly acknowledged
  119.          *  - at least one of the newly acknowledged packets was ack-eliciting.
  120.          */

  121.         if (send_time.max_pn != NGX_TIMER_INFINITE) {
  122.             ngx_quic_rtt_sample(c, ack, pkt->level, send_time.max_pn);
  123.         }
  124.     }

  125.     if (f->data) {
  126.         pos = f->data->buf->pos;
  127.         end = f->data->buf->last;

  128.     } else {
  129.         pos = NULL;
  130.         end = NULL;
  131.     }

  132.     for (i = 0; i < ack->range_count; i++) {

  133.         n = ngx_quic_parse_ack_range(pkt->log, pos, end, &gap, &range);
  134.         if (n == NGX_ERROR) {
  135.             return NGX_ERROR;
  136.         }
  137.         pos += n;

  138.         if (gap + 2 > min) {
  139.             qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
  140.             ngx_log_error(NGX_LOG_INFO, c->log, 0,
  141.                           "quic invalid range:%ui in ack frame", i);
  142.             return NGX_ERROR;
  143.         }

  144.         max = min - gap - 2;

  145.         if (range > max) {
  146.             qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
  147.             ngx_log_error(NGX_LOG_INFO, c->log, 0,
  148.                           "quic invalid range:%ui in ack frame", i);
  149.             return NGX_ERROR;
  150.         }

  151.         min = max - range;

  152.         if (ngx_quic_handle_ack_frame_range(c, ctx, min, max, &send_time)
  153.             != NGX_OK)
  154.         {
  155.             return NGX_ERROR;
  156.         }
  157.     }

  158.     return ngx_quic_detect_lost(c, &send_time);
  159. }


  160. static void
  161. ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
  162.     ngx_uint_t level, ngx_msec_t send_time)
  163. {
  164.     ngx_msec_t              latest_rtt, ack_delay, adjusted_rtt, rttvar_sample;
  165.     ngx_quic_connection_t  *qc;

  166.     qc = ngx_quic_get_connection(c);

  167.     latest_rtt = ngx_current_msec - send_time;
  168.     qc->latest_rtt = latest_rtt;

  169.     if (qc->min_rtt == NGX_TIMER_INFINITE) {
  170.         qc->min_rtt = latest_rtt;
  171.         qc->avg_rtt = latest_rtt;
  172.         qc->rttvar = latest_rtt / 2;
  173.         qc->first_rtt = ngx_current_msec;

  174.     } else {
  175.         qc->min_rtt = ngx_min(qc->min_rtt, latest_rtt);

  176.         ack_delay = (ack->delay << qc->ctp.ack_delay_exponent) / 1000;

  177.         if (c->ssl->handshaked) {
  178.             ack_delay = ngx_min(ack_delay, qc->ctp.max_ack_delay);
  179.         }

  180.         adjusted_rtt = latest_rtt;

  181.         if (qc->min_rtt + ack_delay < latest_rtt) {
  182.             adjusted_rtt -= ack_delay;
  183.         }

  184.         rttvar_sample = ngx_abs((ngx_msec_int_t) (qc->avg_rtt - adjusted_rtt));
  185.         qc->rttvar += (rttvar_sample >> 2) - (qc->rttvar >> 2);
  186.         qc->avg_rtt += (adjusted_rtt >> 3) - (qc->avg_rtt >> 3);
  187.     }

  188.     ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
  189.                    "quic rtt sample latest:%M min:%M avg:%M var:%M",
  190.                    latest_rtt, qc->min_rtt, qc->avg_rtt, qc->rttvar);
  191. }


  192. static ngx_int_t
  193. ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
  194.     uint64_t min, uint64_t max, ngx_quic_ack_stat_t *st)
  195. {
  196.     ngx_uint_t              found;
  197.     ngx_queue_t            *q;
  198.     ngx_quic_frame_t       *f;
  199.     ngx_quic_connection_t  *qc;

  200.     qc = ngx_quic_get_connection(c);

  201.     if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
  202.         if (ngx_quic_handle_path_mtu(c, qc->path, min, max) != NGX_OK) {
  203.             return NGX_ERROR;
  204.         }
  205.     }

  206.     st->max_pn = NGX_TIMER_INFINITE;
  207.     found = 0;

  208.     q = ngx_queue_head(&ctx->sent);

  209.     while (q != ngx_queue_sentinel(&ctx->sent)) {

  210.         f = ngx_queue_data(q, ngx_quic_frame_t, queue);
  211.         q = ngx_queue_next(q);

  212.         if (f->pnum > max) {
  213.             break;
  214.         }

  215.         if (f->pnum >= min) {
  216.             ngx_quic_congestion_ack(c, f);

  217.             switch (f->type) {
  218.             case NGX_QUIC_FT_ACK:
  219.             case NGX_QUIC_FT_ACK_ECN:
  220.                 ngx_quic_drop_ack_ranges(c, ctx, f->u.ack.largest);
  221.                 break;

  222.             case NGX_QUIC_FT_STREAM:
  223.             case NGX_QUIC_FT_RESET_STREAM:
  224.                 ngx_quic_handle_stream_ack(c, f);
  225.                 break;
  226.             }

  227.             if (f->pnum == max) {
  228.                 st->max_pn = f->send_time;
  229.             }

  230.             /* save earliest and latest send times of frames ack'ed */
  231.             if (st->oldest == NGX_TIMER_INFINITE || f->send_time < st->oldest) {
  232.                 st->oldest = f->send_time;
  233.             }

  234.             if (st->newest == NGX_TIMER_INFINITE || f->send_time > st->newest) {
  235.                 st->newest = f->send_time;
  236.             }

  237.             ngx_queue_remove(&f->queue);
  238.             ngx_quic_free_frame(c, f);
  239.             found = 1;
  240.         }
  241.     }

  242.     if (!found) {

  243.         if (max < ctx->pnum) {
  244.             /* duplicate ACK or ACK for non-ack-eliciting frame */
  245.             return NGX_OK;
  246.         }

  247.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  248.                       "quic ACK for the packet not sent");

  249.         qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;
  250.         qc->error_ftype = NGX_QUIC_FT_ACK;
  251.         qc->error_reason = "unknown packet number";

  252.         return NGX_ERROR;
  253.     }

  254.     if (!qc->push.timer_set) {
  255.         ngx_post_event(&qc->push, &ngx_posted_events);
  256.     }

  257.     qc->pto_count = 0;

  258.     return NGX_OK;
  259. }


  260. void
  261. ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
  262. {
  263.     size_t                  w_cubic;
  264.     ngx_uint_t              blocked;
  265.     ngx_msec_t              now, timer;
  266.     ngx_quic_congestion_t  *cg;
  267.     ngx_quic_connection_t  *qc;

  268.     if (f->plen == 0) {
  269.         return;
  270.     }

  271.     qc = ngx_quic_get_connection(c);
  272.     cg = &qc->congestion;

  273.     if (f->pnum < qc->rst_pnum) {
  274.         return;
  275.     }

  276.     now = ngx_current_msec;

  277.     blocked = (cg->in_flight >= cg->window) ? 1 : 0;

  278.     cg->in_flight -= f->plen;

  279.     /* prevent recovery_start from wrapping */

  280.     timer = now - cg->recovery_start;

  281.     if ((ngx_msec_int_t) timer < 0) {
  282.         cg->recovery_start = ngx_quic_oldest_sent_packet(c) - 1;
  283.     }

  284.     timer = f->send_time - cg->recovery_start;

  285.     if ((ngx_msec_int_t) timer <= 0) {
  286.         ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  287.                        "quic congestion ack rec t:%M win:%uz if:%uz",
  288.                        now, cg->window, cg->in_flight);

  289.         goto done;
  290.     }

  291.     if (cg->idle) {
  292.         ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  293.                        "quic congestion ack idle t:%M win:%uz if:%uz",
  294.                        now, cg->window, cg->in_flight);

  295.         goto done;
  296.     }

  297.     if (cg->window < cg->ssthresh) {
  298.         cg->window += f->plen;

  299.         ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
  300.                        "quic congestion ack ss t:%M win:%uz ss:%z if:%uz",
  301.                        now, cg->window, cg->ssthresh, cg->in_flight);

  302.     } else {

  303.         /* RFC 9438, 4.2. Window Increase Function */

  304.         w_cubic = ngx_quic_congestion_cubic(c);

  305.         if (cg->window < cg->w_prior) {
  306.             cg->w_est += (uint64_t) cg->mtu * f->plen
  307.                          * 3 * (10 - NGX_QUIC_CUBIC_BETA)
  308.                          / (10 + NGX_QUIC_CUBIC_BETA) / cg->window;

  309.         } else {
  310.             cg->w_est += (uint64_t) cg->mtu * f->plen / cg->window;
  311.         }

  312.         if (w_cubic < cg->w_est) {
  313.             cg->window = cg->w_est;

  314.             ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
  315.                        "quic congestion ack reno t:%M win:%uz c:%uz if:%uz",
  316.                        now, cg->window, w_cubic, cg->in_flight);

  317.         } else if (w_cubic > cg->window) {

  318.             if (w_cubic >= cg->window * 3 / 2) {
  319.                 cg->window += cg->mtu / 2;

  320.             } else {
  321.                 cg->window += (uint64_t) cg->mtu * (w_cubic - cg->window)
  322.                               / cg->window;
  323.             }

  324.             ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
  325.                           "quic congestion ack cubic t:%M win:%uz c:%uz if:%uz",
  326.                           now, cg->window, w_cubic, cg->in_flight);

  327.         } else {
  328.             ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
  329.                            "quic congestion ack skip t:%M win:%uz c:%uz if:%uz",
  330.                            now, cg->window, w_cubic, cg->in_flight);
  331.         }
  332.     }

  333. done:

  334.     if (blocked && cg->in_flight < cg->window) {
  335.         ngx_post_event(&qc->push, &ngx_posted_events);
  336.     }
  337. }


  338. static size_t
  339. ngx_quic_congestion_cubic(ngx_connection_t *c)
  340. {
  341.     int64_t                 w, t, cc;
  342.     ngx_msec_t              now;
  343.     ngx_quic_congestion_t  *cg;
  344.     ngx_quic_connection_t  *qc;

  345.     qc = ngx_quic_get_connection(c);
  346.     cg = &qc->congestion;

  347.     ngx_quic_congestion_idle(c, cg->idle);

  348.     now = ngx_current_msec;
  349.     t = (ngx_msec_int_t) (now - cg->k);

  350.     if (t > 1000000) {
  351.         w = NGX_MAX_SIZE_T_VALUE;
  352.         goto done;
  353.     }

  354.     if (t < -1000000) {
  355.         w = 0;
  356.         goto done;
  357.     }

  358.     /*
  359.      * RFC 9438, Figure 1
  360.      *
  361.      *   w_cubic = C * (t_msec / 1000) ^ 3 * mtu + w_max
  362.      */

  363.     cc = 10000000000ll / (int64_t) cg->mtu / NGX_QUIC_CUBIC_C;
  364.     w = t * t * t / cc + (int64_t) cg->w_max;

  365.     if (w > NGX_MAX_SIZE_T_VALUE) {
  366.         w = NGX_MAX_SIZE_T_VALUE;
  367.     }

  368.     if (w < 0) {
  369.         w = 0;
  370.     }

  371. done:

  372.     ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  373.                    "quic cubic t:%L w:%L wm:%uz", t, w, cg->w_max);

  374.     return w;
  375. }


  376. void
  377. ngx_quic_congestion_idle(ngx_connection_t *c, ngx_uint_t idle)
  378. {
  379.     ngx_msec_t              now;
  380.     ngx_quic_congestion_t  *cg;
  381.     ngx_quic_connection_t  *qc;

  382.     qc = ngx_quic_get_connection(c);
  383.     cg = &qc->congestion;

  384.     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
  385.                    "quic congestion idle:%ui", idle);

  386.     if (cg->window >= cg->ssthresh) {
  387.         /* RFC 9438, 5.8. Behavior for Application-Limited Flows */

  388.         now = ngx_current_msec;

  389.         if (cg->idle) {
  390.             cg->k += now - cg->idle_start;
  391.         }

  392.         cg->idle_start = now;
  393.     }

  394.     cg->idle = idle;
  395. }


  396. static void
  397. ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
  398.     uint64_t pn)
  399. {
  400.     uint64_t               base;
  401.     ngx_uint_t             i, smallest, largest;
  402.     ngx_quic_ack_range_t  *r;

  403.     ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
  404.                    "quic ngx_quic_drop_ack_ranges pn:%uL largest:%uL"
  405.                    " fr:%uL nranges:%ui", pn, ctx->largest_range,
  406.                    ctx->first_range, ctx->nranges);

  407.     base = ctx->largest_range;

  408.     if (base == NGX_QUIC_UNSET_PN) {
  409.         return;
  410.     }

  411.     if (ctx->pending_ack != NGX_QUIC_UNSET_PN && pn >= ctx->pending_ack) {
  412.         ctx->pending_ack = NGX_QUIC_UNSET_PN;
  413.     }

  414.     largest = base;
  415.     smallest = largest - ctx->first_range;

  416.     if (pn >= largest) {
  417.         ctx->largest_range = NGX_QUIC_UNSET_PN;
  418.         ctx->first_range = 0;
  419.         ctx->nranges = 0;
  420.         return;
  421.     }

  422.     if (pn >= smallest) {
  423.         ctx->first_range = largest - pn - 1;
  424.         ctx->nranges = 0;
  425.         return;
  426.     }

  427.     for (i = 0; i < ctx->nranges; i++) {
  428.         r = &ctx->ranges[i];

  429.         largest = smallest - r->gap - 2;
  430.         smallest = largest - r->range;

  431.         if (pn >= largest) {
  432.             ctx->nranges = i;
  433.             return;
  434.         }
  435.         if (pn >= smallest) {
  436.             r->range = largest - pn - 1;
  437.             ctx->nranges = i + 1;
  438.             return;
  439.         }
  440.     }
  441. }


  442. static ngx_int_t
  443. ngx_quic_detect_lost(ngx_connection_t *c, ngx_quic_ack_stat_t *st)
  444. {
  445.     uint64_t                pkt_thr;
  446.     ngx_uint_t              i, nlost;
  447.     ngx_msec_t              now, wait, thr, oldest, newest;
  448.     ngx_queue_t            *q;
  449.     ngx_quic_frame_t       *start;
  450.     ngx_quic_send_ctx_t    *ctx;
  451.     ngx_quic_connection_t  *qc;

  452.     qc = ngx_quic_get_connection(c);
  453.     now = ngx_current_msec;
  454.     thr = ngx_quic_time_threshold(qc);

  455. #if (NGX_SUPPRESS_WARN)
  456.     oldest = now;
  457.     newest = now;
  458. #endif

  459.     nlost = 0;

  460.     for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {

  461.         ctx = &qc->send_ctx[i];

  462.         if (ctx->largest_ack == NGX_QUIC_UNSET_PN) {
  463.             continue;
  464.         }

  465.         pkt_thr = ngx_quic_packet_threshold(ctx);

  466.         while (!ngx_queue_empty(&ctx->sent)) {

  467.             q = ngx_queue_head(&ctx->sent);
  468.             start = ngx_queue_data(q, ngx_quic_frame_t, queue);

  469.             if (start->pnum > ctx->largest_ack) {
  470.                 break;
  471.             }

  472.             wait = start->send_time + thr - now;

  473.             ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
  474.                   "quic detect_lost pnum:%uL thr:%M pthr:%uL wait:%i level:%ui",
  475.                   start->pnum, thr, pkt_thr, (ngx_int_t) wait, start->level);

  476.             if ((ngx_msec_int_t) wait > 0
  477.                 && ctx->largest_ack - start->pnum < pkt_thr)
  478.             {
  479.                 break;
  480.             }

  481.             if ((ngx_msec_int_t) (start->send_time - qc->first_rtt) > 0) {

  482.                 if (nlost == 0
  483.                     || (ngx_msec_int_t) (start->send_time - oldest) < 0)
  484.                 {
  485.                     oldest = start->send_time;
  486.                 }

  487.                 if (nlost == 0
  488.                     || (ngx_msec_int_t) (start->send_time - newest) > 0)
  489.                 {
  490.                     newest = start->send_time;
  491.                 }

  492.                 nlost++;
  493.             }

  494.             ngx_quic_resend_frames(c, ctx);
  495.         }
  496.     }


  497.     /* RFC 9002, 7.6.2.  Establishing Persistent Congestion */

  498.     /*
  499.      * Once acknowledged, packets are no longer tracked. Thus no send time
  500.      * information is available for such packets. This limits persistent
  501.      * congestion algorithm to packets mentioned within ACK ranges of the
  502.      * latest ACK frame.
  503.      */

  504.     if (st && nlost >= 2 && ((ngx_msec_int_t) (st->newest - oldest) < 0
  505.                              || (ngx_msec_int_t) (st->oldest - newest) > 0))
  506.     {
  507.         if (newest - oldest > ngx_quic_pcg_duration(c)) {
  508.             ngx_quic_persistent_congestion(c);
  509.         }
  510.     }

  511.     ngx_quic_set_lost_timer(c);

  512.     return NGX_OK;
  513. }


  514. static ngx_msec_t
  515. ngx_quic_pcg_duration(ngx_connection_t *c)
  516. {
  517.     ngx_msec_t              duration;
  518.     ngx_quic_connection_t  *qc;

  519.     qc = ngx_quic_get_connection(c);

  520.     duration = qc->avg_rtt;
  521.     duration += ngx_max(4 * qc->rttvar, NGX_QUIC_TIME_GRANULARITY);
  522.     duration += qc->ctp.max_ack_delay;
  523.     duration *= NGX_QUIC_PERSISTENT_CONGESTION_THR;

  524.     return duration;
  525. }


  526. static void
  527. ngx_quic_persistent_congestion(ngx_connection_t *c)
  528. {
  529.     ngx_quic_congestion_t  *cg;
  530.     ngx_quic_connection_t  *qc;

  531.     qc = ngx_quic_get_connection(c);
  532.     cg = &qc->congestion;

  533.     cg->mtu = qc->path->mtu;
  534.     cg->recovery_start = ngx_quic_oldest_sent_packet(c) - 1;
  535.     cg->window = cg->mtu * 2;

  536.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  537.                    "quic congestion persistent t:%M win:%uz",
  538.                    ngx_current_msec, cg->window);
  539. }


  540. static ngx_msec_t
  541. ngx_quic_oldest_sent_packet(ngx_connection_t *c)
  542. {
  543.     ngx_msec_t              oldest;
  544.     ngx_uint_t              i;
  545.     ngx_queue_t            *q;
  546.     ngx_quic_frame_t       *start;
  547.     ngx_quic_send_ctx_t    *ctx;
  548.     ngx_quic_connection_t  *qc;

  549.     qc = ngx_quic_get_connection(c);
  550.     oldest = ngx_current_msec;

  551.     for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
  552.         ctx = &qc->send_ctx[i];

  553.         if (!ngx_queue_empty(&ctx->sent)) {
  554.             q = ngx_queue_head(&ctx->sent);
  555.             start = ngx_queue_data(q, ngx_quic_frame_t, queue);

  556.             if ((ngx_msec_int_t) (start->send_time - oldest) < 0) {
  557.                 oldest = start->send_time;
  558.             }
  559.         }
  560.     }

  561.     return oldest;
  562. }


  563. void
  564. ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
  565. {
  566.     uint64_t                pnum;
  567.     ngx_queue_t            *q;
  568.     ngx_quic_frame_t       *f, *start;
  569.     ngx_quic_stream_t      *qs;
  570.     ngx_quic_connection_t  *qc;

  571.     qc = ngx_quic_get_connection(c);
  572.     q = ngx_queue_head(&ctx->sent);
  573.     start = ngx_queue_data(q, ngx_quic_frame_t, queue);
  574.     pnum = start->pnum;

  575.     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
  576.                    "quic resend packet pnum:%uL", start->pnum);

  577.     ngx_quic_congestion_lost(c, start);

  578.     do {
  579.         f = ngx_queue_data(q, ngx_quic_frame_t, queue);

  580.         if (f->pnum != pnum) {
  581.             break;
  582.         }

  583.         q = ngx_queue_next(q);

  584.         ngx_queue_remove(&f->queue);

  585.         switch (f->type) {
  586.         case NGX_QUIC_FT_ACK:
  587.         case NGX_QUIC_FT_ACK_ECN:
  588.             if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {
  589.                 /* force generation of most recent acknowledgment */
  590.                 ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
  591.             }

  592.             ngx_quic_free_frame(c, f);
  593.             break;

  594.         case NGX_QUIC_FT_PING:
  595.         case NGX_QUIC_FT_PATH_CHALLENGE:
  596.         case NGX_QUIC_FT_PATH_RESPONSE:
  597.         case NGX_QUIC_FT_CONNECTION_CLOSE:
  598.             ngx_quic_free_frame(c, f);
  599.             break;

  600.         case NGX_QUIC_FT_MAX_DATA:
  601.             f->u.max_data.max_data = qc->streams.recv_max_data;
  602.             ngx_quic_queue_frame(qc, f);
  603.             break;

  604.         case NGX_QUIC_FT_MAX_STREAMS:
  605.         case NGX_QUIC_FT_MAX_STREAMS2:
  606.             f->u.max_streams.limit = f->u.max_streams.bidi
  607.                                      ? qc->streams.client_max_streams_bidi
  608.                                      : qc->streams.client_max_streams_uni;
  609.             ngx_quic_queue_frame(qc, f);
  610.             break;

  611.         case NGX_QUIC_FT_MAX_STREAM_DATA:
  612.             qs = ngx_quic_find_stream(&qc->streams.tree,
  613.                                       f->u.max_stream_data.id);
  614.             if (qs == NULL) {
  615.                 ngx_quic_free_frame(c, f);
  616.                 break;
  617.             }

  618.             f->u.max_stream_data.limit = qs->recv_max_data;
  619.             ngx_quic_queue_frame(qc, f);
  620.             break;

  621.         case NGX_QUIC_FT_STREAM:
  622.             qs = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id);

  623.             if (qs == NULL
  624.                 || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_SENT
  625.                 || qs->send_state == NGX_QUIC_STREAM_SEND_RESET_RECVD)
  626.             {
  627.                 ngx_quic_free_frame(c, f);
  628.                 break;
  629.             }

  630.             /* fall through */

  631.         default:
  632.             ngx_queue_insert_tail(&ctx->frames, &f->queue);
  633.         }

  634.     } while (q != ngx_queue_sentinel(&ctx->sent));

  635.     if (qc->closing) {
  636.         return;
  637.     }

  638.     ngx_post_event(&qc->push, &ngx_posted_events);
  639. }


  640. static void
  641. ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
  642. {
  643.     ngx_uint_t              blocked;
  644.     ngx_msec_t              now, timer;
  645.     ngx_quic_congestion_t  *cg;
  646.     ngx_quic_connection_t  *qc;

  647.     if (f->plen == 0) {
  648.         return;
  649.     }

  650.     qc = ngx_quic_get_connection(c);
  651.     cg = &qc->congestion;

  652.     if (f->pnum < qc->rst_pnum) {
  653.         return;
  654.     }

  655.     blocked = (cg->in_flight >= cg->window) ? 1 : 0;

  656.     cg->in_flight -= f->plen;
  657.     f->plen = 0;

  658.     timer = f->send_time - cg->recovery_start;

  659.     now = ngx_current_msec;

  660.     if ((ngx_msec_int_t) timer <= 0) {
  661.         ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  662.                        "quic congestion lost rec t:%M win:%uz if:%uz",
  663.                        now, cg->window, cg->in_flight);

  664.         goto done;
  665.     }

  666.     if (f->ignore_loss) {
  667.         ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  668.                        "quic congestion lost ignore t:%M win:%uz if:%uz",
  669.                        now, cg->window, cg->in_flight);

  670.         goto done;
  671.     }

  672.     /* RFC 9438, 4.6. Multiplicative Decrease */

  673.     cg->mtu = qc->path->mtu;
  674.     cg->recovery_start = now;
  675.     cg->w_prior = cg->window;
  676.     /* RFC 9438, 4.7. Fast Convergence */
  677.     cg->w_max = (cg->window < cg->w_max)
  678.                 ? cg->window * (10 + NGX_QUIC_CUBIC_BETA) / 20 : cg->window;
  679.     cg->ssthresh = cg->in_flight * NGX_QUIC_CUBIC_BETA / 10;
  680.     cg->window = ngx_max(cg->ssthresh, cg->mtu * 2);
  681.     cg->w_est = cg->window;
  682.     cg->k = now + ngx_quic_congestion_cubic_time(c);
  683.     cg->idle_start = now;

  684.     ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  685.                    "quic congestion lost t:%M win:%uz if:%uz",
  686.                    now, cg->window, cg->in_flight);

  687. done:

  688.     if (blocked && cg->in_flight < cg->window) {
  689.         ngx_post_event(&qc->push, &ngx_posted_events);
  690.     }
  691. }


  692. static ngx_msec_t
  693. ngx_quic_congestion_cubic_time(ngx_connection_t *c)
  694. {
  695.     int64_t                 v, x, d, cc;
  696.     ngx_uint_t              n;
  697.     ngx_quic_congestion_t  *cg;
  698.     ngx_quic_connection_t  *qc;

  699.     qc = ngx_quic_get_connection(c);
  700.     cg = &qc->congestion;

  701.     /*
  702.      * RFC 9438, Figure 2
  703.      *
  704.      *   k_msec = ((w_max - cwnd_epoch) / C / mtu) ^ 1/3 * 1000
  705.      */

  706.     if (cg->w_max <= cg->window) {
  707.         return 0;
  708.     }

  709.     cc = 10000000000ll / (int64_t) cg->mtu / NGX_QUIC_CUBIC_C;
  710.     v = (int64_t) (cg->w_max - cg->window) * cc;

  711.     /*
  712.      * Newton-Raphson method for x ^ 3 = v:
  713.      *
  714.      *   x_next = (2 * x_prev + v / x_prev ^ 2) / 3
  715.      */

  716.     x = 5000;

  717.     for (n = 1; n <= 10; n++) {
  718.         d =  (v / x / x - x) / 3;
  719.         x += d;

  720.         if (ngx_abs(d) <= 100) {
  721.             break;
  722.         }
  723.     }

  724.     if (x > NGX_MAX_SIZE_T_VALUE) {
  725.         return NGX_MAX_SIZE_T_VALUE;
  726.     }

  727.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  728.                    "quic cubic time:%L n:%ui", x, n);

  729.     return x;
  730. }


  731. void
  732. ngx_quic_set_lost_timer(ngx_connection_t *c)
  733. {
  734.     uint64_t                pkt_thr;
  735.     ngx_uint_t              i;
  736.     ngx_msec_t              now;
  737.     ngx_queue_t            *q;
  738.     ngx_msec_int_t          lost, pto, w;
  739.     ngx_quic_frame_t       *f;
  740.     ngx_quic_send_ctx_t    *ctx;
  741.     ngx_quic_connection_t  *qc;

  742.     qc = ngx_quic_get_connection(c);
  743.     now = ngx_current_msec;

  744.     lost = -1;
  745.     pto = -1;

  746.     for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
  747.         ctx = &qc->send_ctx[i];

  748.         if (ngx_queue_empty(&ctx->sent)) {
  749.             continue;
  750.         }

  751.         if (ctx->largest_ack != NGX_QUIC_UNSET_PN) {
  752.             q = ngx_queue_head(&ctx->sent);
  753.             f = ngx_queue_data(q, ngx_quic_frame_t, queue);
  754.             w = (ngx_msec_int_t)
  755.                             (f->send_time + ngx_quic_time_threshold(qc) - now);

  756.             if (f->pnum <= ctx->largest_ack) {
  757.                 pkt_thr = ngx_quic_packet_threshold(ctx);

  758.                 if (w < 0 || ctx->largest_ack - f->pnum >= pkt_thr) {
  759.                     w = 0;
  760.                 }

  761.                 if (lost == -1 || w < lost) {
  762.                     lost = w;
  763.                 }
  764.             }
  765.         }

  766.         q = ngx_queue_last(&ctx->sent);
  767.         f = ngx_queue_data(q, ngx_quic_frame_t, queue);
  768.         w = (ngx_msec_int_t)
  769.                 (f->send_time + (ngx_quic_pto(c, ctx) << qc->pto_count) - now);

  770.         if (w < 0) {
  771.             w = 0;
  772.         }

  773.         if (pto == -1 || w < pto) {
  774.             pto = w;
  775.         }
  776.     }

  777.     if (qc->pto.timer_set) {
  778.         ngx_del_timer(&qc->pto);
  779.     }

  780.     if (lost != -1) {
  781.         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
  782.                        "quic lost timer lost:%M", lost);

  783.         qc->pto.handler = ngx_quic_lost_handler;
  784.         ngx_add_timer(&qc->pto, lost);
  785.         return;
  786.     }

  787.     if (pto != -1) {
  788.         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
  789.                        "quic lost timer pto:%M", pto);

  790.         qc->pto.handler = ngx_quic_pto_handler;
  791.         ngx_add_timer(&qc->pto, pto);
  792.         return;
  793.     }

  794.     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic lost timer unset");
  795. }


  796. ngx_msec_t
  797. ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
  798. {
  799.     ngx_msec_t              duration;
  800.     ngx_quic_connection_t  *qc;

  801.     qc = ngx_quic_get_connection(c);

  802.     /* RFC 9002, Appendix A.8.  Setting the Loss Detection Timer */

  803.     duration = qc->avg_rtt;
  804.     duration += ngx_max(4 * qc->rttvar, NGX_QUIC_TIME_GRANULARITY);

  805.     if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION && c->ssl->handshaked) {
  806.         duration += qc->ctp.max_ack_delay;
  807.     }

  808.     return duration;
  809. }


  810. static
  811. void ngx_quic_lost_handler(ngx_event_t *ev)
  812. {
  813.     ngx_connection_t  *c;

  814.     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic lost timer");

  815.     c = ev->data;

  816.     if (ngx_quic_detect_lost(c, NULL) != NGX_OK) {
  817.         ngx_quic_close_connection(c, NGX_ERROR);
  818.         return;
  819.     }

  820.     ngx_quic_connstate_dbg(c);
  821. }


  822. void
  823. ngx_quic_pto_handler(ngx_event_t *ev)
  824. {
  825.     ngx_uint_t              i, n;
  826.     ngx_msec_t              now;
  827.     ngx_queue_t            *q;
  828.     ngx_msec_int_t          w;
  829.     ngx_connection_t       *c;
  830.     ngx_quic_frame_t       *f;
  831.     ngx_quic_send_ctx_t    *ctx;
  832.     ngx_quic_connection_t  *qc;

  833.     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic pto timer");

  834.     c = ev->data;
  835.     qc = ngx_quic_get_connection(c);
  836.     now = ngx_current_msec;

  837.     for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {

  838.         ctx = &qc->send_ctx[i];

  839.         if (ngx_queue_empty(&ctx->sent)) {
  840.             continue;
  841.         }

  842.         q = ngx_queue_last(&ctx->sent);
  843.         f = ngx_queue_data(q, ngx_quic_frame_t, queue);
  844.         w = (ngx_msec_int_t)
  845.                 (f->send_time + (ngx_quic_pto(c, ctx) << qc->pto_count) - now);

  846.         if (f->pnum <= ctx->largest_ack
  847.             && ctx->largest_ack != NGX_QUIC_UNSET_PN)
  848.         {
  849.             continue;
  850.         }

  851.         if (w > 0) {
  852.             continue;
  853.         }

  854.         ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
  855.                        "quic pto %s pto_count:%ui",
  856.                        ngx_quic_level_name(ctx->level), qc->pto_count);

  857.         for (n = 0; n < 2; n++) {

  858.             f = ngx_quic_alloc_frame(c);
  859.             if (f == NULL) {
  860.                 goto failed;
  861.             }

  862.             f->level = ctx->level;
  863.             f->type = NGX_QUIC_FT_PING;
  864.             f->ignore_congestion = 1;

  865.             if (ngx_quic_frame_sendto(c, f, 0, qc->path) == NGX_ERROR) {
  866.                 goto failed;
  867.             }
  868.         }
  869.     }

  870.     qc->pto_count++;

  871.     ngx_quic_set_lost_timer(c);

  872.     ngx_quic_connstate_dbg(c);

  873.     return;

  874. failed:

  875.     ngx_quic_close_connection(c, NGX_ERROR);
  876.     return;
  877. }


  878. ngx_int_t
  879. ngx_quic_ack_packet(ngx_connection_t *c, ngx_quic_header_t *pkt)
  880. {
  881.     uint64_t                base, largest, smallest, gs, ge, gap, range, pn;
  882.     uint64_t                prev_pending;
  883.     ngx_uint_t              i, nr;
  884.     ngx_quic_send_ctx_t    *ctx;
  885.     ngx_quic_ack_range_t   *r;
  886.     ngx_quic_connection_t  *qc;

  887.     c->log->action = "preparing ack";

  888.     qc = ngx_quic_get_connection(c);

  889.     ctx = ngx_quic_get_send_ctx(qc, pkt->level);

  890.     ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
  891.                    "quic ngx_quic_ack_packet pn:%uL largest %L fr:%uL"
  892.                    " nranges:%ui", pkt->pn, (int64_t) ctx->largest_range,
  893.                    ctx->first_range, ctx->nranges);

  894.     if (!ngx_quic_keys_available(qc->keys, ctx->level, 1)) {
  895.         return NGX_OK;
  896.     }

  897.     prev_pending = ctx->pending_ack;

  898.     if (pkt->need_ack) {

  899.         ngx_post_event(&qc->push, &ngx_posted_events);

  900.         if (ctx->send_ack == 0) {
  901.             ctx->ack_delay_start = ngx_current_msec;
  902.         }

  903.         ctx->send_ack++;

  904.         if (ctx->pending_ack == NGX_QUIC_UNSET_PN
  905.             || ctx->pending_ack < pkt->pn)
  906.         {
  907.             ctx->pending_ack = pkt->pn;
  908.         }
  909.     }

  910.     base = ctx->largest_range;
  911.     pn = pkt->pn;

  912.     if (base == NGX_QUIC_UNSET_PN) {
  913.         ctx->largest_range = pn;
  914.         ctx->largest_received = pkt->received;
  915.         return NGX_OK;
  916.     }

  917.     if (base == pn) {
  918.         return NGX_OK;
  919.     }

  920.     largest = base;
  921.     smallest = largest - ctx->first_range;

  922.     if (pn > base) {

  923.         if (pn - base == 1) {
  924.             ctx->first_range++;
  925.             ctx->largest_range = pn;
  926.             ctx->largest_received = pkt->received;

  927.             return NGX_OK;

  928.         } else {
  929.             /* new gap in front of current largest */

  930.             /* no place for new range, send current range as is */
  931.             if (ctx->nranges == NGX_QUIC_MAX_RANGES) {

  932.                 if (prev_pending != NGX_QUIC_UNSET_PN) {
  933.                     if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
  934.                         return NGX_ERROR;
  935.                     }
  936.                 }

  937.                 if (prev_pending == ctx->pending_ack || !pkt->need_ack) {
  938.                     ctx->pending_ack = NGX_QUIC_UNSET_PN;
  939.                 }
  940.             }

  941.             gap = pn - base - 2;
  942.             range = ctx->first_range;

  943.             ctx->first_range = 0;
  944.             ctx->largest_range = pn;
  945.             ctx->largest_received = pkt->received;

  946.             /* packet is out of order, force send */
  947.             if (pkt->need_ack) {
  948.                 ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
  949.             }

  950.             i = 0;

  951.             goto insert;
  952.         }
  953.     }

  954.     /*  pn < base, perform lookup in existing ranges */

  955.     /* packet is out of order */
  956.     if (pkt->need_ack) {
  957.         ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
  958.     }

  959.     if (pn >= smallest && pn <= largest) {
  960.         return NGX_OK;
  961.     }

  962. #if (NGX_SUPPRESS_WARN)
  963.     r = NULL;
  964. #endif

  965.     for (i = 0; i < ctx->nranges; i++) {
  966.         r = &ctx->ranges[i];

  967.         ge = smallest - 1;
  968.         gs = ge - r->gap;

  969.         if (pn >= gs && pn <= ge) {

  970.             if (gs == ge) {
  971.                 /* gap size is exactly one packet, now filled */

  972.                 /* data moves to previous range, current is removed */

  973.                 if (i == 0) {
  974.                     ctx->first_range += r->range + 2;

  975.                 } else {
  976.                     ctx->ranges[i - 1].range += r->range + 2;
  977.                 }

  978.                 nr = ctx->nranges - i - 1;
  979.                 if (nr) {
  980.                     ngx_memmove(&ctx->ranges[i], &ctx->ranges[i + 1],
  981.                                 sizeof(ngx_quic_ack_range_t) * nr);
  982.                 }

  983.                 ctx->nranges--;

  984.             } else if (pn == gs) {
  985.                 /* current gap shrinks from tail (current range grows) */
  986.                 r->gap--;
  987.                 r->range++;

  988.             } else if (pn == ge) {
  989.                 /* current gap shrinks from head (previous range grows) */
  990.                 r->gap--;

  991.                 if (i == 0) {
  992.                     ctx->first_range++;

  993.                 } else {
  994.                     ctx->ranges[i - 1].range++;
  995.                 }

  996.             } else {
  997.                 /* current gap is split into two parts */

  998.                 gap = ge - pn - 1;
  999.                 range = 0;

  1000.                 if (ctx->nranges == NGX_QUIC_MAX_RANGES) {
  1001.                     if (prev_pending != NGX_QUIC_UNSET_PN) {
  1002.                         if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
  1003.                             return NGX_ERROR;
  1004.                         }
  1005.                     }

  1006.                     if (prev_pending == ctx->pending_ack || !pkt->need_ack) {
  1007.                         ctx->pending_ack = NGX_QUIC_UNSET_PN;
  1008.                     }
  1009.                 }

  1010.                 r->gap = pn - gs - 1;
  1011.                 goto insert;
  1012.             }

  1013.             return NGX_OK;
  1014.         }

  1015.         largest = smallest - r->gap - 2;
  1016.         smallest = largest - r->range;

  1017.         if (pn >= smallest && pn <= largest) {
  1018.             /* this packet number is already known */
  1019.             return NGX_OK;
  1020.         }

  1021.     }

  1022.     if (pn == smallest - 1) {
  1023.         /* extend first or last range */

  1024.         if (i == 0) {
  1025.             ctx->first_range++;

  1026.         } else {
  1027.             r->range++;
  1028.         }

  1029.         return NGX_OK;
  1030.     }

  1031.     /* nothing found, add new range at the tail  */

  1032.     if (ctx->nranges == NGX_QUIC_MAX_RANGES) {
  1033.         /* packet is too old to keep it */

  1034.         if (pkt->need_ack) {
  1035.             return ngx_quic_send_ack_range(c, ctx, pn, pn);
  1036.         }

  1037.         return NGX_OK;
  1038.     }

  1039.     gap = smallest - 2 - pn;
  1040.     range = 0;

  1041. insert:

  1042.     if (ctx->nranges < NGX_QUIC_MAX_RANGES) {
  1043.         ctx->nranges++;
  1044.     }

  1045.     ngx_memmove(&ctx->ranges[i + 1], &ctx->ranges[i],
  1046.                 sizeof(ngx_quic_ack_range_t) * (ctx->nranges - i - 1));

  1047.     ctx->ranges[i].gap = gap;
  1048.     ctx->ranges[i].range = range;

  1049.     return NGX_OK;
  1050. }


  1051. ngx_int_t
  1052. ngx_quic_generate_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
  1053. {
  1054.     ngx_msec_t              delay;
  1055.     ngx_quic_connection_t  *qc;

  1056.     if (!ctx->send_ack) {
  1057.         return NGX_OK;
  1058.     }

  1059.     if (ctx->level == NGX_QUIC_ENCRYPTION_APPLICATION) {

  1060.         delay = ngx_current_msec - ctx->ack_delay_start;
  1061.         qc = ngx_quic_get_connection(c);

  1062.         if (ngx_queue_empty(&ctx->frames)
  1063.             && ctx->send_ack < NGX_QUIC_MAX_ACK_GAP
  1064.             && delay < qc->tp.max_ack_delay)
  1065.         {
  1066.             if (!qc->push.timer_set && !qc->closing) {
  1067.                 ngx_add_timer(&qc->push,
  1068.                               qc->tp.max_ack_delay - delay);
  1069.             }

  1070.             return NGX_OK;
  1071.         }
  1072.     }

  1073.     if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
  1074.         return NGX_ERROR;
  1075.     }

  1076.     ctx->send_ack = 0;

  1077.     return NGX_OK;
  1078. }