src/http/v3/ngx_http_v3_table.c - nginx source code

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_int_t
  145. ngx_http_v3_ref_insert(ngx_connection_t *c, ngx_uint_t dynamic,
  146.     ngx_uint_t index, ngx_str_t *value)
  147. {
  148.     ngx_str_t                     name;
  149.     ngx_http_v3_session_t        *h3c;
  150.     ngx_http_v3_dynamic_table_t  *dt;

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

  154.         h3c = ngx_http_v3_get_session(c);
  155.         dt = &h3c->table;

  156.         if (dt->base + dt->nelts <= index) {
  157.             return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  158.         }

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

  160.         if (ngx_http_v3_lookup(c, index, &name, NULL) != NGX_OK) {
  161.             return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  162.         }

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

  166.         if (ngx_http_v3_lookup_static(c, index, &name, NULL) != NGX_OK) {
  167.             return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  168.         }
  169.     }

  170.     return ngx_http_v3_insert(c, &name, value);
  171. }


  172. ngx_int_t
  173. ngx_http_v3_insert(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value)
  174. {
  175.     u_char                       *p;
  176.     size_t                        size;
  177.     ngx_http_v3_field_t          *field;
  178.     ngx_http_v3_session_t        *h3c;
  179.     ngx_http_v3_dynamic_table_t  *dt;

  180.     size = ngx_http_v3_table_entry_size(name, value);

  181.     h3c = ngx_http_v3_get_session(c);
  182.     dt = &h3c->table;

  183.     if (size > dt->capacity) {
  184.         ngx_log_error(NGX_LOG_ERR, c->log, 0,
  185.                       "not enough dynamic table capacity");
  186.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  187.     }

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

  191.     p = ngx_alloc(sizeof(ngx_http_v3_field_t) + name->len + value->len,
  192.                   c->log);
  193.     if (p == NULL) {
  194.         return NGX_ERROR;
  195.     }

  196.     field = (ngx_http_v3_field_t *) p;

  197.     field->name.data = p + sizeof(ngx_http_v3_field_t);
  198.     field->name.len = name->len;
  199.     field->value.data = ngx_cpymem(field->name.data, name->data, name->len);
  200.     field->value.len = value->len;
  201.     ngx_memcpy(field->value.data, value->data, value->len);

  202.     dt->elts[dt->nelts++] = field;
  203.     dt->size += size;

  204.     dt->insert_count++;

  205.     if (ngx_http_v3_evict(c, dt->capacity) != NGX_OK) {
  206.         return NGX_ERROR;
  207.     }

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

  209.     if (ngx_http_v3_new_entry(c) != NGX_OK) {
  210.         return NGX_ERROR;
  211.     }

  212.     return NGX_OK;
  213. }


  214. void
  215. ngx_http_v3_inc_insert_count_handler(ngx_event_t *ev)
  216. {
  217.     ngx_connection_t             *c;
  218.     ngx_http_v3_session_t        *h3c;
  219.     ngx_http_v3_dynamic_table_t  *dt;

  220.     c = ev->data;

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

  223.     h3c = ngx_http_v3_get_session(c);
  224.     dt = &h3c->table;

  225.     if (dt->insert_count > dt->ack_insert_count) {
  226.         if (ngx_http_v3_send_inc_insert_count(c,
  227.                                        dt->insert_count - dt->ack_insert_count)
  228.             != NGX_OK)
  229.         {
  230.             return;
  231.         }

  232.         dt->ack_insert_count = dt->insert_count;
  233.     }
  234. }


  235. ngx_int_t
  236. ngx_http_v3_set_capacity(ngx_connection_t *c, ngx_uint_t capacity)
  237. {
  238.     ngx_uint_t                     max, prev_max;
  239.     ngx_http_v3_field_t          **elts;
  240.     ngx_http_v3_session_t         *h3c;
  241.     ngx_http_v3_srv_conf_t        *h3scf;
  242.     ngx_http_v3_dynamic_table_t   *dt;

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

  245.     h3c = ngx_http_v3_get_session(c);
  246.     h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);

  247.     if (capacity > h3scf->max_table_capacity) {
  248.         ngx_log_error(NGX_LOG_INFO, c->log, 0,
  249.                       "client exceeded http3_max_table_capacity limit");
  250.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  251.     }

  252.     if (ngx_http_v3_evict(c, capacity) != NGX_OK) {
  253.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  254.     }

  255.     dt = &h3c->table;
  256.     max = capacity / 32;
  257.     prev_max = dt->capacity / 32;

  258.     if (max > prev_max) {
  259.         elts = ngx_alloc((max + 1) * sizeof(void *), c->log);
  260.         if (elts == NULL) {
  261.             return NGX_ERROR;
  262.         }

  263.         if (dt->elts) {
  264.             ngx_memcpy(elts, dt->elts, dt->nelts * sizeof(void *));
  265.             ngx_free(dt->elts);
  266.         }

  267.         dt->elts = elts;
  268.     }

  269.     dt->capacity = capacity;

  270.     return NGX_OK;
  271. }


  272. void
  273. ngx_http_v3_cleanup_table(ngx_http_v3_session_t *h3c)
  274. {
  275.     ngx_uint_t                    n;
  276.     ngx_http_v3_dynamic_table_t  *dt;

  277.     dt = &h3c->table;

  278.     if (dt->elts == NULL) {
  279.         return;
  280.     }

  281.     for (n = 0; n < dt->nelts; n++) {
  282.         ngx_free(dt->elts[n]);
  283.     }

  284.     ngx_free(dt->elts);
  285. }


  286. static ngx_int_t
  287. ngx_http_v3_evict(ngx_connection_t *c, size_t target)
  288. {
  289.     size_t                        size;
  290.     ngx_uint_t                    n;
  291.     ngx_http_v3_field_t          *field;
  292.     ngx_http_v3_session_t        *h3c;
  293.     ngx_http_v3_dynamic_table_t  *dt;

  294.     h3c = ngx_http_v3_get_session(c);
  295.     dt = &h3c->table;
  296.     n = 0;

  297.     while (dt->size > target) {
  298.         field = dt->elts[n++];
  299.         size = ngx_http_v3_table_entry_size(&field->name, &field->value);

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

  303.         ngx_free(field);
  304.         dt->size -= size;
  305.     }

  306.     if (n) {
  307.         dt->nelts -= n;
  308.         dt->base += n;
  309.         ngx_memmove(dt->elts, &dt->elts[n], dt->nelts * sizeof(void *));
  310.     }

  311.     return NGX_OK;
  312. }


  313. ngx_int_t
  314. ngx_http_v3_duplicate(ngx_connection_t *c, ngx_uint_t index)
  315. {
  316.     ngx_str_t                     name, value;
  317.     ngx_http_v3_session_t        *h3c;
  318.     ngx_http_v3_dynamic_table_t  *dt;

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

  320.     h3c = ngx_http_v3_get_session(c);
  321.     dt = &h3c->table;

  322.     if (dt->base + dt->nelts <= index) {
  323.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  324.     }

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

  326.     if (ngx_http_v3_lookup(c, index, &name, &value) != NGX_OK) {
  327.         return NGX_HTTP_V3_ERR_ENCODER_STREAM_ERROR;
  328.     }

  329.     return ngx_http_v3_insert(c, &name, &value);
  330. }


  331. ngx_int_t
  332. ngx_http_v3_ack_section(ngx_connection_t *c, ngx_uint_t stream_id)
  333. {
  334.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  335.                    "http3 ack section %ui", stream_id);

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

  337.     return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR;
  338. }


  339. ngx_int_t
  340. ngx_http_v3_inc_insert_count(ngx_connection_t *c, ngx_uint_t inc)
  341. {
  342.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  343.                    "http3 increment insert count %ui", inc);

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

  345.     return NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR;
  346. }


  347. ngx_int_t
  348. ngx_http_v3_lookup_static(ngx_connection_t *c, ngx_uint_t index,
  349.     ngx_str_t *name, ngx_str_t *value)
  350. {
  351.     ngx_uint_t            nelts;
  352.     ngx_http_v3_field_t  *field;

  353.     nelts = sizeof(ngx_http_v3_static_table)
  354.             / sizeof(ngx_http_v3_static_table[0]);

  355.     if (index >= nelts) {
  356.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  357.                        "http3 static[%ui] lookup out of bounds: %ui",
  358.                        index, nelts);
  359.         return NGX_ERROR;
  360.     }

  361.     field = &ngx_http_v3_static_table[index];

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

  365.     if (name) {
  366.         *name = field->name;
  367.     }

  368.     if (value) {
  369.         *value = field->value;
  370.     }

  371.     return NGX_OK;
  372. }


  373. ngx_int_t
  374. ngx_http_v3_lookup(ngx_connection_t *c, ngx_uint_t index, ngx_str_t *name,
  375.     ngx_str_t *value)
  376. {
  377.     ngx_http_v3_field_t          *field;
  378.     ngx_http_v3_session_t        *h3c;
  379.     ngx_http_v3_dynamic_table_t  *dt;

  380.     h3c = ngx_http_v3_get_session(c);
  381.     dt = &h3c->table;

  382.     if (index < dt->base || index - dt->base >= dt->nelts) {
  383.         ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
  384.                        "http3 dynamic[%ui] lookup out of bounds: [%ui,%ui]",
  385.                        index, dt->base, dt->base + dt->nelts);
  386.         return NGX_ERROR;
  387.     }

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

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

  392.     if (name) {
  393.         *name = field->name;
  394.     }

  395.     if (value) {
  396.         *value = field->value;
  397.     }

  398.     return NGX_OK;
  399. }


  400. ngx_int_t
  401. ngx_http_v3_decode_insert_count(ngx_connection_t *c, ngx_uint_t *insert_count)
  402. {
  403.     ngx_uint_t                    max_entries, full_range, max_value,
  404.                                   max_wrapped, req_insert_count;
  405.     ngx_http_v3_srv_conf_t       *h3scf;
  406.     ngx_http_v3_session_t        *h3c;
  407.     ngx_http_v3_dynamic_table_t  *dt;

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

  409.     if (*insert_count == 0) {
  410.         return NGX_OK;
  411.     }

  412.     h3c = ngx_http_v3_get_session(c);
  413.     dt = &h3c->table;

  414.     h3scf = ngx_http_v3_get_module_srv_conf(c, ngx_http_v3_module);

  415.     max_entries = h3scf->max_table_capacity / 32;
  416.     full_range = 2 * max_entries;

  417.     if (*insert_count > full_range) {
  418.         return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  419.     }

  420.     max_value = dt->base + dt->nelts + max_entries;
  421.     max_wrapped = (max_value / full_range) * full_range;
  422.     req_insert_count = max_wrapped + *insert_count - 1;

  423.     if (req_insert_count > max_value) {
  424.         if (req_insert_count <= full_range) {
  425.             return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  426.         }

  427.         req_insert_count -= full_range;
  428.     }

  429.     if (req_insert_count == 0) {
  430.         return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  431.     }

  432.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  433.                    "http3 decode insert_count %ui -> %ui",
  434.                    *insert_count, req_insert_count);

  435.     *insert_count = req_insert_count;

  436.     return NGX_OK;
  437. }


  438. ngx_int_t
  439. ngx_http_v3_check_insert_count(ngx_connection_t *c, ngx_uint_t insert_count)
  440. {
  441.     size_t                        n;
  442.     ngx_pool_cleanup_t           *cln;
  443.     ngx_http_v3_block_t          *block;
  444.     ngx_http_v3_session_t        *h3c;
  445.     ngx_http_v3_srv_conf_t       *h3scf;
  446.     ngx_http_v3_dynamic_table_t  *dt;

  447.     h3c = ngx_http_v3_get_session(c);
  448.     dt = &h3c->table;

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

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

  453.     if (n >= insert_count) {
  454.         return NGX_OK;
  455.     }

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

  457.     block = NULL;

  458.     for (cln = c->pool->cleanup; cln; cln = cln->next) {
  459.         if (cln->handler == ngx_http_v3_unblock) {
  460.             block = cln->data;
  461.             break;
  462.         }
  463.     }

  464.     if (block == NULL) {
  465.         cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_v3_block_t));
  466.         if (cln == NULL) {
  467.             return NGX_ERROR;
  468.         }

  469.         cln->handler = ngx_http_v3_unblock;

  470.         block = cln->data;
  471.         block->queue.prev = NULL;
  472.         block->connection = c;
  473.         block->nblocked = &h3c->nblocked;
  474.     }

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

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

  480.             ngx_http_v3_finalize_connection(c,
  481.                                           NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED,
  482.                                           "too many blocked streams");
  483.             return NGX_HTTP_V3_ERR_DECOMPRESSION_FAILED;
  484.         }

  485.         h3c->nblocked++;
  486.         ngx_queue_insert_tail(&h3c->blocked, &block->queue);
  487.     }

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

  490.     return NGX_BUSY;
  491. }


  492. void
  493. ngx_http_v3_ack_insert_count(ngx_connection_t *c, uint64_t insert_count)
  494. {
  495.     ngx_http_v3_session_t        *h3c;
  496.     ngx_http_v3_dynamic_table_t  *dt;

  497.     h3c = ngx_http_v3_get_session(c);
  498.     dt = &h3c->table;

  499.     if (dt->ack_insert_count < insert_count) {
  500.         dt->ack_insert_count = insert_count;
  501.     }
  502. }


  503. static void
  504. ngx_http_v3_unblock(void *data)
  505. {
  506.     ngx_http_v3_block_t  *block = data;

  507.     if (block->queue.prev) {
  508.         ngx_queue_remove(&block->queue);
  509.         block->queue.prev = NULL;
  510.         (*block->nblocked)--;
  511.     }
  512. }


  513. static ngx_int_t
  514. ngx_http_v3_new_entry(ngx_connection_t *c)
  515. {
  516.     ngx_queue_t            *q;
  517.     ngx_connection_t       *bc;
  518.     ngx_http_v3_block_t    *block;
  519.     ngx_http_v3_session_t  *h3c;

  520.     h3c = ngx_http_v3_get_session(c);

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

  523.     while (!ngx_queue_empty(&h3c->blocked)) {
  524.         q = ngx_queue_head(&h3c->blocked);
  525.         block = (ngx_http_v3_block_t *) q;
  526.         bc = block->connection;

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

  528.         ngx_http_v3_unblock(block);
  529.         ngx_post_event(bc->read, &ngx_posted_events);
  530.     }

  531.     return NGX_OK;
  532. }


  533. ngx_int_t
  534. ngx_http_v3_set_param(ngx_connection_t *c, uint64_t id, uint64_t value)
  535. {
  536.     switch (id) {

  537.     case NGX_HTTP_V3_PARAM_MAX_TABLE_CAPACITY:
  538.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  539.                        "http3 param QPACK_MAX_TABLE_CAPACITY:%uL", value);
  540.         break;

  541.     case NGX_HTTP_V3_PARAM_MAX_FIELD_SECTION_SIZE:
  542.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  543.                        "http3 param SETTINGS_MAX_FIELD_SECTION_SIZE:%uL",
  544.                        value);
  545.         break;

  546.     case NGX_HTTP_V3_PARAM_BLOCKED_STREAMS:
  547.         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
  548.                        "http3 param QPACK_BLOCKED_STREAMS:%uL", value);
  549.         break;

  550.     default:

  551.         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
  552.                        "http3 param #%uL:%uL", id, value);
  553.     }

  554.     return NGX_OK;
  555. }