src/http/v2/ngx_http_v2_table.c - nginx source code

Global variables defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Nginx, Inc.
  3. * Copyright (C) Valentin V. Bartenev
  4. */


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


  8. #define NGX_HTTP_V2_TABLE_SIZE  4096


  9. static ngx_int_t ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c,
  10.     size_t size);


  11. static ngx_http_v2_header_t  ngx_http_v2_static_table[] = {
  12.     { ngx_string(":authority"), ngx_string("") },
  13.     { ngx_string(":method"), ngx_string("GET") },
  14.     { ngx_string(":method"), ngx_string("POST") },
  15.     { ngx_string(":path"), ngx_string("/") },
  16.     { ngx_string(":path"), ngx_string("/index.html") },
  17.     { ngx_string(":scheme"), ngx_string("http") },
  18.     { ngx_string(":scheme"), ngx_string("https") },
  19.     { ngx_string(":status"), ngx_string("200") },
  20.     { ngx_string(":status"), ngx_string("204") },
  21.     { ngx_string(":status"), ngx_string("206") },
  22.     { ngx_string(":status"), ngx_string("304") },
  23.     { ngx_string(":status"), ngx_string("400") },
  24.     { ngx_string(":status"), ngx_string("404") },
  25.     { ngx_string(":status"), ngx_string("500") },
  26.     { ngx_string("accept-charset"), ngx_string("") },
  27.     { ngx_string("accept-encoding"), ngx_string("gzip, deflate") },
  28.     { ngx_string("accept-language"), ngx_string("") },
  29.     { ngx_string("accept-ranges"), ngx_string("") },
  30.     { ngx_string("accept"), ngx_string("") },
  31.     { ngx_string("access-control-allow-origin"), ngx_string("") },
  32.     { ngx_string("age"), ngx_string("") },
  33.     { ngx_string("allow"), ngx_string("") },
  34.     { ngx_string("authorization"), ngx_string("") },
  35.     { ngx_string("cache-control"), ngx_string("") },
  36.     { ngx_string("content-disposition"), ngx_string("") },
  37.     { ngx_string("content-encoding"), ngx_string("") },
  38.     { ngx_string("content-language"), ngx_string("") },
  39.     { ngx_string("content-length"), ngx_string("") },
  40.     { ngx_string("content-location"), ngx_string("") },
  41.     { ngx_string("content-range"), ngx_string("") },
  42.     { ngx_string("content-type"), ngx_string("") },
  43.     { ngx_string("cookie"), ngx_string("") },
  44.     { ngx_string("date"), ngx_string("") },
  45.     { ngx_string("etag"), ngx_string("") },
  46.     { ngx_string("expect"), ngx_string("") },
  47.     { ngx_string("expires"), ngx_string("") },
  48.     { ngx_string("from"), ngx_string("") },
  49.     { ngx_string("host"), ngx_string("") },
  50.     { ngx_string("if-match"), ngx_string("") },
  51.     { ngx_string("if-modified-since"), ngx_string("") },
  52.     { ngx_string("if-none-match"), ngx_string("") },
  53.     { ngx_string("if-range"), ngx_string("") },
  54.     { ngx_string("if-unmodified-since"), ngx_string("") },
  55.     { ngx_string("last-modified"), ngx_string("") },
  56.     { ngx_string("link"), ngx_string("") },
  57.     { ngx_string("location"), ngx_string("") },
  58.     { ngx_string("max-forwards"), ngx_string("") },
  59.     { ngx_string("proxy-authenticate"), ngx_string("") },
  60.     { ngx_string("proxy-authorization"), ngx_string("") },
  61.     { ngx_string("range"), ngx_string("") },
  62.     { ngx_string("referer"), ngx_string("") },
  63.     { ngx_string("refresh"), ngx_string("") },
  64.     { ngx_string("retry-after"), ngx_string("") },
  65.     { ngx_string("server"), ngx_string("") },
  66.     { ngx_string("set-cookie"), ngx_string("") },
  67.     { ngx_string("strict-transport-security"), ngx_string("") },
  68.     { ngx_string("transfer-encoding"), ngx_string("") },
  69.     { ngx_string("user-agent"), ngx_string("") },
  70.     { ngx_string("vary"), ngx_string("") },
  71.     { ngx_string("via"), ngx_string("") },
  72.     { ngx_string("www-authenticate"), ngx_string("") },
  73. };

  74. #define NGX_HTTP_V2_STATIC_TABLE_ENTRIES                                      \
  75.     (sizeof(ngx_http_v2_static_table)                                         \
  76.      / sizeof(ngx_http_v2_header_t))


  77. ngx_str_t *
  78. ngx_http_v2_get_static_name(ngx_uint_t index)
  79. {
  80.     return &ngx_http_v2_static_table[index - 1].name;
  81. }


  82. ngx_str_t *
  83. ngx_http_v2_get_static_value(ngx_uint_t index)
  84. {
  85.     return &ngx_http_v2_static_table[index - 1].value;
  86. }


  87. ngx_int_t
  88. ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index,
  89.     ngx_uint_t name_only)
  90. {
  91.     u_char                *p;
  92.     size_t                 rest;
  93.     ngx_http_v2_header_t  *entry;

  94.     if (index == 0) {
  95.         ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
  96.                       "client sent invalid hpack table index 0");
  97.         return NGX_ERROR;
  98.     }

  99.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  100.                    "http2 get indexed %s: %ui",
  101.                    name_only ? "name" : "header", index);

  102.     index--;

  103.     if (index < NGX_HTTP_V2_STATIC_TABLE_ENTRIES) {
  104.         h2c->state.header = ngx_http_v2_static_table[index];
  105.         return NGX_OK;
  106.     }

  107.     index -= NGX_HTTP_V2_STATIC_TABLE_ENTRIES;

  108.     if (index < h2c->hpack.added - h2c->hpack.deleted) {
  109.         index = (h2c->hpack.added - index - 1) % h2c->hpack.allocated;
  110.         entry = h2c->hpack.entries[index];

  111.         p = ngx_pnalloc(h2c->state.pool, entry->name.len + 1);
  112.         if (p == NULL) {
  113.             return NGX_ERROR;
  114.         }

  115.         h2c->state.header.name.len = entry->name.len;
  116.         h2c->state.header.name.data = p;

  117.         rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->name.data;

  118.         if (entry->name.len > rest) {
  119.             p = ngx_cpymem(p, entry->name.data, rest);
  120.             p = ngx_cpymem(p, h2c->hpack.storage, entry->name.len - rest);

  121.         } else {
  122.             p = ngx_cpymem(p, entry->name.data, entry->name.len);
  123.         }

  124.         *p = '\0';

  125.         if (name_only) {
  126.             return NGX_OK;
  127.         }

  128.         p = ngx_pnalloc(h2c->state.pool, entry->value.len + 1);
  129.         if (p == NULL) {
  130.             return NGX_ERROR;
  131.         }

  132.         h2c->state.header.value.len = entry->value.len;
  133.         h2c->state.header.value.data = p;

  134.         rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->value.data;

  135.         if (entry->value.len > rest) {
  136.             p = ngx_cpymem(p, entry->value.data, rest);
  137.             p = ngx_cpymem(p, h2c->hpack.storage, entry->value.len - rest);

  138.         } else {
  139.             p = ngx_cpymem(p, entry->value.data, entry->value.len);
  140.         }

  141.         *p = '\0';

  142.         return NGX_OK;
  143.     }

  144.     ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
  145.                   "client sent out of bound hpack table index: %ui", index);

  146.     return NGX_ERROR;
  147. }


  148. ngx_int_t
  149. ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,
  150.     ngx_http_v2_header_t *header)
  151. {
  152.     size_t                 avail;
  153.     ngx_uint_t             index;
  154.     ngx_http_v2_header_t  *entry, **entries;

  155.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  156.                    "http2 table add: \"%V: %V\"",
  157.                    &header->name, &header->value);

  158.     if (h2c->hpack.entries == NULL) {
  159.         h2c->hpack.allocated = 64;
  160.         h2c->hpack.size = NGX_HTTP_V2_TABLE_SIZE;
  161.         h2c->hpack.free = NGX_HTTP_V2_TABLE_SIZE;

  162.         h2c->hpack.entries = ngx_palloc(h2c->connection->pool,
  163.                                         sizeof(ngx_http_v2_header_t *)
  164.                                         * h2c->hpack.allocated);
  165.         if (h2c->hpack.entries == NULL) {
  166.             return NGX_ERROR;
  167.         }

  168.         h2c->hpack.storage = ngx_palloc(h2c->connection->pool,
  169.                                         h2c->hpack.free);
  170.         if (h2c->hpack.storage == NULL) {
  171.             return NGX_ERROR;
  172.         }

  173.         h2c->hpack.pos = h2c->hpack.storage;
  174.     }

  175.     if (ngx_http_v2_table_account(h2c, header->name.len + header->value.len)
  176.         != NGX_OK)
  177.     {
  178.         return NGX_OK;
  179.     }

  180.     if (h2c->hpack.reused == h2c->hpack.deleted) {
  181.         entry = ngx_palloc(h2c->connection->pool, sizeof(ngx_http_v2_header_t));
  182.         if (entry == NULL) {
  183.             return NGX_ERROR;
  184.         }

  185.     } else {
  186.         entry = h2c->hpack.entries[h2c->hpack.reused++ % h2c->hpack.allocated];
  187.     }

  188.     avail = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - h2c->hpack.pos;

  189.     entry->name.len = header->name.len;
  190.     entry->name.data = h2c->hpack.pos;

  191.     if (avail >= header->name.len) {
  192.         h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->name.data,
  193.                                     header->name.len);
  194.     } else {
  195.         ngx_memcpy(h2c->hpack.pos, header->name.data, avail);
  196.         h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
  197.                                     header->name.data + avail,
  198.                                     header->name.len - avail);
  199.         avail = NGX_HTTP_V2_TABLE_SIZE;
  200.     }

  201.     avail -= header->name.len;

  202.     entry->value.len = header->value.len;
  203.     entry->value.data = h2c->hpack.pos;

  204.     if (avail >= header->value.len) {
  205.         h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->value.data,
  206.                                     header->value.len);
  207.     } else {
  208.         ngx_memcpy(h2c->hpack.pos, header->value.data, avail);
  209.         h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
  210.                                     header->value.data + avail,
  211.                                     header->value.len - avail);
  212.     }

  213.     if (h2c->hpack.allocated == h2c->hpack.added - h2c->hpack.deleted) {

  214.         entries = ngx_palloc(h2c->connection->pool,
  215.                              sizeof(ngx_http_v2_header_t *)
  216.                              * (h2c->hpack.allocated + 64));
  217.         if (entries == NULL) {
  218.             return NGX_ERROR;
  219.         }

  220.         index = h2c->hpack.deleted % h2c->hpack.allocated;

  221.         ngx_memcpy(entries, &h2c->hpack.entries[index],
  222.                    (h2c->hpack.allocated - index)
  223.                    * sizeof(ngx_http_v2_header_t *));

  224.         ngx_memcpy(&entries[h2c->hpack.allocated - index], h2c->hpack.entries,
  225.                    index * sizeof(ngx_http_v2_header_t *));

  226.         (void) ngx_pfree(h2c->connection->pool, h2c->hpack.entries);

  227.         h2c->hpack.entries = entries;

  228.         h2c->hpack.added = h2c->hpack.allocated;
  229.         h2c->hpack.deleted = 0;
  230.         h2c->hpack.reused = 0;
  231.         h2c->hpack.allocated += 64;
  232.     }

  233.     h2c->hpack.entries[h2c->hpack.added++ % h2c->hpack.allocated] = entry;

  234.     return NGX_OK;
  235. }


  236. static ngx_int_t
  237. ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c, size_t size)
  238. {
  239.     ngx_http_v2_header_t  *entry;

  240.     size += 32;

  241.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  242.                    "http2 table account: %uz free:%uz",
  243.                    size, h2c->hpack.free);

  244.     if (size <= h2c->hpack.free) {
  245.         h2c->hpack.free -= size;
  246.         return NGX_OK;
  247.     }

  248.     if (size > h2c->hpack.size) {
  249.         h2c->hpack.deleted = h2c->hpack.added;
  250.         h2c->hpack.free = h2c->hpack.size;
  251.         return NGX_DECLINED;
  252.     }

  253.     do {
  254.         entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
  255.         h2c->hpack.free += 32 + entry->name.len + entry->value.len;
  256.     } while (size > h2c->hpack.free);

  257.     h2c->hpack.free -= size;

  258.     return NGX_OK;
  259. }


  260. ngx_int_t
  261. ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size)
  262. {
  263.     ssize_t                needed;
  264.     ngx_http_v2_header_t  *entry;

  265.     if (size > NGX_HTTP_V2_TABLE_SIZE) {
  266.         ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
  267.                       "client sent invalid table size update: %uz", size);

  268.         return NGX_ERROR;
  269.     }

  270.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
  271.                    "http2 new hpack table size: %uz was:%uz",
  272.                    size, h2c->hpack.size);

  273.     needed = h2c->hpack.size - size;

  274.     while (needed > (ssize_t) h2c->hpack.free) {
  275.         entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
  276.         h2c->hpack.free += 32 + entry->name.len + entry->value.len;
  277.     }

  278.     h2c->hpack.size = size;
  279.     h2c->hpack.free -= needed;

  280.     return NGX_OK;
  281. }