comparison src/http/v2/ngx_http_v2_table.c @ 6246:257b51c37c5a

The HTTP/2 implementation (RFC 7240, 7241). The SPDY support is removed, as it's incompatible with the new module.
author Valentin Bartenev <vbart@nginx.com>
date Fri, 11 Sep 2015 20:13:06 +0300
parents
children d4b031cf32cf
comparison
equal deleted inserted replaced
6245:3cf25d33886a 6246:257b51c37c5a
1
2 /*
3 * Copyright (C) Nginx, Inc.
4 * Copyright (C) Valentin V. Bartenev
5 */
6
7
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11
12
13 #define NGX_HTTP_V2_TABLE_SIZE 4096
14
15
16 static ngx_int_t ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c,
17 size_t size);
18
19
20 static ngx_http_v2_header_t ngx_http_v2_static_table[] = {
21 { ngx_string(":authority"), ngx_string("") },
22 { ngx_string(":method"), ngx_string("GET") },
23 { ngx_string(":method"), ngx_string("POST") },
24 { ngx_string(":path"), ngx_string("/") },
25 { ngx_string(":path"), ngx_string("/index.html") },
26 { ngx_string(":scheme"), ngx_string("http") },
27 { ngx_string(":scheme"), ngx_string("https") },
28 { ngx_string(":status"), ngx_string("200") },
29 { ngx_string(":status"), ngx_string("204") },
30 { ngx_string(":status"), ngx_string("206") },
31 { ngx_string(":status"), ngx_string("304") },
32 { ngx_string(":status"), ngx_string("400") },
33 { ngx_string(":status"), ngx_string("404") },
34 { ngx_string(":status"), ngx_string("500") },
35 { ngx_string("accept-charset"), ngx_string("") },
36 { ngx_string("accept-encoding"), ngx_string("gzip, deflate") },
37 { ngx_string("accept-language"), ngx_string("") },
38 { ngx_string("accept-ranges"), ngx_string("") },
39 { ngx_string("accept"), ngx_string("") },
40 { ngx_string("access-control-allow-origin"), ngx_string("") },
41 { ngx_string("age"), ngx_string("") },
42 { ngx_string("allow"), ngx_string("") },
43 { ngx_string("authorization"), ngx_string("") },
44 { ngx_string("cache-control"), ngx_string("") },
45 { ngx_string("content-disposition"), ngx_string("") },
46 { ngx_string("content-encoding"), ngx_string("") },
47 { ngx_string("content-language"), ngx_string("") },
48 { ngx_string("content-length"), ngx_string("") },
49 { ngx_string("content-location"), ngx_string("") },
50 { ngx_string("content-range"), ngx_string("") },
51 { ngx_string("content-type"), ngx_string("") },
52 { ngx_string("cookie"), ngx_string("") },
53 { ngx_string("date"), ngx_string("") },
54 { ngx_string("etag"), ngx_string("") },
55 { ngx_string("expect"), ngx_string("") },
56 { ngx_string("expires"), ngx_string("") },
57 { ngx_string("from"), ngx_string("") },
58 { ngx_string("host"), ngx_string("") },
59 { ngx_string("if-match"), ngx_string("") },
60 { ngx_string("if-modified-since"), ngx_string("") },
61 { ngx_string("if-none-match"), ngx_string("") },
62 { ngx_string("if-range"), ngx_string("") },
63 { ngx_string("if-unmodified-since"), ngx_string("") },
64 { ngx_string("last-modified"), ngx_string("") },
65 { ngx_string("link"), ngx_string("") },
66 { ngx_string("location"), ngx_string("") },
67 { ngx_string("max-forwards"), ngx_string("") },
68 { ngx_string("proxy-authenticate"), ngx_string("") },
69 { ngx_string("proxy-authorization"), ngx_string("") },
70 { ngx_string("range"), ngx_string("") },
71 { ngx_string("referer"), ngx_string("") },
72 { ngx_string("refresh"), ngx_string("") },
73 { ngx_string("retry-after"), ngx_string("") },
74 { ngx_string("server"), ngx_string("") },
75 { ngx_string("set-cookie"), ngx_string("") },
76 { ngx_string("strict-transport-security"), ngx_string("") },
77 { ngx_string("transfer-encoding"), ngx_string("") },
78 { ngx_string("user-agent"), ngx_string("") },
79 { ngx_string("vary"), ngx_string("") },
80 { ngx_string("via"), ngx_string("") },
81 { ngx_string("www-authenticate"), ngx_string("") },
82 };
83
84 #define NGX_HTTP_V2_STATIC_TABLE_ENTRIES \
85 (sizeof(ngx_http_v2_static_table) \
86 / sizeof(ngx_http_v2_header_t))
87
88
89 ngx_int_t
90 ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index,
91 ngx_uint_t name_only)
92 {
93 u_char *p;
94 size_t rest;
95 ngx_http_v2_header_t *entry;
96
97 if (index == 0) {
98 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
99 "client sent invalid hpack table index 0");
100 return NGX_ERROR;
101 }
102
103 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
104 "http2 get indexed %s: %ui",
105 name_only ? "header" : "header name", index);
106
107 index--;
108
109 if (index < NGX_HTTP_V2_STATIC_TABLE_ENTRIES) {
110 h2c->state.header = ngx_http_v2_static_table[index];
111 return NGX_OK;
112 }
113
114 index -= NGX_HTTP_V2_STATIC_TABLE_ENTRIES;
115
116 if (index < h2c->hpack.added - h2c->hpack.deleted) {
117 index = (h2c->hpack.added - index - 1) % h2c->hpack.allocated;
118 entry = h2c->hpack.entries[index];
119
120 p = ngx_pnalloc(h2c->state.pool, entry->name.len + 1);
121 if (p == NULL) {
122 return NGX_ERROR;
123 }
124
125 h2c->state.header.name.len = entry->name.len;
126 h2c->state.header.name.data = p;
127
128 rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->name.data;
129
130 if (entry->name.len > rest) {
131 p = ngx_cpymem(p, entry->name.data, rest);
132 p = ngx_cpymem(p, h2c->hpack.storage, entry->name.len - rest);
133
134 } else {
135 p = ngx_cpymem(p, entry->name.data, entry->name.len);
136 }
137
138 *p = '\0';
139
140 if (name_only) {
141 return NGX_OK;
142 }
143
144 p = ngx_pnalloc(h2c->state.pool, entry->value.len + 1);
145 if (p == NULL) {
146 return NGX_ERROR;
147 }
148
149 h2c->state.header.value.len = entry->value.len;
150 h2c->state.header.value.data = p;
151
152 rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->value.data;
153
154 if (entry->value.len > rest) {
155 p = ngx_cpymem(p, entry->value.data, rest);
156 p = ngx_cpymem(p, h2c->hpack.storage, entry->value.len - rest);
157
158 } else {
159 p = ngx_cpymem(p, entry->value.data, entry->value.len);
160 }
161
162 *p = '\0';
163
164 return NGX_OK;
165 }
166
167 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
168 "client sent out of bound hpack table index: %ui", index);
169
170 return NGX_ERROR;
171 }
172
173
174 ngx_int_t
175 ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,
176 ngx_http_v2_header_t *header)
177 {
178 size_t avail;
179 ngx_uint_t index;
180 ngx_http_v2_header_t *entry, **entries;
181
182 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
183 "http2 add header to hpack table: \"%V: %V\"",
184 &header->name, &header->value);
185
186 if (h2c->hpack.entries == NULL) {
187 h2c->hpack.allocated = 64;
188 h2c->hpack.size = NGX_HTTP_V2_TABLE_SIZE;
189 h2c->hpack.free = NGX_HTTP_V2_TABLE_SIZE;
190
191 h2c->hpack.entries = ngx_palloc(h2c->connection->pool,
192 sizeof(ngx_http_v2_header_t *)
193 * h2c->hpack.allocated);
194 if (h2c->hpack.entries == NULL) {
195 return NGX_ERROR;
196 }
197
198 h2c->hpack.storage = ngx_palloc(h2c->connection->pool,
199 h2c->hpack.free);
200 if (h2c->hpack.storage == NULL) {
201 return NGX_ERROR;
202 }
203
204 h2c->hpack.pos = h2c->hpack.storage;
205 }
206
207 if (ngx_http_v2_table_account(h2c, header->name.len + header->value.len)
208 != NGX_OK)
209 {
210 return NGX_OK;
211 }
212
213 if (h2c->hpack.reused == h2c->hpack.deleted) {
214 entry = ngx_palloc(h2c->connection->pool, sizeof(ngx_http_v2_header_t));
215 if (entry == NULL) {
216 return NGX_ERROR;
217 }
218
219 } else {
220 entry = h2c->hpack.entries[h2c->hpack.reused++ % h2c->hpack.allocated];
221 }
222
223 avail = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - h2c->hpack.pos;
224
225 entry->name.len = header->name.len;
226 entry->name.data = h2c->hpack.pos;
227
228 if (avail >= header->name.len) {
229 h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->name.data,
230 header->name.len);
231 } else {
232 ngx_memcpy(h2c->hpack.pos, header->name.data, avail);
233 h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
234 header->name.data + avail,
235 header->name.len - avail);
236 avail = NGX_HTTP_V2_TABLE_SIZE;
237 }
238
239 avail -= header->name.len;
240
241 entry->value.len = header->value.len;
242 entry->value.data = h2c->hpack.pos;
243
244 if (avail >= header->value.len) {
245 h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->value.data,
246 header->value.len);
247 } else {
248 ngx_memcpy(h2c->hpack.pos, header->value.data, avail);
249 h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,
250 header->value.data + avail,
251 header->value.len - avail);
252 }
253
254 if (h2c->hpack.allocated == h2c->hpack.added - h2c->hpack.deleted) {
255
256 entries = ngx_palloc(h2c->connection->pool,
257 sizeof(ngx_http_v2_header_t *)
258 * (h2c->hpack.allocated + 64));
259 if (entries == NULL) {
260 return NGX_ERROR;
261 }
262
263 index = h2c->hpack.deleted % h2c->hpack.allocated;
264
265 ngx_memcpy(entries, &h2c->hpack.entries[index],
266 (h2c->hpack.allocated - index)
267 * sizeof(ngx_http_v2_header_t *));
268
269 ngx_memcpy(&entries[h2c->hpack.allocated - index], h2c->hpack.entries,
270 index * sizeof(ngx_http_v2_header_t *));
271
272 (void) ngx_pfree(h2c->connection->pool, h2c->hpack.entries);
273
274 h2c->hpack.entries = entries;
275
276 h2c->hpack.added = h2c->hpack.allocated;
277 h2c->hpack.deleted = 0;
278 h2c->hpack.reused = 0;
279 h2c->hpack.allocated += 64;
280 }
281
282 h2c->hpack.entries[h2c->hpack.added++ % h2c->hpack.allocated] = entry;
283
284 return NGX_OK;
285 }
286
287
288 static ngx_int_t
289 ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c, size_t size)
290 {
291 ngx_http_v2_header_t *entry;
292
293 size += 32;
294
295 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
296 "http2 hpack table account: %uz free:%uz",
297 size, h2c->hpack.free);
298
299 if (size <= h2c->hpack.free) {
300 h2c->hpack.free -= size;
301 return NGX_OK;
302 }
303
304 if (size > h2c->hpack.size) {
305 h2c->hpack.deleted = h2c->hpack.added;
306 h2c->hpack.free = h2c->hpack.size;
307 return NGX_DECLINED;
308 }
309
310 do {
311 entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
312 h2c->hpack.free += 32 + entry->name.len + entry->value.len;
313 } while (size > h2c->hpack.free);
314
315 h2c->hpack.free -= size;
316
317 return NGX_OK;
318 }
319
320
321 ngx_int_t
322 ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size)
323 {
324 ssize_t needed;
325 ngx_http_v2_header_t *entry;
326
327 if (size > NGX_HTTP_V2_TABLE_SIZE) {
328 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
329 "client sent invalid table size update: %uz", size);
330
331 return NGX_ERROR;
332 }
333
334 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
335 "http2 new hpack table size: %uz was:%uz",
336 size, h2c->hpack.size);
337
338 needed = h2c->hpack.size - size;
339
340 while (needed > (ssize_t) h2c->hpack.free) {
341 entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];
342 h2c->hpack.free += 32 + entry->name.len + entry->value.len;
343 }
344
345 h2c->hpack.size = size;
346 h2c->hpack.free -= needed;
347
348 return NGX_OK;
349 }