Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

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_int_t
0090 ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index,
0091     ngx_uint_t name_only)
0092 {
0093     u_char                *p;
0094     size_t                 rest;
0095     ngx_http_v2_header_t  *entry;
0096 
0097     if (index == 0) {
0098         ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
0099                       "client sent invalid hpack table index 0");
0100         return NGX_ERROR;
0101     }
0102 
0103     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
0104                    "http2 get indexed %s: %ui",
0105                    name_only ? "header" : "header name", index);
0106 
0107     index--;
0108 
0109     if (index < NGX_HTTP_V2_STATIC_TABLE_ENTRIES) {
0110         h2c->state.header = ngx_http_v2_static_table[index];
0111         return NGX_OK;
0112     }
0113 
0114     index -= NGX_HTTP_V2_STATIC_TABLE_ENTRIES;
0115 
0116     if (index < h2c->hpack.added - h2c->hpack.deleted) {
0117         index = (h2c->hpack.added - index - 1) % h2c->hpack.allocated;
0118         entry = h2c->hpack.entries[index];
0119 
0120         p = ngx_pnalloc(h2c->state.pool, entry->name.len + 1);
0121         if (p == NULL) {
0122             return NGX_ERROR;
0123         }
0124 
0125         h2c->state.header.name.len = entry->name.len;
0126         h2c->state.header.name.data = p;
0127 
0128         rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->name.data;
0129 
0130         if (entry->name.len > rest) {
0131             p = ngx_cpymem(p, entry->name.data, rest);
0132             p = ngx_cpymem(p, h2c->hpack.storage, entry->name.len - rest);
0133 
0134         } else {
0135             p = ngx_cpymem(p, entry->name.data, entry->name.len);
0136         }
0137 
0138         *p = '\0';
0139 
0140         if (name_only) {
0141             return NGX_OK;
0142         }
0143 
0144         p = ngx_pnalloc(h2c->state.pool, entry->value.len + 1);
0145         if (p == NULL) {
0146             return NGX_ERROR;
0147         }
0148 
0149         h2c->state.header.value.len = entry->value.len;
0150         h2c->state.header.value.data = p;
0151 
0152         rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->value.data;
0153 
0154         if (entry->value.len > rest) {
0155             p = ngx_cpymem(p, entry->value.data, rest);
0156             p = ngx_cpymem(p, h2c->hpack.storage, entry->value.len - rest);
0157 
0158         } else {
0159             p = ngx_cpymem(p, entry->value.data, entry->value.len);
0160         }
0161 
0162         *p = '\0';
0163 
0164         return NGX_OK;
0165     }
0166 
0167     ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
0168                   "client sent out of bound hpack table index: %ui", index);
0169 
0170     return NGX_ERROR;
0171 }
0172 
0173 
0174 ngx_int_t
0175 ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,
0176     ngx_http_v2_header_t *header)
0177 {
0178     size_t                 avail;
0179     ngx_uint_t             index;
0180     ngx_http_v2_header_t  *entry, **entries;
0181 
0182     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
0183                    "http2 add header to hpack table: \"%V: %V\"",
0184                    &header->name, &header->value);
0185 
0186     if (h2c->hpack.entries == NULL) {
0187         h2c->hpack.allocated = 64;
0188         h2c->hpack.size = NGX_HTTP_V2_TABLE_SIZE;
0189         h2c->hpack.free = NGX_HTTP_V2_TABLE_SIZE;
0190 
0191         h2c->hpack.entries = ngx_palloc(h2c->connection->pool,
0192                                         sizeof(ngx_http_v2_header_t *)
0193                                         * h2c->hpack.allocated);
0194         if (h2c->hpack.entries == NULL) {
0195             return NGX_ERROR;
0196         }
0197 
0198         h2c->hpack.storage = ngx_palloc(h2c->connection->pool,
0199                                         h2c->hpack.free);
0200         if (h2c->hpack.storage == NULL) {
0201             return NGX_ERROR;
0202         }
0203 
0204         h2c->hpack.pos = h2c->hpack.storage;
0205     }
0206 
0207     if (ngx_http_v2_table_account(h2c, header->name.len + header->value.len)
0208         != NGX_OK)
0209     {
0210         return NGX_OK;
0211     }
0212 
0213     if (h2c->hpack.reused == h2c->hpack.deleted) {
0214         entry = ngx_palloc(h2c->connection->pool, sizeof(ngx_http_v2_header_t));
0215         if (entry == NULL) {
0216             return NGX_ERROR;
0217         }
0218 
0219     } else {
0220         entry = h2c->hpack.entries[h2c->hpack.reused++ % h2c->hpack.allocated];
0221     }
0222 
0223     avail = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - h2c->hpack.pos;
0224 
0225     entry->name.len = header->name.len;
0226     entry->name.data = h2c->hpack.pos;
0227 
0228     if (avail >= header->name.len) {
0229         h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->name.data,
0230                                     header->name.len);
0231     } else {
0232         ngx_memcpy(h2c->hpack.pos, header->name.data, avail);
0233         h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
0234                                     header->name.data + avail,
0235                                     header->name.len - avail);
0236         avail = NGX_HTTP_V2_TABLE_SIZE;
0237     }
0238 
0239     avail -= header->name.len;
0240 
0241     entry->value.len = header->value.len;
0242     entry->value.data = h2c->hpack.pos;
0243 
0244     if (avail >= header->value.len) {
0245         h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->value.data,
0246                                     header->value.len);
0247     } else {
0248         ngx_memcpy(h2c->hpack.pos, header->value.data, avail);
0249         h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
0250                                     header->value.data + avail,
0251                                     header->value.len - avail);
0252     }
0253 
0254     if (h2c->hpack.allocated == h2c->hpack.added - h2c->hpack.deleted) {
0255 
0256         entries = ngx_palloc(h2c->connection->pool,
0257                              sizeof(ngx_http_v2_header_t *)
0258                              * (h2c->hpack.allocated + 64));
0259         if (entries == NULL) {
0260             return NGX_ERROR;
0261         }
0262 
0263         index = h2c->hpack.deleted % h2c->hpack.allocated;
0264 
0265         ngx_memcpy(entries, &h2c->hpack.entries[index],
0266                    (h2c->hpack.allocated - index)
0267                    * sizeof(ngx_http_v2_header_t *));
0268 
0269         ngx_memcpy(&entries[h2c->hpack.allocated - index], h2c->hpack.entries,
0270                    index * sizeof(ngx_http_v2_header_t *));
0271 
0272         (void) ngx_pfree(h2c->connection->pool, h2c->hpack.entries);
0273 
0274         h2c->hpack.entries = entries;
0275 
0276         h2c->hpack.added = h2c->hpack.allocated;
0277         h2c->hpack.deleted = 0;
0278         h2c->hpack.reused = 0;
0279         h2c->hpack.allocated += 64;
0280     }
0281 
0282     h2c->hpack.entries[h2c->hpack.added++ % h2c->hpack.allocated] = entry;
0283 
0284     return NGX_OK;
0285 }
0286 
0287 
0288 static ngx_int_t
0289 ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c, size_t size)
0290 {
0291     ngx_http_v2_header_t  *entry;
0292 
0293     size += 32;
0294 
0295     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
0296                    "http2 hpack table account: %uz free:%uz",
0297                    size, h2c->hpack.free);
0298 
0299     if (size <= h2c->hpack.free) {
0300         h2c->hpack.free -= size;
0301         return NGX_OK;
0302     }
0303 
0304     if (size > h2c->hpack.size) {
0305         h2c->hpack.deleted = h2c->hpack.added;
0306         h2c->hpack.free = h2c->hpack.size;
0307         return NGX_DECLINED;
0308     }
0309 
0310     do {
0311         entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
0312         h2c->hpack.free += 32 + entry->name.len + entry->value.len;
0313     } while (size > h2c->hpack.free);
0314 
0315     h2c->hpack.free -= size;
0316 
0317     return NGX_OK;
0318 }
0319 
0320 
0321 ngx_int_t
0322 ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size)
0323 {
0324     ssize_t                needed;
0325     ngx_http_v2_header_t  *entry;
0326 
0327     if (size > NGX_HTTP_V2_TABLE_SIZE) {
0328         ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
0329                       "client sent invalid table size update: %uz", size);
0330 
0331         return NGX_ERROR;
0332     }
0333 
0334     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
0335                    "http2 new hpack table size: %uz was:%uz",
0336                    size, h2c->hpack.size);
0337 
0338     needed = h2c->hpack.size - size;
0339 
0340     while (needed > (ssize_t) h2c->hpack.free) {
0341         entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
0342         h2c->hpack.free += 32 + entry->name.len + entry->value.len;
0343     }
0344 
0345     h2c->hpack.size = size;
0346     h2c->hpack.free -= needed;
0347 
0348     return NGX_OK;
0349 }