Mercurial > hg > nginx
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 } |