src/http/v3/ngx_http_v3_table.c - nginx

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


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


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. #define ngx_http_v3_table_entry_size(n, v) ((n)->len + (v)->len + 32)


  9. static ngx_int_t ngx_http_v3_evict(ngx_connection_t *c, size_t target);
  10. static void ngx_http_v3_unblock(void *data);
  11. static ngx_int_t ngx_http_v3_new_entry(ngx_connection_t *c);


  12. typedef struct {
  13.     ngx_queue_t        queue;
  14.     ngx_connection_t  *connection;
  15.     ngx_uint_t        *nblocked;
  16. } ngx_http_v3_block_t;


  17. static ngx_http_v3_field_t  ngx_http_v3_static_table[] = {

  18.     { ngx_string(":authority"),            ngx_string("") },
  19.     { ngx_string(":path"),                 ngx_string("/") },
  20.     { ngx_string("age"),                   ngx_string("0") },
  21.     { ngx_string("content-disposition"),   ngx_string("") },
  22.     { ngx_string("content-length"),        ngx_string("0") },
  23.     { ngx_string("cookie"),                ngx_string("") },
  24.     { ngx_string("date"),                  ngx_string("") },
  25.     { ngx_string("etag"),                  ngx_string("") },
  26.     { ngx_string("if-modified-since"),     ngx_string("") },
  27.     { ngx_string("if-none-match"),         ngx_string("") },
  28.     { ngx_string("last-modified"),         ngx_string("") },
  29.     { ngx_string("link"),                  ngx_string("") },
  30.     { ngx_string("location"),              ngx_string("") },
  31.     { ngx_string("referer"),               ngx_string("") },
  32.     { ngx_string("set-cookie"),            ngx_string("") },
  33.     { ngx_string(":method"),               ngx_string("CONNECT") },
  34.     { ngx_string(":method"),               ngx_string("DELETE") },
  35.     { ngx_string(":method"),               ngx_string("GET") },
  36.     { ngx_string(":method"),               ngx_string("HEAD") },
  37.     { ngx_string(":method"),               ngx_string("OPTIONS") },
  38.     { ngx_string(":method"),               ngx_string("POST") },
  39.     { ngx_string(":method"),               ngx_string("PUT") },
  40.     { ngx_string(":scheme"),               ngx_string("http") },
  41.     { ngx_string(":scheme"),               ngx_string("https") },
  42.     { ngx_string(":status"),               ngx_string("103") },
  43.     { ngx_string(":status"),               ngx_string("200") },
  44.     { ngx_string(":status"),               ngx_string("304") },
  45.     { ngx_string(":status"),               ngx_string("404") },
  46.     { ngx_string(":status"),               ngx_string("503") },
  47.     { ngx_string("accept"),                ngx_string("*/*") },
  48.     { ngx_string("accept"),
  49.           ngx_string("application/dns-message") },
  50.     { ngx_string("accept-encoding"),       ngx_string("gzip, deflate, br") },
  51.     { ngx_string("accept-ranges"),         ngx_string("bytes") },
  52.     { ngx_string("access-control-allow-headers"),
  53.                                            ngx_string("cache-control") },
  54.     { ngx_string("access-control-allow-headers"),
  55.                                            ngx_string("content-type") },
  56.     { ngx_string("access-control-allow-origin"),
  57.                                            ngx_string("*") },
  58.     { ngx_string("cache-control"),         ngx_string("max-age=0") },
  59.     { ngx_string("cache-control"),         ngx_string("max-age=2592000") },
  60.     { ngx_string("cache-control"),         ngx_string("max-age=604800") },
  61.     { ngx_string("cache-control"),         ngx_string("no-cache") },
  62.     { ngx_string("cache-control"),         ngx_string("no-store") },
  63.     { ngx_string("cache-control"),
  64.           ngx_string("public, max-age=31536000") },
  65.     { ngx_string("content-encoding"),      ngx_string("br") },
  66.     { ngx_string("content-encoding"),      ngx_string("gzip") },
  67.     { ngx_string("content-type"),
  68.           ngx_string("application/dns-message") },
  69.     { ngx_string("content-type"),
  70.           ngx_string("application/javascript") },
  71.     { ngx_string("content-type"),          ngx_string("application/json") },
  72.     { ngx_string("content-type"),
  73.           ngx_string("application/x-www-form-urlencoded") },
  74.     { ngx_string("content-type"),          ngx_string("image/gif") },
  75.     { ngx_string("content-type"),          ngx_string("image/jpeg") },
  76.     { ngx_string("content-type"),          ngx_string("image/png") },
  77.     { ngx_string("content-type"),          ngx_string("text/css") },
  78.     { ngx_string("content-type"),
  79.           ngx_string("text/html;charset=utf-8") },
  80.     { ngx_string("content-type"),          ngx_string("text/plain") },
  81.     { ngx_string("content-type"),
  82.           ngx_string("text/plain;charset=utf-8") },
  83.     { ngx_string("range"),                 ngx_string("bytes=0-") },
  84.     { ngx_string("strict-transport-security"),
  85.                                            ngx_string("max-age=31536000") },
  86.     { ngx_string("strict-transport-security"),
  87.           ngx_string("max-age=31536000;includesubdomains") },
  88.     { ngx_string("strict-transport-security"),
  89.           ngx_string("max-age=31536000;includesubdomains;preload") },
  90.     { ngx_string("vary"),                  ngx_string("accept-encoding") },
  91.     { ngx_string("vary"),                  ngx_string("origin") },
  92.     { ngx_string("x-content-type-options"),
  93.                                            ngx_string("nosniff") },
  94.     { ngx_string("x-xss-protection"),      ngx_string("1;mode=block") },
  95.     { ngx_string(":status"),               ngx_string("100") },
  96.     { ngx_string(":status"),               ngx_string("204") },
  97.     { ngx_string(":status"),               ngx_string("206") },
  98.     { ngx_string(":status"),               ngx_string("302") },
  99.     { ngx_string(":status"),               ngx_string("400") },
  100.     { ngx_string(":status"),               ngx_string("403") },
  101.     { ngx_string(":status"),               ngx_string("421") },
  102.     { ngx_string(":status"),               ngx_string("425") },
  103.     { ngx_string(":status"),               ngx_string("500") },
  104.     { ngx_string("accept-language"),       ngx_string("") },
  105.     { ngx_string("access-control-allow-credentials"),
  106.                                            ngx_string("FALSE") },
  107.     { ngx_string("access-control-allow-credentials"),
  108.                                            ngx_string("TRUE") },
  109.     { ngx_string("access-control-allow-headers"),
  110.                                            ngx_string("*") },
  111.     { ngx_string("access-control-allow-methods"),
  112.                                            ngx_string("get") },
  113.     { ngx_string("access-control-allow-methods"),
  114.                                            ngx_string("get, post, options") },
  115.     { ngx_string("access-control-allow-methods"),
  116.                                            ngx_string("options") },
  117.     { ngx_string("access-control-expose-headers"),
  118.                                            ngx_string("content-length") },
  119.     { ngx_string("access-control-request-headers"),
  120.                                            ngx_string("content-type") },
  121.     { ngx_string("access-control-request-method"),
  122.                                            ngx_string("get") },
  123.     { ngx_string("access-control-request-method"),
  124.                                            ngx_string("post") },
  125.     { ngx_string("alt-svc"),               ngx_string("clear") },
  126.     { ngx_string("authorization"),         ngx_string("") },
  127.     { ngx_string("content-security-policy"),
  128.           ngx_string("script-src 'none';object-src 'none';base-uri 'none'") },
  129.     { ngx_string("early-data"),            ngx_string("1") },
  130.     { ngx_string("expect-ct"),             ngx_string("") },
  131.     { ngx_string("forwarded"),             ngx_string("") },
  132.     { ngx_string("if-range"),              ngx_string("") },
  133.     { ngx_string("origin"),                ngx_string("") },
  134.     { ngx_string("purpose"),               ngx_string("prefetch") },
  135.     { ngx_string("server"),                ngx_string("") },
  136.     { ngx_string("timing-allow-origin"),   ngx_string("*") },
  137.     { ngx_string("upgrade-insecure-requests"),
  138.                                            ngx_string("1") },
  139.     { ngx_string("user-agent"),            ngx_string("") },
  140.     { ngx_string("x-forwarded-for"),       ngx_string("") },
  141.     { ngx_string("x-frame-options"),       ngx_string("deny") },
  142.     { ngx_string("x-frame-options"),       ngx_string("sameorigin") }
  143. };


  144. ngx_buf_t *
  145. ngx_http_v3_get_insert_buffer(ngx_connection_t *c)
  146. {
  147.     ngx_http_v3_session_t        *h3c;
  148.     ngx_http_v3_dynamic_table_t  *dt;

  149.     h3c = ngx_http_v3_get_session(c);
  150.     dt = &h3c->table;

  151.     if (dt->insert_buffer == NULL) {
  152.         dt->insert_buffer = ngx_create_temp_buf(c->pool, dt->capacity);
  153.         if (dt->insert_buffer == NULL) {
  154.             return NULL;
  155.         }
  156.     }

  157.     dt->insert_buffer->last = dt->insert_buffer->pos;

  158.     return dt->insert_buffer;
  159. }


  160. ngx_int_t
  161. ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic,
  162.     ngx_uint_t index, ngx_str_t *value)
  163. {
  164.     ngx_str_t                     name;
  165.     ngx_http_v3_session_t        *h3c;
  166.     ngx_http_v3_dynamic_table_t  *dt;

  167.     if (dynamic) {
  168.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  169.                        "http3 ref insert dynamic[%ui] \"%V\"", index, value);

  170.         h3c = ngx_http_v3_get_session(c);
  171.         dt = &h3c->table;

  172.         if (dt->base + dt->nelts <= index) {
  173.             return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  174.         }

  175.         index = dt->base + dt->nelts - 1 - index;

  176.         if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) {
  177.             return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  178.         }

  179.     } else {
  180.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  181.                        "http3 ref insert static[%ui] \"%V\"", index, value);

  182.         if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) {
  183.             return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  184.         }
  185.     }

  186.     return ngx_http_v3_insert(c, &name, value);
  187. }


  188. ngx_int_t
  189. ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value)
  190. {
  191.     u_char                       *p;
  192.     size_t                        size;
  193.     ngx_http_v3_field_t          *field;
  194.     ngx_http_v3_session_t        *h3c;
  195.     ngx_http_v3_dynamic_table_t  *dt;

  196.     size = ngx_http_v3_table_entry_size(name, value);

  197.     h3c = ngx_http_v3_get_session(c);
  198.     dt = &h3c->table;

  199.     if (size > dt->capacity) {
  200.         ngx_log_error(NGX_LOG_ERR, c->log, 0,
  201.                       "not enough dynamic table capacity");
  202.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  203.     }

  204.     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
  205.                    "http3 insert [%ui] \"%V\":\"%V\", size:%uz",
  206.                    dt->base + dt->nelts, name, value, size);

  207.     p = ngx_alloc(sizeof(ngx_http_v3_field_t) + name->len + value->len,
  208.                   c->log);
  209.     if (p == NULL) {
  210.         return NGX_ERROR;
  211.     }

  212.     field = (ngx_http_v3_field_t *) p;

  213.     field->name.data = p + sizeof(ngx_http_v3_field_t);
  214.     field->name.len = name->len;
  215.     field->value.data = ngx_cpymem(field->name.data, name->data, name->len);
  216.     field->value.len = value->len;
  217.     ngx_memcpy(field->value.data, value->data, value->len);

  218.     dt->elts[dt->nelts++] = field;
  219.     dt->size += size;

  220.     dt->insert_count++;

  221.     if (ngx_http_v3_evict(c, dt->capacity) != NGX_OK) {
  222.         return NGX_ERROR;
  223.     }

  224.     ngx_post_event(&dt->send_insert_count, &ngx_posted_events);

  225.     if (ngx_http_v3_new_entry(c) != NGX_OK) {
  226.         return NGX_ERROR;
  227.     }

  228.     return NGX_OK;
  229. }


  230. void
  231. ngx_http_v3_inc_insert_count_handler(ngx_event_t *ev)
  232. {
  233.     ngx_connection_t             *c;
  234.     ngx_http_v3_session_t        *h3c;
  235.     ngx_http_v3_dynamic_table_t  *dt;

  236.     c = ev->data;

  237.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
  238.                    "http3 inc insert count handler");

  239.     h3c = ngx_http_v3_get_session(c);
  240.     dt = &h3c->table;

  241.     if (dt->insert_count > dt->ack_insert_count) {
  242.         if (ngx_http_v3_send_inc_insert_count(c,
  243.                                        dt->insert_count - dt->ack_insert_count)
  244.             != NGX_OK)
  245.         {
  246.             return;
  247.         }

  248.         dt->ack_insert_count = dt->insert_count;
  249.     }
  250. }


  251. ngx_int_t
  252. ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity)
  253. {
  254.     ngx_uint_t                     max, prev_max;
  255.     ngx_http_v3_field_t          **elts;
  256.     ngx_http_v3_session_t         *h3c;
  257.     ngx_http_v3_srv_conf_t        *h3scf;
  258.     ngx_http_v3_dynamic_table_t   *dt;

  259.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  260.                    "http3 set capacity %ui", capacity);

  261.     h3c = ngx_http_v3_get_session(c);
  262.     h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);

  263.     if (capacity > h3scf->max_table_capacity) {
  264.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  265.                       "client exceeded http3_max_table_capacity limit");
  266.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  267.     }

  268.     if (ngx_http_v3_evict(c, capacity) != NGX_OK) {
  269.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  270.     }

  271.     dt = &h3c->table;
  272.     max = capacity / 32;
  273.     prev_max = dt->capacity / 32;

  274.     if (max > prev_max) {
  275.         elts = ngx_alloc((max + 1) * sizeof(void *), c->log);
  276.         if (elts == NULL) {
  277.             return NGX_ERROR;
  278.         }

  279.         if (dt->elts) {
  280.             ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *));
  281.             ngx_free(dt->elts);
  282.         }

  283.         dt->elts = elts;
  284.     }

  285.     dt->capacity = capacity;

  286.     return NGX_OK;
  287. }


  288. void
  289. ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c)
  290. {
  291.     ngx_uint_t                    n;
  292.     ngx_http_v3_dynamic_table_t  *dt;

  293.     dt = &h3c->table;

  294.     if (dt->elts == NULL) {
  295.         return;
  296.     }

  297.     for (n = 0; n < dt->nelts; n++) {
  298.         ngx_free(dt->elts[n]);
  299.     }

  300.     ngx_free(dt->elts);
  301. }


  302. static ngx_int_t
  303. ngx_http_v3_evict(ngx_connection_t *c, size_t target)
  304. {
  305.     size_t                        size;
  306.     ngx_uint_t                    n;
  307.     ngx_http_v3_field_t          *field;
  308.     ngx_http_v3_session_t        *h3c;
  309.     ngx_http_v3_dynamic_table_t  *dt;

  310.     h3c = ngx_http_v3_get_session(c);
  311.     dt = &h3c->table;
  312.     n = 0;

  313.     while (dt->size > target) {
  314.         field = dt->elts[n++];
  315.         size = ngx_http_v3_table_entry_size(&field->name, &field->value);

  316.         ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,
  317.                        "http3 evict [%ui] \"%V\":\"%V\" size:%uz",
  318.                        dt->base, &field->name, &field->value, size);

  319.         ngx_free(field);
  320.         dt->size -= size;
  321.     }

  322.     if (n) {
  323.         dt->nelts -= n;
  324.         dt->base += n;
  325.         ngx_memmove(dt->elts, &dt->elts[n], dt->nelts * sizeof(void *));
  326.     }

  327.     return NGX_OK;
  328. }


  329. ngx_int_t
  330. ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index)
  331. {
  332.     ngx_str_t                     name, value;
  333.     ngx_http_v3_session_t        *h3c;
  334.     ngx_http_v3_dynamic_table_t  *dt;

  335.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 duplicate %ui", index);

  336.     h3c = ngx_http_v3_get_session(c);
  337.     dt = &h3c->table;

  338.     if (dt->base + dt->nelts <= index) {
  339.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  340.     }

  341.     index = dt->base + dt->nelts - 1 - index;

  342.     if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) {
  343.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  344.     }

  345.     return ngx_http_v3_insert(c, &name, &value);
  346. }


  347. ngx_int_t
  348. ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id)
  349. {
  350.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  351.                    "http3 ack section %ui", stream_id);

  352.     /* we do not use dynamic tables */

  353.     return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR;
  354. }


  355. ngx_int_t
  356. ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc)
  357. {
  358.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  359.                    "http3 increment insert count %ui", inc);

  360.     /* we do not use dynamic tables */

  361.     return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR;
  362. }


  363. ngx_int_t
  364. ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index,
  365.     ngx_str_t *name, ngx_str_t *value)
  366. {
  367.     ngx_uint_t            nelts;
  368.     ngx_http_v3_field_t  *field;

  369.     nelts = sizeof(ngx_http_v3_static_table)
  370.             / sizeof(ngx_http_v3_static_table[0]);

  371.     if (index >= nelts) {
  372.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  373.                        "http3 static[%ui] lookup out of bounds: %ui",
  374.                        index, nelts);
  375.         return NGX_ERROR;
  376.     }

  377.     field = &ngx_http_v3_static_table[index];

  378.     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
  379.                    "http3 static[%ui] lookup \"%V\":\"%V\"",
  380.                    index, &field->name, &field->value);

  381.     if (name) {
  382.         *name = field->name;
  383.     }

  384.     if (value) {
  385.         *value = field->value;
  386.     }

  387.     return NGX_OK;
  388. }


  389. ngx_int_t
  390. ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name,
  391.     ngx_str_t *value)
  392. {
  393.     ngx_http_v3_field_t          *field;
  394.     ngx_http_v3_session_t        *h3c;
  395.     ngx_http_v3_dynamic_table_t  *dt;

  396.     h3c = ngx_http_v3_get_session(c);
  397.     dt = &h3c->table;

  398.     if (index < dt->base || index - dt->base >= dt->nelts) {
  399.         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
  400.                        "http3 dynamic[%ui] lookup out of bounds: [%ui,%ui]",
  401.                        index, dt->base, dt->base + dt->nelts);
  402.         return NGX_ERROR;
  403.     }

  404.     field = dt->elts[index - dt->base];

  405.     ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
  406.                    "http3 dynamic[%ui] lookup \"%V\":\"%V\"",
  407.                    index, &field->name, &field->value);

  408.     if (name) {
  409.         *name = field->name;
  410.     }

  411.     if (value) {
  412.         *value = field->value;
  413.     }

  414.     return NGX_OK;
  415. }


  416. ngx_int_t
  417. ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count)
  418. {
  419.     ngx_uint_t                    max_entries, full_range, max_value,
  420.                                   max_wrapped, req_insert_count;
  421.     ngx_http_v3_srv_conf_t       *h3scf;
  422.     ngx_http_v3_session_t        *h3c;
  423.     ngx_http_v3_dynamic_table_t  *dt;

  424.     /* QPACK 4.5.1.1. Required Insert Count */

  425.     if (*insert_count == 0) {
  426.         return NGX_OK;
  427.     }

  428.     h3c = ngx_http_v3_get_session(c);
  429.     dt = &h3c->table;

  430.     h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);

  431.     max_entries = h3scf->max_table_capacity / 32;
  432.     full_range = 2 * max_entries;

  433.     if (*insert_count > full_range) {
  434.         return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  435.     }

  436.     max_value = dt->base + dt->nelts + max_entries;
  437.     max_wrapped = (max_value / full_range) * full_range;
  438.     req_insert_count = max_wrapped + *insert_count - 1;

  439.     if (req_insert_count > max_value) {
  440.         if (req_insert_count <= full_range) {
  441.             return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  442.         }

  443.         req_insert_count -= full_range;
  444.     }

  445.     if (req_insert_count == 0) {
  446.         return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  447.     }

  448.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  449.                    "http3 decode insert_count %ui -> %ui",
  450.                    *insert_count, req_insert_count);

  451.     *insert_count = req_insert_count;

  452.     return NGX_OK;
  453. }


  454. ngx_int_t
  455. ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count)
  456. {
  457.     size_t                        n;
  458.     ngx_pool_cleanup_t           *cln;
  459.     ngx_http_v3_block_t          *block;
  460.     ngx_http_v3_session_t        *h3c;
  461.     ngx_http_v3_srv_conf_t       *h3scf;
  462.     ngx_http_v3_dynamic_table_t  *dt;

  463.     h3c = ngx_http_v3_get_session(c);
  464.     dt = &h3c->table;

  465.     n = dt->base + dt->nelts;

  466.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  467.                    "http3 check insert count req:%ui, have:%ui",
  468.                    insert_count, n);

  469.     if (n >= insert_count) {
  470.         return NGX_OK;
  471.     }

  472.     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 block stream");

  473.     block = NULL;

  474.     for (cln = c->pool->cleanup; cln; cln = cln->next) {
  475.         if (cln->handler == ngx_http_v3_unblock) {
  476.             block = cln->data;
  477.             break;
  478.         }
  479.     }

  480.     if (block == NULL) {
  481.         cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t));
  482.         if (cln == NULL) {
  483.             return NGX_ERROR;
  484.         }

  485.         cln->handler = ngx_http_v3_unblock;

  486.         block = cln->data;
  487.         block->queue.prev = NULL;
  488.         block->connection = c;
  489.         block->nblocked = &h3c->nblocked;
  490.     }

  491.     if (block->queue.prev == NULL) {
  492.         h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);

  493.         if (h3c->nblocked == h3scf->max_blocked_streams) {
  494.             ngx_log_error(NGX_LOG_INFO, c->log, 0,
  495.                           "client exceeded http3_max_blocked_streams limit");

  496.             ngx_http_v3_finalize_connection(c,
  497.                                           NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED,
  498.                                           "too many blocked streams");
  499.             return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  500.         }

  501.         h3c->nblocked++;
  502.         ngx_queue_insert_tail(&h3c->blocked, &block->queue);
  503.     }

  504.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  505.                    "http3 blocked:%ui", h3c->nblocked);

  506.     return NGX_BUSY;
  507. }


  508. void
  509. ngx_http_v3_ack_insert_count(ngx_connection_t *c, uint64_t insert_count)
  510. {
  511.     ngx_http_v3_session_t        *h3c;
  512.     ngx_http_v3_dynamic_table_t  *dt;

  513.     h3c = ngx_http_v3_get_session(c);
  514.     dt = &h3c->table;

  515.     if (dt->ack_insert_count < insert_count) {
  516.         dt->ack_insert_count = insert_count;
  517.     }
  518. }


  519. static void
  520. ngx_http_v3_unblock(void *data)
  521. {
  522.     ngx_http_v3_block_t  *block = data;

  523.     if (block->queue.prev) {
  524.         ngx_queue_remove(&block->queue);
  525.         block->queue.prev = NULL;
  526.         (*block->nblocked)--;
  527.     }
  528. }


  529. static ngx_int_t
  530. ngx_http_v3_new_entry(ngx_connection_t *c)
  531. {
  532.     ngx_queue_t            *q;
  533.     ngx_connection_t       *bc;
  534.     ngx_http_v3_block_t    *block;
  535.     ngx_http_v3_session_t  *h3c;

  536.     h3c = ngx_http_v3_get_session(c);

  537.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  538.                    "http3 new dynamic entry, blocked:%ui", h3c->nblocked);

  539.     while (!ngx_queue_empty(&h3c->blocked)) {
  540.         q = ngx_queue_head(&h3c->blocked);
  541.         block = (ngx_http_v3_block_t *) q;
  542.         bc = block->connection;

  543.         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, bc->log, 0, "http3 unblock stream");

  544.         ngx_http_v3_unblock(block);
  545.         ngx_post_event(bc->read, &ngx_posted_events);
  546.     }

  547.     return NGX_OK;
  548. }


  549. ngx_int_t
  550. ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value)
  551. {
  552.     switch (id) {

  553.     case NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY:
  554.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  555.                        "http3 param QPACK_MAX_TABLE_CAPACITY:%uL", value);
  556.         break;

  557.     case NGX_HTTP_V3_PARAM_MAX_FIELD_SECTION_SIZE:
  558.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  559.                        "http3 param SETTINGS_MAX_FIELD_SECTION_SIZE:%uL",
  560.                        value);
  561.         break;

  562.     case NGX_HTTP_V3_PARAM_BLOCKED_STREAMS:
  563.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  564.                        "http3 param QPACK_BLOCKED_STREAMS:%uL", value);
  565.         break;

  566.     default:

  567.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  568.                        "http3 param #%uL:%uL", id, value);
  569.     }

  570.     return NGX_OK;
  571. }