Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

0001 
0002 /*
0003  * Copyright (C) Nginx, Inc.
0004  * Copyright (C) Valentin V. Bartenev
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 #define NGX_HTTP_V2_TABLE_SIZE  4096
0014 
0015 
0016 static ngx_int_t ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c,
0017     size_t size);
0018 
0019 
0020 static ngx_http_v2_header_t  ngx_http_v2_static_table[] = {
0021     { ngx_string(":authority"), ngx_string("") },
0022     { ngx_string(":method"), ngx_string("GET") },
0023     { ngx_string(":method"), ngx_string("POST") },
0024     { ngx_string(":path"), ngx_string("/") },
0025     { ngx_string(":path"), ngx_string("/index.html") },
0026     { ngx_string(":scheme"), ngx_string("http") },
0027     { ngx_string(":scheme"), ngx_string("https") },
0028     { ngx_string(":status"), ngx_string("200") },
0029     { ngx_string(":status"), ngx_string("204") },
0030     { ngx_string(":status"), ngx_string("206") },
0031     { ngx_string(":status"), ngx_string("304") },
0032     { ngx_string(":status"), ngx_string("400") },
0033     { ngx_string(":status"), ngx_string("404") },
0034     { ngx_string(":status"), ngx_string("500") },
0035     { ngx_string("accept-charset"), ngx_string("") },
0036     { ngx_string("accept-encoding"), ngx_string("gzip, deflate") },
0037     { ngx_string("accept-language"), ngx_string("") },
0038     { ngx_string("accept-ranges"), ngx_string("") },
0039     { ngx_string("accept"), ngx_string("") },
0040     { ngx_string("access-control-allow-origin"), ngx_string("") },
0041     { ngx_string("age"), ngx_string("") },
0042     { ngx_string("allow"), ngx_string("") },
0043     { ngx_string("authorization"), ngx_string("") },
0044     { ngx_string("cache-control"), ngx_string("") },
0045     { ngx_string("content-disposition"), ngx_string("") },
0046     { ngx_string("content-encoding"), ngx_string("") },
0047     { ngx_string("content-language"), ngx_string("") },
0048     { ngx_string("content-length"), ngx_string("") },
0049     { ngx_string("content-location"), ngx_string("") },
0050     { ngx_string("content-range"), ngx_string("") },
0051     { ngx_string("content-type"), ngx_string("") },
0052     { ngx_string("cookie"), ngx_string("") },
0053     { ngx_string("date"), ngx_string("") },
0054     { ngx_string("etag"), ngx_string("") },
0055     { ngx_string("expect"), ngx_string("") },
0056     { ngx_string("expires"), ngx_string("") },
0057     { ngx_string("from"), ngx_string("") },
0058     { ngx_string("host"), ngx_string("") },
0059     { ngx_string("if-match"), ngx_string("") },
0060     { ngx_string("if-modified-since"), ngx_string("") },
0061     { ngx_string("if-none-match"), ngx_string("") },
0062     { ngx_string("if-range"), ngx_string("") },
0063     { ngx_string("if-unmodified-since"), ngx_string("") },
0064     { ngx_string("last-modified"), ngx_string("") },
0065     { ngx_string("link"), ngx_string("") },
0066     { ngx_string("location"), ngx_string("") },
0067     { ngx_string("max-forwards"), ngx_string("") },
0068     { ngx_string("proxy-authenticate"), ngx_string("") },
0069     { ngx_string("proxy-authorization"), ngx_string("") },
0070     { ngx_string("range"), ngx_string("") },
0071     { ngx_string("referer"), ngx_string("") },
0072     { ngx_string("refresh"), ngx_string("") },
0073     { ngx_string("retry-after"), ngx_string("") },
0074     { ngx_string("server"), ngx_string("") },
0075     { ngx_string("set-cookie"), ngx_string("") },
0076     { ngx_string("strict-transport-security"), ngx_string("") },
0077     { ngx_string("transfer-encoding"), ngx_string("") },
0078     { ngx_string("user-agent"), ngx_string("") },
0079     { ngx_string("vary"), ngx_string("") },
0080     { ngx_string("via"), ngx_string("") },
0081     { ngx_string("www-authenticate"), ngx_string("") },
0082 };
0083 
0084 #define NGX_HTTP_V2_STATIC_TABLE_ENTRIES                                      \
0085     (sizeof(ngx_http_v2_static_table)                                         \
0086      / sizeof(ngx_http_v2_header_t))
0087 
0088 
0089 ngx_str_t *
0090 ngx_http_v2_get_static_name(ngx_uint_t index)
0091 {
0092     return &ngx_http_v2_static_table[index - 1].name;
0093 }
0094 
0095 
0096 ngx_str_t *
0097 ngx_http_v2_get_static_value(ngx_uint_t index)
0098 {
0099     return &ngx_http_v2_static_table[index - 1].value;
0100 }
0101 
0102 
0103 ngx_int_t
0104 ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index,
0105     ngx_uint_t name_only)
0106 {
0107     u_char                *p;
0108     size_t                 rest;
0109     ngx_http_v2_header_t  *entry;
0110 
0111     if (index == 0) {
0112         ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
0113                       "client sent invalid hpack table index 0");
0114         return NGX_ERROR;
0115     }
0116 
0117     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
0118                    "http2 get indexed %s: %ui",
0119                    name_only ? "name" : "header", index);
0120 
0121     index--;
0122 
0123     if (index < NGX_HTTP_V2_STATIC_TABLE_ENTRIES) {
0124         h2c->state.header = ngx_http_v2_static_table[index];
0125         return NGX_OK;
0126     }
0127 
0128     index -= NGX_HTTP_V2_STATIC_TABLE_ENTRIES;
0129 
0130     if (index < h2c->hpack.added - h2c->hpack.deleted) {
0131         index = (h2c->hpack.added - index - 1) % h2c->hpack.allocated;
0132         entry = h2c->hpack.entries[index];
0133 
0134         p = ngx_pnalloc(h2c->state.pool, entry->name.len + 1);
0135         if (p == NULL) {
0136             return NGX_ERROR;
0137         }
0138 
0139         h2c->state.header.name.len = entry->name.len;
0140         h2c->state.header.name.data = p;
0141 
0142         rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->name.data;
0143 
0144         if (entry->name.len > rest) {
0145             p = ngx_cpymem(p, entry->name.data, rest);
0146             p = ngx_cpymem(p, h2c->hpack.storage, entry->name.len - rest);
0147 
0148         } else {
0149             p = ngx_cpymem(p, entry->name.data, entry->name.len);
0150         }
0151 
0152         *p = '\0';
0153 
0154         if (name_only) {
0155             return NGX_OK;
0156         }
0157 
0158         p = ngx_pnalloc(h2c->state.pool, entry->value.len + 1);
0159         if (p == NULL) {
0160             return NGX_ERROR;
0161         }
0162 
0163         h2c->state.header.value.len = entry->value.len;
0164         h2c->state.header.value.data = p;
0165 
0166         rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->value.data;
0167 
0168         if (entry->value.len > rest) {
0169             p = ngx_cpymem(p, entry->value.data, rest);
0170             p = ngx_cpymem(p, h2c->hpack.storage, entry->value.len - rest);
0171 
0172         } else {
0173             p = ngx_cpymem(p, entry->value.data, entry->value.len);
0174         }
0175 
0176         *p = '\0';
0177 
0178         return NGX_OK;
0179     }
0180 
0181     ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
0182                   "client sent out of bound hpack table index: %ui", index);
0183 
0184     return NGX_ERROR;
0185 }
0186 
0187 
0188 ngx_int_t
0189 ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,
0190     ngx_http_v2_header_t *header)
0191 {
0192     size_t                 avail;
0193     ngx_uint_t             index;
0194     ngx_http_v2_header_t  *entry, **entries;
0195 
0196     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
0197                    "http2 table add: \"%V: %V\"",
0198                    &header->name, &header->value);
0199 
0200     if (h2c->hpack.entries == NULL) {
0201         h2c->hpack.allocated = 64;
0202         h2c->hpack.size = NGX_HTTP_V2_TABLE_SIZE;
0203         h2c->hpack.free = NGX_HTTP_V2_TABLE_SIZE;
0204 
0205         h2c->hpack.entries = ngx_palloc(h2c->connection->pool,
0206                                         sizeof(ngx_http_v2_header_t *)
0207                                         * h2c->hpack.allocated);
0208         if (h2c->hpack.entries == NULL) {
0209             return NGX_ERROR;
0210         }
0211 
0212         h2c->hpack.storage = ngx_palloc(h2c->connection->pool,
0213                                         h2c->hpack.free);
0214         if (h2c->hpack.storage == NULL) {
0215             return NGX_ERROR;
0216         }
0217 
0218         h2c->hpack.pos = h2c->hpack.storage;
0219     }
0220 
0221     if (ngx_http_v2_table_account(h2c, header->name.len + header->value.len)
0222         != NGX_OK)
0223     {
0224         return NGX_OK;
0225     }
0226 
0227     if (h2c->hpack.reused == h2c->hpack.deleted) {
0228         entry = ngx_palloc(h2c->connection->pool, sizeof(ngx_http_v2_header_t));
0229         if (entry == NULL) {
0230             return NGX_ERROR;
0231         }
0232 
0233     } else {
0234         entry = h2c->hpack.entries[h2c->hpack.reused++ % h2c->hpack.allocated];
0235     }
0236 
0237     avail = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - h2c->hpack.pos;
0238 
0239     entry->name.len = header->name.len;
0240     entry->name.data = h2c->hpack.pos;
0241 
0242     if (avail >= header->name.len) {
0243         h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->name.data,
0244                                     header->name.len);
0245     } else {
0246         ngx_memcpy(h2c->hpack.pos, header->name.data, avail);
0247         h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
0248                                     header->name.data + avail,
0249                                     header->name.len - avail);
0250         avail = NGX_HTTP_V2_TABLE_SIZE;
0251     }
0252 
0253     avail -= header->name.len;
0254 
0255     entry->value.len = header->value.len;
0256     entry->value.data = h2c->hpack.pos;
0257 
0258     if (avail >= header->value.len) {
0259         h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->value.data,
0260                                     header->value.len);
0261     } else {
0262         ngx_memcpy(h2c->hpack.pos, header->value.data, avail);
0263         h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
0264                                     header->value.data + avail,
0265                                     header->value.len - avail);
0266     }
0267 
0268     if (h2c->hpack.allocated == h2c->hpack.added - h2c->hpack.deleted) {
0269 
0270         entries = ngx_palloc(h2c->connection->pool,
0271                              sizeof(ngx_http_v2_header_t *)
0272                              * (h2c->hpack.allocated + 64));
0273         if (entries == NULL) {
0274             return NGX_ERROR;
0275         }
0276 
0277         index = h2c->hpack.deleted % h2c->hpack.allocated;
0278 
0279         ngx_memcpy(entries, &h2c->hpack.entries[index],
0280                    (h2c->hpack.allocated - index)
0281                    * sizeof(ngx_http_v2_header_t *));
0282 
0283         ngx_memcpy(&entries[h2c->hpack.allocated - index], h2c->hpack.entries,
0284                    index * sizeof(ngx_http_v2_header_t *));
0285 
0286         (void) ngx_pfree(h2c->connection->pool, h2c->hpack.entries);
0287 
0288         h2c->hpack.entries = entries;
0289 
0290         h2c->hpack.added = h2c->hpack.allocated;
0291         h2c->hpack.deleted = 0;
0292         h2c->hpack.reused = 0;
0293         h2c->hpack.allocated += 64;
0294     }
0295 
0296     h2c->hpack.entries[h2c->hpack.added++ % h2c->hpack.allocated] = entry;
0297 
0298     return NGX_OK;
0299 }
0300 
0301 
0302 static ngx_int_t
0303 ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c, size_t size)
0304 {
0305     ngx_http_v2_header_t  *entry;
0306 
0307     size += 32;
0308 
0309     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
0310                    "http2 table account: %uz free:%uz",
0311                    size, h2c->hpack.free);
0312 
0313     if (size <= h2c->hpack.free) {
0314         h2c->hpack.free -= size;
0315         return NGX_OK;
0316     }
0317 
0318     if (size > h2c->hpack.size) {
0319         h2c->hpack.deleted = h2c->hpack.added;
0320         h2c->hpack.free = h2c->hpack.size;
0321         return NGX_DECLINED;
0322     }
0323 
0324     do {
0325         entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
0326         h2c->hpack.free += 32 + entry->name.len + entry->value.len;
0327     } while (size > h2c->hpack.free);
0328 
0329     h2c->hpack.free -= size;
0330 
0331     return NGX_OK;
0332 }
0333 
0334 
0335 ngx_int_t
0336 ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size)
0337 {
0338     ssize_t                needed;
0339     ngx_http_v2_header_t  *entry;
0340 
0341     if (size > NGX_HTTP_V2_TABLE_SIZE) {
0342         ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
0343                       "client sent invalid table size update: %uz", size);
0344 
0345         return NGX_ERROR;
0346     }
0347 
0348     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
0349                    "http2 new hpack table size: %uz was:%uz",
0350                    size, h2c->hpack.size);
0351 
0352     needed = h2c->hpack.size - size;
0353 
0354     while (needed > (ssize_t) h2c->hpack.free) {
0355         entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
0356         h2c->hpack.free += 32 + entry->name.len + entry->value.len;
0357     }
0358 
0359     h2c->hpack.size = size;
0360     h2c->hpack.free -= needed;
0361 
0362     return NGX_OK;
0363 }