src/http/v3/ngx_http_v3_table.c - nginx-1.31.3 nginx/ @ 42f8df65b

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_srv_conf_t       *h3scf;
  149.     ngx_http_v3_dynamic_table_t  *dt;

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

  152.     if (dt->insert_buffer == NULL) {
  153.         h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);

  154.         dt->insert_buffer = ngx_create_temp_buf(c->quic->parent->pool,
  155.                                                 h3scf->max_table_capacity);
  156.         if (dt->insert_buffer == NULL) {
  157.             return NULL;
  158.         }
  159.     }

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

  161.     return dt->insert_buffer;
  162. }


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

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

  173.         h3c = ngx_http_v3_get_session(c);
  174.         dt = &h3c->table;

  175.         if (dt->base + dt->nelts <= index) {
  176.             return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  177.         }

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

  179.         if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) {
  180.             return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  181.         }

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

  185.         if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) {
  186.             return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  187.         }
  188.     }

  189.     return ngx_http_v3_insert(c, &name, value);
  190. }


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

  199.     size = ngx_http_v3_table_entry_size(name, value);

  200.     h3c = ngx_http_v3_get_session(c);
  201.     dt = &h3c->table;

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

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

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

  215.     field = (ngx_http_v3_field_t *) p;

  216.     field->name.data = p + sizeof(ngx_http_v3_field_t);
  217.     field->name.len = name->len;
  218.     field->value.data = ngx_cpymem(field->name.data, name->data, name->len);
  219.     field->value.len = value->len;
  220.     ngx_memcpy(field->value.data, value->data, value->len);

  221.     dt->elts[dt->nelts++] = field;
  222.     dt->size += size;

  223.     dt->insert_count++;

  224.     if (ngx_http_v3_evict(c, dt->capacity) != NGX_OK) {
  225.         return NGX_ERROR;
  226.     }

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

  228.     if (ngx_http_v3_new_entry(c) != NGX_OK) {
  229.         return NGX_ERROR;
  230.     }

  231.     return NGX_OK;
  232. }


  233. void
  234. ngx_http_v3_inc_insert_count_handler(ngx_event_t *ev)
  235. {
  236.     ngx_connection_t             *c;
  237.     ngx_http_v3_session_t        *h3c;
  238.     ngx_http_v3_dynamic_table_t  *dt;

  239.     c = ev->data;

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

  242.     h3c = ngx_http_v3_get_session(c);
  243.     dt = &h3c->table;

  244.     if (dt->insert_count > dt->ack_insert_count) {
  245.         if (ngx_http_v3_send_inc_insert_count(c,
  246.                                        dt->insert_count - dt->ack_insert_count)
  247.             != NGX_OK)
  248.         {
  249.             return;
  250.         }

  251.         dt->ack_insert_count = dt->insert_count;
  252.     }
  253. }


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

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

  264.     h3c = ngx_http_v3_get_session(c);
  265.     h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);

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

  271.     if (ngx_http_v3_evict(c, capacity) != NGX_OK) {
  272.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  273.     }

  274.     dt = &h3c->table;
  275.     max = capacity / 32;
  276.     prev_max = dt->capacity / 32;

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

  282.         if (dt->elts) {
  283.             ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *));
  284.             ngx_free(dt->elts);
  285.         }

  286.         dt->elts = elts;
  287.     }

  288.     dt->capacity = capacity;

  289.     return NGX_OK;
  290. }


  291. void
  292. ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c)
  293. {
  294.     ngx_uint_t                    n;
  295.     ngx_http_v3_dynamic_table_t  *dt;

  296.     dt = &h3c->table;

  297.     if (dt->elts == NULL) {
  298.         return;
  299.     }

  300.     for (n = 0; n < dt->nelts; n++) {
  301.         ngx_free(dt->elts[n]);
  302.     }

  303.     ngx_free(dt->elts);
  304. }


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

  313.     h3c = ngx_http_v3_get_session(c);
  314.     dt = &h3c->table;
  315.     n = 0;

  316.     while (dt->size > target) {
  317.         field = dt->elts[n++];
  318.         size = ngx_http_v3_table_entry_size(&field->name, &field->value);

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

  322.         ngx_free(field);
  323.         dt->size -= size;
  324.     }

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

  330.     return NGX_OK;
  331. }


  332. ngx_int_t
  333. ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index)
  334. {
  335.     ngx_str_t                     name, value;
  336.     ngx_http_v3_session_t        *h3c;
  337.     ngx_http_v3_dynamic_table_t  *dt;

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

  339.     h3c = ngx_http_v3_get_session(c);
  340.     dt = &h3c->table;

  341.     if (dt->base + dt->nelts <= index) {
  342.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  343.     }

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

  345.     if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) {
  346.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  347.     }

  348.     return ngx_http_v3_insert(c, &name, &value);
  349. }


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

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

  356.     return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR;
  357. }


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

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

  364.     return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR;
  365. }


  366. ngx_int_t
  367. ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index,
  368.     ngx_str_t *name, ngx_str_t *value)
  369. {
  370.     ngx_uint_t            nelts;
  371.     ngx_http_v3_field_t  *field;

  372.     nelts = sizeof(ngx_http_v3_static_table)
  373.             / sizeof(ngx_http_v3_static_table[0]);

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

  380.     field = &ngx_http_v3_static_table[index];

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

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

  387.     if (value) {
  388.         *value = field->value;
  389.     }

  390.     return NGX_OK;
  391. }


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

  399.     h3c = ngx_http_v3_get_session(c);
  400.     dt = &h3c->table;

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

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

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

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

  414.     if (value) {
  415.         *value = field->value;
  416.     }

  417.     return NGX_OK;
  418. }


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

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

  428.     if (*insert_count == 0) {
  429.         return NGX_OK;
  430.     }

  431.     h3c = ngx_http_v3_get_session(c);
  432.     dt = &h3c->table;

  433.     h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);

  434.     max_entries = h3scf->max_table_capacity / 32;
  435.     full_range = 2 * max_entries;

  436.     if (*insert_count > full_range) {
  437.         return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  438.     }

  439.     max_value = dt->base + dt->nelts + max_entries;
  440.     max_wrapped = (max_value / full_range) * full_range;
  441.     req_insert_count = max_wrapped + *insert_count - 1;

  442.     if (req_insert_count > max_value) {
  443.         if (req_insert_count <= full_range) {
  444.             return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  445.         }

  446.         req_insert_count -= full_range;
  447.     }

  448.     if (req_insert_count == 0) {
  449.         return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  450.     }

  451.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  452.                    "http3 decode insert_count %ui -> %ui",
  453.                    *insert_count, req_insert_count);

  454.     *insert_count = req_insert_count;

  455.     return NGX_OK;
  456. }


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

  466.     h3c = ngx_http_v3_get_session(c);
  467.     dt = &h3c->table;

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

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

  472.     if (n >= insert_count) {
  473.         return NGX_OK;
  474.     }

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

  476.     block = NULL;

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

  483.     if (block == NULL) {
  484.         cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t));
  485.         if (cln == NULL) {
  486.             return NGX_ERROR;
  487.         }

  488.         cln->handler = ngx_http_v3_unblock;

  489.         block = cln->data;
  490.         block->queue.prev = NULL;
  491.         block->connection = c;
  492.         block->nblocked = &h3c->nblocked;
  493.     }

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

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

  499.             ngx_http_v3_finalize_connection(c,
  500.                                           NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED,
  501.                                           "too many blocked streams");
  502.             return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  503.         }

  504.         h3c->nblocked++;
  505.         ngx_queue_insert_tail(&h3c->blocked, &block->queue);
  506.     }

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

  509.     return NGX_BUSY;
  510. }


  511. void
  512. ngx_http_v3_ack_insert_count(ngx_connection_t *c, uint64_t insert_count)
  513. {
  514.     ngx_http_v3_session_t        *h3c;
  515.     ngx_http_v3_dynamic_table_t  *dt;

  516.     h3c = ngx_http_v3_get_session(c);
  517.     dt = &h3c->table;

  518.     if (dt->ack_insert_count < insert_count) {
  519.         dt->ack_insert_count = insert_count;
  520.     }
  521. }


  522. static void
  523. ngx_http_v3_unblock(void *data)
  524. {
  525.     ngx_http_v3_block_t  *block = data;

  526.     if (block->queue.prev) {
  527.         ngx_queue_remove(&block->queue);
  528.         block->queue.prev = NULL;
  529.         (*block->nblocked)--;
  530.     }
  531. }


  532. static ngx_int_t
  533. ngx_http_v3_new_entry(ngx_connection_t *c)
  534. {
  535.     ngx_queue_t            *q;
  536.     ngx_connection_t       *bc;
  537.     ngx_http_v3_block_t    *block;
  538.     ngx_http_v3_session_t  *h3c;

  539.     h3c = ngx_http_v3_get_session(c);

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

  542.     while (!ngx_queue_empty(&h3c->blocked)) {
  543.         q = ngx_queue_head(&h3c->blocked);
  544.         block = (ngx_http_v3_block_t *) q;
  545.         bc = block->connection;

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

  547.         ngx_http_v3_unblock(block);
  548.         ngx_post_event(bc->read, &ngx_posted_events);
  549.     }

  550.     return NGX_OK;
  551. }


  552. ngx_int_t
  553. ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value)
  554. {
  555.     switch (id) {

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

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

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

  569.     default:

  570.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  571.                        "http3 param #%uL:%uL", id, value);
  572.     }

  573.     return NGX_OK;
  574. }