Mercurial > hg > nginx-quic
comparison src/http/ngx_http_spdy.c @ 5121:c0f7b94e88ba
Preliminary experimental support for SPDY draft 2.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Wed, 20 Mar 2013 10:36:57 +0000 |
parents | |
children | 7a015bbda96f |
comparison
equal
deleted
inserted
replaced
5120:7956af6b6a02 | 5121:c0f7b94e88ba |
---|---|
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 #include <ngx_http_spdy_module.h> | |
12 | |
13 #include <zlib.h> | |
14 | |
15 | |
16 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) | |
17 | |
18 #define ngx_str5cmp(m, c0, c1, c2, c3, c4) \ | |
19 *(uint32_t *) m == (c3 << 24 | c2 << 16 | c1 << 8 | c0) \ | |
20 && m[4] == c4 | |
21 | |
22 #else | |
23 | |
24 #define ngx_str5cmp(m, c0, c1, c2, c3, c4) \ | |
25 m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4 | |
26 | |
27 #endif | |
28 | |
29 | |
30 #if (NGX_HAVE_NONALIGNED) | |
31 | |
32 #define ngx_spdy_frame_parse_uint16(p) ntohs(*(uint16_t *) (p)) | |
33 #define ngx_spdy_frame_parse_uint32(p) ntohl(*(uint32_t *) (p)) | |
34 | |
35 #else | |
36 | |
37 #define ngx_spdy_frame_parse_uint16(p) ((p)[0] << 8 | (p)[1]) | |
38 #define ngx_spdy_frame_parse_uint32(p) \ | |
39 ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]) | |
40 | |
41 #endif | |
42 | |
43 #define ngx_spdy_frame_parse_sid(p) \ | |
44 (ngx_spdy_frame_parse_uint32(p) & 0x7fffffff) | |
45 | |
46 | |
47 #define ngx_spdy_ctl_frame_check(h) \ | |
48 (((h) & 0xffffff00) == ngx_spdy_ctl_frame_head(0)) | |
49 #define ngx_spdy_data_frame_check(h) \ | |
50 (!((h) & (uint32_t) NGX_SPDY_CTL_BIT << 31)) | |
51 | |
52 #define ngx_spdy_ctl_frame_type(h) ((h) & 0x000000ff) | |
53 #define ngx_spdy_frame_flags(p) ((p) >> 24) | |
54 #define ngx_spdy_frame_length(p) ((p) & 0x00ffffff) | |
55 | |
56 | |
57 #define NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE 4096 | |
58 #define NGX_SPDY_CTL_FRAME_BUFFER_SIZE 16 | |
59 | |
60 #define NGX_SPDY_PROTOCOL_ERROR 1 | |
61 #define NGX_SPDY_INVALID_STREAM 2 | |
62 #define NGX_SPDY_REFUSED_STREAM 3 | |
63 #define NGX_SPDY_UNSUPPORTED_VERSION 4 | |
64 #define NGX_SPDY_CANCEL 5 | |
65 #define NGX_SPDY_INTERNAL_ERROR 6 | |
66 #define NGX_SPDY_FLOW_CONTROL_ERROR 7 | |
67 | |
68 #define NGX_SPDY_SETTINGS_MAX_STREAMS 4 | |
69 | |
70 #define NGX_SPDY_SETTINGS_FLAG_PERSIST 0x01 | |
71 | |
72 typedef struct { | |
73 ngx_uint_t hash; | |
74 u_char len; | |
75 u_char header[7]; | |
76 ngx_int_t (*handler)(ngx_http_request_t *r); | |
77 } ngx_http_spdy_request_header_t; | |
78 | |
79 | |
80 static void ngx_http_spdy_read_handler(ngx_event_t *rev); | |
81 static void ngx_http_spdy_write_handler(ngx_event_t *wev); | |
82 static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc); | |
83 | |
84 static u_char *ngx_http_spdy_state_detect_settings( | |
85 ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end); | |
86 static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, | |
87 u_char *pos, u_char *end); | |
88 static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, | |
89 u_char *pos, u_char *end); | |
90 static u_char *ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, | |
91 u_char *pos, u_char *end); | |
92 static u_char *ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, | |
93 u_char *pos, u_char *end); | |
94 static u_char *ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, | |
95 u_char *pos, u_char *end); | |
96 static u_char *ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, | |
97 u_char *pos, u_char *end); | |
98 static u_char *ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, | |
99 u_char *pos, u_char *end); | |
100 static u_char *ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, | |
101 u_char *pos, u_char *end); | |
102 static u_char *ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, | |
103 u_char *pos, u_char *end); | |
104 static u_char *ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, | |
105 u_char *pos, u_char *end); | |
106 static u_char *ngx_http_spdy_state_noop(ngx_http_spdy_connection_t *sc, | |
107 u_char *pos, u_char *end); | |
108 static u_char *ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, | |
109 u_char *pos, u_char *end); | |
110 static u_char *ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, | |
111 u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler); | |
112 static u_char *ngx_http_spdy_state_protocol_error( | |
113 ngx_http_spdy_connection_t *sc); | |
114 static u_char *ngx_http_spdy_state_internal_error( | |
115 ngx_http_spdy_connection_t *sc); | |
116 | |
117 static ngx_int_t ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, | |
118 ngx_uint_t sid, ngx_uint_t status, ngx_uint_t priority); | |
119 static ngx_int_t ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc); | |
120 static ngx_int_t ngx_http_spdy_settings_frame_handler( | |
121 ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); | |
122 static ngx_http_spdy_out_frame_t *ngx_http_spdy_get_ctl_frame( | |
123 ngx_http_spdy_connection_t *sc, size_t size, ngx_uint_t priority); | |
124 static ngx_int_t ngx_http_spdy_ctl_frame_handler( | |
125 ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame); | |
126 | |
127 static ngx_http_spdy_stream_t *ngx_http_spdy_create_stream( | |
128 ngx_http_spdy_connection_t *sc, ngx_uint_t id, ngx_uint_t priority); | |
129 static ngx_http_spdy_stream_t *ngx_http_spdy_get_stream_by_id( | |
130 ngx_http_spdy_connection_t *sc, ngx_uint_t sid); | |
131 #define ngx_http_spdy_streams_index_size(sscf) (sscf->streams_index_mask + 1) | |
132 #define ngx_http_spdy_stream_index(sscf, sid) \ | |
133 ((sid >> 1) & sscf->streams_index_mask) | |
134 | |
135 static ngx_int_t ngx_http_spdy_parse_header(ngx_http_request_t *r); | |
136 static ngx_int_t ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r); | |
137 | |
138 static ngx_int_t ngx_http_spdy_handle_request_header(ngx_http_request_t *r); | |
139 static ngx_int_t ngx_http_spdy_parse_method(ngx_http_request_t *r); | |
140 static ngx_int_t ngx_http_spdy_parse_scheme(ngx_http_request_t *r); | |
141 static ngx_int_t ngx_http_spdy_parse_url(ngx_http_request_t *r); | |
142 static ngx_int_t ngx_http_spdy_parse_version(ngx_http_request_t *r); | |
143 | |
144 static ngx_int_t ngx_http_spdy_construct_request_line(ngx_http_request_t *r); | |
145 static void ngx_http_spdy_run_request(ngx_http_request_t *r); | |
146 static ngx_int_t ngx_http_spdy_init_request_body(ngx_http_request_t *r); | |
147 | |
148 static void ngx_http_spdy_handle_connection_handler(ngx_event_t *rev); | |
149 static void ngx_http_spdy_keepalive_handler(ngx_event_t *rev); | |
150 static void ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, | |
151 ngx_int_t rc); | |
152 | |
153 static void ngx_http_spdy_pool_cleanup(void *data); | |
154 | |
155 static void *ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size); | |
156 static void ngx_http_spdy_zfree(void *opaque, void *address); | |
157 | |
158 | |
159 static const u_char ngx_http_spdy_dict[] = | |
160 "options" "get" "head" "post" "put" "delete" "trace" | |
161 "accept" "accept-charset" "accept-encoding" "accept-language" | |
162 "authorization" "expect" "from" "host" | |
163 "if-modified-since" "if-match" "if-none-match" "if-range" | |
164 "if-unmodifiedsince" "max-forwards" "proxy-authorization" | |
165 "range" "referer" "te" "user-agent" | |
166 "100" "101" "200" "201" "202" "203" "204" "205" "206" | |
167 "300" "301" "302" "303" "304" "305" "306" "307" | |
168 "400" "401" "402" "403" "404" "405" "406" "407" "408" "409" "410" | |
169 "411" "412" "413" "414" "415" "416" "417" | |
170 "500" "501" "502" "503" "504" "505" | |
171 "accept-ranges" "age" "etag" "location" "proxy-authenticate" "public" | |
172 "retry-after" "server" "vary" "warning" "www-authenticate" "allow" | |
173 "content-base" "content-encoding" "cache-control" "connection" "date" | |
174 "trailer" "transfer-encoding" "upgrade" "via" "warning" | |
175 "content-language" "content-length" "content-location" | |
176 "content-md5" "content-range" "content-type" "etag" "expires" | |
177 "last-modified" "set-cookie" | |
178 "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday" "Sunday" | |
179 "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec" | |
180 "chunked" "text/html" "image/png" "image/jpg" "image/gif" | |
181 "application/xml" "application/xhtml" "text/plain" "public" "max-age" | |
182 "charset=iso-8859-1" "utf-8" "gzip" "deflate" "HTTP/1.1" "status" | |
183 "version" "url"; | |
184 | |
185 | |
186 static ngx_http_spdy_request_header_t ngx_http_spdy_request_headers[] = { | |
187 { 0, 6, "method", ngx_http_spdy_parse_method }, | |
188 { 0, 6, "scheme", ngx_http_spdy_parse_scheme }, | |
189 { 0, 3, "url", ngx_http_spdy_parse_url }, | |
190 { 0, 7, "version", ngx_http_spdy_parse_version }, | |
191 }; | |
192 | |
193 #define NGX_SPDY_REQUEST_HEADERS \ | |
194 (sizeof(ngx_http_spdy_request_headers) \ | |
195 / sizeof(ngx_http_spdy_request_header_t)) | |
196 | |
197 | |
198 void | |
199 ngx_http_spdy_init(ngx_event_t *rev) | |
200 { | |
201 int rc; | |
202 ngx_connection_t *c; | |
203 ngx_pool_cleanup_t *cln; | |
204 ngx_http_connection_t *hc; | |
205 ngx_http_spdy_srv_conf_t *sscf; | |
206 ngx_http_spdy_main_conf_t *smcf; | |
207 ngx_http_spdy_connection_t *sc; | |
208 | |
209 c = rev->data; | |
210 hc = c->data; | |
211 | |
212 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
213 "init spdy request"); | |
214 | |
215 c->log->action = "processing SPDY"; | |
216 | |
217 smcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_spdy_module); | |
218 | |
219 if (smcf->recv_buffer == NULL) { | |
220 smcf->recv_buffer = ngx_palloc(ngx_cycle->pool, smcf->recv_buffer_size); | |
221 if (smcf->recv_buffer == NULL) { | |
222 ngx_http_close_connection(c); | |
223 return; | |
224 } | |
225 } | |
226 | |
227 sc = ngx_pcalloc(c->pool, sizeof(ngx_http_spdy_connection_t)); | |
228 if (sc == NULL) { | |
229 ngx_http_close_connection(c); | |
230 return; | |
231 } | |
232 | |
233 sc->connection = c; | |
234 sc->http_connection = hc; | |
235 | |
236 sc->handler = ngx_http_spdy_state_detect_settings; | |
237 | |
238 sc->zstream_in.zalloc = ngx_http_spdy_zalloc; | |
239 sc->zstream_in.zfree = ngx_http_spdy_zfree; | |
240 sc->zstream_in.opaque = sc; | |
241 | |
242 rc = inflateInit(&sc->zstream_in); | |
243 if (rc != Z_OK) { | |
244 ngx_log_error(NGX_LOG_ALERT, c->log, 0, | |
245 "inflateInit() failed: %d", rc); | |
246 ngx_http_close_connection(c); | |
247 return; | |
248 } | |
249 | |
250 sc->zstream_out.zalloc = ngx_http_spdy_zalloc; | |
251 sc->zstream_out.zfree = ngx_http_spdy_zfree; | |
252 sc->zstream_out.opaque = sc; | |
253 | |
254 sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_spdy_module); | |
255 | |
256 rc = deflateInit2(&sc->zstream_out, (int) sscf->headers_comp, | |
257 Z_DEFLATED, 11, 4, Z_DEFAULT_STRATEGY); | |
258 | |
259 if (rc != Z_OK) { | |
260 ngx_log_error(NGX_LOG_ALERT, c->log, 0, | |
261 "deflateInit2() failed: %d", rc); | |
262 ngx_http_close_connection(c); | |
263 return; | |
264 } | |
265 | |
266 rc = deflateSetDictionary(&sc->zstream_out, ngx_http_spdy_dict, | |
267 sizeof(ngx_http_spdy_dict)); | |
268 if (rc != Z_OK) { | |
269 ngx_log_error(NGX_LOG_ALERT, c->log, 0, | |
270 "deflateSetDictionary() failed: %d", rc); | |
271 ngx_http_close_connection(c); | |
272 return; | |
273 } | |
274 | |
275 sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log); | |
276 if (sc->pool == NULL) { | |
277 ngx_http_close_connection(c); | |
278 return; | |
279 } | |
280 | |
281 cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_pool_cleanup_file_t)); | |
282 if (cln == NULL) { | |
283 ngx_http_close_connection(c); | |
284 return; | |
285 } | |
286 | |
287 cln->handler = ngx_http_spdy_pool_cleanup; | |
288 cln->data = sc; | |
289 | |
290 sc->streams_index = ngx_pcalloc(sc->pool, | |
291 ngx_http_spdy_streams_index_size(sscf) | |
292 * sizeof(ngx_http_spdy_stream_t *)); | |
293 if (sc->streams_index == NULL) { | |
294 ngx_http_close_connection(c); | |
295 return; | |
296 } | |
297 | |
298 c->data = sc; | |
299 | |
300 rev->handler = ngx_http_spdy_read_handler; | |
301 c->write->handler = ngx_http_spdy_write_handler; | |
302 | |
303 ngx_http_spdy_read_handler(rev); | |
304 } | |
305 | |
306 | |
307 static void | |
308 ngx_http_spdy_read_handler(ngx_event_t *rev) | |
309 { | |
310 u_char *p, *end; | |
311 size_t available; | |
312 ssize_t n; | |
313 ngx_connection_t *c; | |
314 ngx_http_spdy_main_conf_t *smcf; | |
315 ngx_http_spdy_connection_t *sc; | |
316 | |
317 c = rev->data; | |
318 sc = c->data; | |
319 | |
320 if (rev->timedout) { | |
321 ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); | |
322 ngx_http_spdy_finalize_connection(sc, NGX_HTTP_REQUEST_TIME_OUT); | |
323 return; | |
324 } | |
325 | |
326 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy read handler"); | |
327 | |
328 sc->blocked = 1; | |
329 | |
330 smcf = ngx_http_get_module_main_conf(sc->http_connection->conf_ctx, | |
331 ngx_http_spdy_module); | |
332 | |
333 available = smcf->recv_buffer_size - 2 * NGX_SPDY_STATE_BUFFER_SIZE; | |
334 | |
335 do { | |
336 p = smcf->recv_buffer; | |
337 | |
338 ngx_memcpy(p, sc->buffer, NGX_SPDY_STATE_BUFFER_SIZE); | |
339 end = p + sc->buffer_used; | |
340 | |
341 n = c->recv(c, end, available); | |
342 | |
343 if (n == NGX_AGAIN) { | |
344 break; | |
345 } | |
346 | |
347 if (n == 0 && (sc->waiting || sc->processing)) { | |
348 ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
349 "client closed prematurely connection"); | |
350 } | |
351 | |
352 if (n == 0 || n == NGX_ERROR) { | |
353 ngx_http_spdy_finalize_connection(sc, | |
354 NGX_HTTP_CLIENT_CLOSED_REQUEST); | |
355 return; | |
356 } | |
357 | |
358 end += n; | |
359 | |
360 sc->buffer_used = 0; | |
361 sc->waiting = 0; | |
362 | |
363 do { | |
364 p = sc->handler(sc, p, end); | |
365 | |
366 if (p == NULL) { | |
367 return; | |
368 } | |
369 | |
370 } while (p != end); | |
371 | |
372 } while (rev->ready); | |
373 | |
374 if (ngx_handle_read_event(rev, 0) != NGX_OK) { | |
375 ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
376 return; | |
377 } | |
378 | |
379 sc->blocked = 0; | |
380 | |
381 if (sc->processing) { | |
382 if (rev->timer_set) { | |
383 ngx_del_timer(rev); | |
384 } | |
385 return; | |
386 } | |
387 | |
388 ngx_http_spdy_handle_connection(sc); | |
389 } | |
390 | |
391 | |
392 static void | |
393 ngx_http_spdy_write_handler(ngx_event_t *wev) | |
394 { | |
395 ngx_int_t rc; | |
396 ngx_connection_t *c; | |
397 ngx_http_spdy_stream_t *stream, *s, *sn; | |
398 ngx_http_spdy_connection_t *sc; | |
399 | |
400 c = wev->data; | |
401 sc = c->data; | |
402 | |
403 if (wev->timedout) { | |
404 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
405 "spdy write event timed out"); | |
406 ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); | |
407 return; | |
408 } | |
409 | |
410 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy write handler"); | |
411 | |
412 sc->blocked = 2; | |
413 | |
414 rc = ngx_http_spdy_send_output_queue(sc); | |
415 | |
416 if (rc == NGX_ERROR) { | |
417 ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); | |
418 return; | |
419 } | |
420 | |
421 stream = NULL; | |
422 | |
423 for (s = sc->last_stream; s; s = sn) { | |
424 sn = s->next; | |
425 s->next = stream; | |
426 stream = s; | |
427 } | |
428 | |
429 sc->last_stream = NULL; | |
430 | |
431 sc->blocked = 1; | |
432 | |
433 for ( /* void */ ; stream; stream = sn) { | |
434 sn = stream->next; | |
435 stream->handled = 0; | |
436 | |
437 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
438 "spdy run stream %ui", stream->id); | |
439 | |
440 wev = stream->request->connection->write; | |
441 wev->handler(wev); | |
442 } | |
443 | |
444 sc->blocked = 0; | |
445 | |
446 if (rc == NGX_AGAIN) { | |
447 return; | |
448 } | |
449 | |
450 ngx_http_spdy_handle_connection(sc); | |
451 } | |
452 | |
453 | |
454 ngx_int_t | |
455 ngx_http_spdy_send_output_queue(ngx_http_spdy_connection_t *sc) | |
456 { | |
457 ngx_chain_t *cl; | |
458 ngx_event_t *wev; | |
459 ngx_connection_t *c; | |
460 ngx_http_core_loc_conf_t *clcf; | |
461 ngx_http_spdy_out_frame_t *out, *frame, *fn; | |
462 | |
463 c = sc->connection; | |
464 | |
465 if (c->error) { | |
466 return NGX_ERROR; | |
467 } | |
468 | |
469 wev = c->write; | |
470 | |
471 if (!wev->ready) { | |
472 return NGX_OK; | |
473 } | |
474 | |
475 cl = NULL; | |
476 out = NULL; | |
477 | |
478 for (frame = sc->last_out; frame; frame = fn) { | |
479 frame->last->next = cl; | |
480 cl = frame->first; | |
481 | |
482 fn = frame->next; | |
483 frame->next = out; | |
484 out = frame; | |
485 | |
486 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
487 "spdy frame out: %p sid:%ui prio:%ui bl:%ui size:%uz", | |
488 out, out->stream ? out->stream->id : 0, out->priority, | |
489 out->blocked, out->size); | |
490 } | |
491 | |
492 cl = c->send_chain(c, cl, 0); | |
493 | |
494 if (cl == NGX_CHAIN_ERROR) { | |
495 c->error = 1; | |
496 | |
497 if (!sc->blocked) { | |
498 ngx_post_event(wev, &ngx_posted_events); | |
499 } | |
500 | |
501 return NGX_ERROR; | |
502 } | |
503 | |
504 clcf = ngx_http_get_module_loc_conf(sc->http_connection->conf_ctx, | |
505 ngx_http_core_module); | |
506 | |
507 if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) { | |
508 return NGX_ERROR; /* FIXME */ | |
509 } | |
510 | |
511 if (cl) { | |
512 ngx_add_timer(wev, clcf->send_timeout); | |
513 | |
514 } else { | |
515 if (wev->timer_set) { | |
516 ngx_del_timer(wev); | |
517 } | |
518 } | |
519 | |
520 for ( /* void */ ; out; out = out->next) { | |
521 if (out->handler(sc, out) != NGX_OK) { | |
522 out->blocked = 1; | |
523 out->priority = NGX_SPDY_HIGHEST_PRIORITY; | |
524 break; | |
525 } | |
526 | |
527 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
528 "spdy frame sent: %p sid:%ui bl:%ui size:%uz", | |
529 out, out->stream ? out->stream->id : 0, | |
530 out->blocked, out->size); | |
531 } | |
532 | |
533 frame = NULL; | |
534 | |
535 for ( /* void */ ; out; out = fn) { | |
536 fn = out->next; | |
537 out->next = frame; | |
538 frame = out; | |
539 } | |
540 | |
541 sc->last_out = frame; | |
542 | |
543 return NGX_OK; | |
544 } | |
545 | |
546 | |
547 static void | |
548 ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc) | |
549 { | |
550 ngx_connection_t *c; | |
551 ngx_http_spdy_srv_conf_t *sscf; | |
552 | |
553 if (sc->last_out || sc->processing) { | |
554 return; | |
555 } | |
556 | |
557 c = sc->connection; | |
558 | |
559 if (c->error) { | |
560 ngx_http_close_connection(c); | |
561 return; | |
562 } | |
563 | |
564 if (c->buffered) { | |
565 return; | |
566 } | |
567 | |
568 sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
569 ngx_http_spdy_module); | |
570 if (sc->waiting) { | |
571 ngx_add_timer(c->read, sscf->recv_timeout); | |
572 return; | |
573 } | |
574 | |
575 if (ngx_terminate || ngx_exiting) { | |
576 ngx_http_close_connection(c); | |
577 return; | |
578 } | |
579 | |
580 ngx_destroy_pool(sc->pool); | |
581 | |
582 sc->pool = NULL; | |
583 sc->free_ctl_frames = NULL; | |
584 sc->free_fake_connections = NULL; | |
585 | |
586 #if (NGX_HTTP_SSL) | |
587 if (c->ssl) { | |
588 ngx_ssl_free_buffer(c); | |
589 } | |
590 #endif | |
591 | |
592 c->destroyed = 1; | |
593 c->idle = 1; | |
594 ngx_reusable_connection(c, 1); | |
595 | |
596 c->write->handler = ngx_http_empty_handler; | |
597 c->read->handler = ngx_http_spdy_keepalive_handler; | |
598 | |
599 if (c->write->timer_set) { | |
600 ngx_del_timer(c->write); | |
601 } | |
602 | |
603 ngx_add_timer(c->read, sscf->keepalive_timeout); | |
604 } | |
605 | |
606 | |
607 static u_char * | |
608 ngx_http_spdy_state_detect_settings(ngx_http_spdy_connection_t *sc, | |
609 u_char *pos, u_char *end) | |
610 { | |
611 if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) { | |
612 return ngx_http_spdy_state_save(sc, pos, end, | |
613 ngx_http_spdy_state_detect_settings); | |
614 } | |
615 | |
616 /* | |
617 * Since this is the first frame in a buffer, | |
618 * then it is properly aligned | |
619 */ | |
620 | |
621 if (*(uint32_t *) pos == htonl(ngx_spdy_ctl_frame_head(NGX_SPDY_SETTINGS))) | |
622 { | |
623 sc->length = ngx_spdy_frame_length(htonl(((uint32_t *) pos)[1])); | |
624 | |
625 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
626 "spdy SETTINGS frame received, size: %uz", sc->length); | |
627 | |
628 pos += NGX_SPDY_FRAME_HEADER_SIZE; | |
629 | |
630 return ngx_http_spdy_state_settings(sc, pos, end); | |
631 } | |
632 | |
633 ngx_http_spdy_send_settings(sc); | |
634 | |
635 return ngx_http_spdy_state_head(sc, pos, end); | |
636 } | |
637 | |
638 | |
639 static u_char * | |
640 ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, | |
641 u_char *end) | |
642 { | |
643 uint32_t head, flen; | |
644 | |
645 if (end - pos < NGX_SPDY_FRAME_HEADER_SIZE) { | |
646 return ngx_http_spdy_state_save(sc, pos, end, | |
647 ngx_http_spdy_state_head); | |
648 } | |
649 | |
650 head = ngx_spdy_frame_parse_uint32(pos); | |
651 | |
652 pos += sizeof(uint32_t); | |
653 | |
654 flen = ngx_spdy_frame_parse_uint32(pos); | |
655 | |
656 sc->flags = ngx_spdy_frame_flags(flen); | |
657 sc->length = ngx_spdy_frame_length(flen); | |
658 | |
659 pos += sizeof(uint32_t); | |
660 | |
661 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
662 "spdy process frame head:%08Xd f:%ui l:%ui", | |
663 head, sc->flags, sc->length); | |
664 | |
665 if (ngx_spdy_ctl_frame_check(head)) { | |
666 switch (ngx_spdy_ctl_frame_type(head)) { | |
667 | |
668 case NGX_SPDY_SYN_STREAM: | |
669 return ngx_http_spdy_state_syn_stream(sc, pos, end); | |
670 | |
671 case NGX_SPDY_SYN_REPLY: | |
672 return ngx_http_spdy_state_protocol_error(sc); | |
673 | |
674 case NGX_SPDY_RST_STREAM: | |
675 return ngx_http_spdy_state_rst_stream(sc, pos, end); | |
676 | |
677 case NGX_SPDY_SETTINGS: | |
678 return ngx_http_spdy_state_skip(sc, pos, end); | |
679 | |
680 case NGX_SPDY_NOOP: | |
681 return ngx_http_spdy_state_noop(sc, pos, end); | |
682 | |
683 case NGX_SPDY_PING: | |
684 return ngx_http_spdy_state_ping(sc, pos, end); | |
685 | |
686 case NGX_SPDY_GOAWAY: | |
687 return ngx_http_spdy_state_skip(sc, pos, end); /* TODO */ | |
688 | |
689 case NGX_SPDY_HEADERS: | |
690 return ngx_http_spdy_state_protocol_error(sc); | |
691 | |
692 default: /* TODO logging */ | |
693 return ngx_http_spdy_state_skip(sc, pos, end); | |
694 } | |
695 } | |
696 | |
697 if (ngx_spdy_data_frame_check(head)) { | |
698 sc->stream = ngx_http_spdy_get_stream_by_id(sc, head); | |
699 return ngx_http_spdy_state_data(sc, pos, end); | |
700 } | |
701 | |
702 | |
703 /* TODO version & type check */ | |
704 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
705 "spdy unknown frame"); | |
706 | |
707 return ngx_http_spdy_state_protocol_error(sc); | |
708 } | |
709 | |
710 | |
711 static u_char * | |
712 ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, u_char *pos, | |
713 u_char *end) | |
714 { | |
715 ngx_uint_t sid, prio; | |
716 ngx_http_spdy_stream_t *stream; | |
717 ngx_http_spdy_srv_conf_t *sscf; | |
718 | |
719 if (end - pos < NGX_SPDY_SYN_STREAM_SIZE) { | |
720 return ngx_http_spdy_state_save(sc, pos, end, | |
721 ngx_http_spdy_state_syn_stream); | |
722 } | |
723 | |
724 if (sc->length <= NGX_SPDY_SYN_STREAM_SIZE) { | |
725 /* TODO logging */ | |
726 return ngx_http_spdy_state_protocol_error(sc); | |
727 } | |
728 | |
729 sc->length -= NGX_SPDY_SYN_STREAM_SIZE; | |
730 | |
731 sid = ngx_spdy_frame_parse_sid(pos); | |
732 prio = pos[8] >> 6; | |
733 | |
734 pos += NGX_SPDY_SYN_STREAM_SIZE; | |
735 | |
736 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
737 "spdy SYN_STREAM frame sid:%ui prio:%ui", sid, prio); | |
738 | |
739 sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
740 ngx_http_spdy_module); | |
741 | |
742 if (sc->processing >= sscf->concurrent_streams) { | |
743 | |
744 ngx_log_error(NGX_LOG_INFO, sc->connection->log, 0, | |
745 "spdy concurrent streams excessed %ui", sc->processing); | |
746 | |
747 if (ngx_http_spdy_send_rst_stream(sc, sid, NGX_SPDY_REFUSED_STREAM, | |
748 prio) | |
749 != NGX_OK) | |
750 { | |
751 return ngx_http_spdy_state_internal_error(sc); | |
752 } | |
753 | |
754 return ngx_http_spdy_state_headers_skip(sc, pos, end); | |
755 } | |
756 | |
757 stream = ngx_http_spdy_create_stream(sc, sid, prio); | |
758 if (stream == NULL) { | |
759 return ngx_http_spdy_state_internal_error(sc); | |
760 } | |
761 | |
762 stream->in_closed = (sc->flags & NGX_SPDY_FLAG_FIN) ? 1 : 0; | |
763 | |
764 stream->request->request_length = NGX_SPDY_FRAME_HEADER_SIZE | |
765 + NGX_SPDY_SYN_STREAM_SIZE | |
766 + sc->length; | |
767 | |
768 sc->stream = stream; | |
769 | |
770 sc->last_sid = sid; | |
771 | |
772 return ngx_http_spdy_state_headers(sc, pos, end); | |
773 } | |
774 | |
775 | |
776 static u_char * | |
777 ngx_http_spdy_state_headers(ngx_http_spdy_connection_t *sc, u_char *pos, | |
778 u_char *end) | |
779 { | |
780 int z; | |
781 size_t size; | |
782 ngx_buf_t *buf; | |
783 ngx_int_t rc; | |
784 ngx_uint_t complete; | |
785 ngx_http_request_t *r; | |
786 | |
787 size = end - pos; | |
788 | |
789 if (size == 0) { | |
790 return ngx_http_spdy_state_save(sc, pos, end, | |
791 ngx_http_spdy_state_headers); | |
792 } | |
793 | |
794 if (size >= sc->length) { | |
795 size = sc->length; | |
796 complete = 1; | |
797 | |
798 } else { | |
799 complete = 0; | |
800 } | |
801 | |
802 r = sc->stream->request; | |
803 | |
804 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
805 "spdy process HEADERS %uz of %uz", size, sc->length); | |
806 | |
807 buf = r->header_in; | |
808 | |
809 sc->zstream_in.next_in = pos; | |
810 sc->zstream_in.avail_in = size; | |
811 sc->zstream_in.next_out = buf->last; | |
812 sc->zstream_in.avail_out = buf->end - buf->last - 1; | |
813 | |
814 z = inflate(&sc->zstream_in, Z_NO_FLUSH); | |
815 | |
816 if (z == Z_NEED_DICT) { | |
817 z = inflateSetDictionary(&sc->zstream_in, ngx_http_spdy_dict, | |
818 sizeof(ngx_http_spdy_dict)); | |
819 if (z != Z_OK) { | |
820 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
821 "spdy inflateSetDictionary() failed: %d", z); | |
822 ngx_http_spdy_close_stream(sc->stream, 0); | |
823 return ngx_http_spdy_state_protocol_error(sc); | |
824 } | |
825 | |
826 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
827 "spdy inflateSetDictionary(): %d", z); | |
828 | |
829 z = sc->zstream_in.avail_in ? inflate(&sc->zstream_in, Z_NO_FLUSH) | |
830 : Z_OK; | |
831 } | |
832 | |
833 if (z != Z_OK) { | |
834 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
835 "spdy inflate() failed: %d", z); | |
836 ngx_http_spdy_close_stream(sc->stream, 0); | |
837 return ngx_http_spdy_state_protocol_error(sc); | |
838 } | |
839 | |
840 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
841 "spdy inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", | |
842 sc->zstream_in.next_in, sc->zstream_in.next_out, | |
843 sc->zstream_in.avail_in, sc->zstream_in.avail_out, | |
844 z); | |
845 | |
846 sc->length -= sc->zstream_in.next_in - pos; | |
847 pos = sc->zstream_in.next_in; | |
848 | |
849 buf->last = sc->zstream_in.next_out; | |
850 | |
851 if (r->headers_in.headers.part.elts == NULL) { | |
852 | |
853 if (buf->last - buf->pos < NGX_SPDY_NV_NUM_SIZE) { | |
854 return ngx_http_spdy_state_save(sc, pos, end, | |
855 ngx_http_spdy_state_headers); | |
856 } | |
857 | |
858 sc->headers = ngx_spdy_frame_parse_uint16(buf->pos); | |
859 | |
860 buf->pos += NGX_SPDY_NV_NUM_SIZE; | |
861 | |
862 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
863 "spdy headers count: %ui", sc->headers); | |
864 | |
865 if (ngx_list_init(&r->headers_in.headers, r->pool, sc->headers + 3, | |
866 sizeof(ngx_table_elt_t)) | |
867 != NGX_OK) | |
868 { | |
869 ngx_http_spdy_close_stream(sc->stream, | |
870 NGX_HTTP_INTERNAL_SERVER_ERROR); | |
871 return ngx_http_spdy_state_headers_error(sc, pos, end); | |
872 } | |
873 | |
874 if (ngx_array_init(&r->headers_in.cookies, r->pool, 2, | |
875 sizeof(ngx_table_elt_t *)) | |
876 != NGX_OK) | |
877 { | |
878 ngx_http_spdy_close_stream(sc->stream, | |
879 NGX_HTTP_INTERNAL_SERVER_ERROR); | |
880 return ngx_http_spdy_state_headers_error(sc, pos, end); | |
881 } | |
882 } | |
883 | |
884 while (sc->headers) { | |
885 | |
886 rc = ngx_http_spdy_parse_header(r); | |
887 | |
888 switch (rc) { | |
889 | |
890 case NGX_DONE: | |
891 sc->headers--; | |
892 | |
893 case NGX_OK: | |
894 break; | |
895 | |
896 case NGX_AGAIN: | |
897 | |
898 if (sc->zstream_in.avail_in) { | |
899 | |
900 rc = ngx_http_spdy_alloc_large_header_buffer(r); | |
901 | |
902 if (rc == NGX_DECLINED) { | |
903 /* TODO logging */ | |
904 ngx_http_finalize_request(r, | |
905 NGX_HTTP_REQUEST_HEADER_TOO_LARGE); | |
906 return ngx_http_spdy_state_headers_error(sc, pos, end); | |
907 } | |
908 | |
909 if (rc != NGX_OK) { | |
910 ngx_http_spdy_close_stream(sc->stream, | |
911 NGX_HTTP_INTERNAL_SERVER_ERROR); | |
912 return ngx_http_spdy_state_headers_error(sc, pos, end); | |
913 } | |
914 | |
915 buf = r->header_in; | |
916 | |
917 sc->zstream_in.next_out = buf->last; | |
918 sc->zstream_in.avail_out = buf->end - buf->last - 1; | |
919 | |
920 z = inflate(&sc->zstream_in, Z_NO_FLUSH); | |
921 | |
922 if (z != Z_OK) { | |
923 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
924 "spdy inflate() failed: %d", z); | |
925 ngx_http_spdy_close_stream(sc->stream, 0); | |
926 return ngx_http_spdy_state_protocol_error(sc); | |
927 } | |
928 | |
929 sc->length -= sc->zstream_in.next_in - pos; | |
930 pos = sc->zstream_in.next_in; | |
931 | |
932 buf->last = sc->zstream_in.next_out; | |
933 | |
934 continue; | |
935 } | |
936 | |
937 if (complete) { | |
938 /* TODO: improve error message */ | |
939 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
940 "spdy again while last chunk"); | |
941 ngx_http_spdy_close_stream(sc->stream, 0); | |
942 return ngx_http_spdy_state_protocol_error(sc); | |
943 } | |
944 | |
945 return ngx_http_spdy_state_save(sc, pos, end, | |
946 ngx_http_spdy_state_headers); | |
947 | |
948 case NGX_HTTP_PARSE_INVALID_REQUEST: | |
949 | |
950 /* TODO: improve error message */ | |
951 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
952 "client sent invalid header line"); | |
953 | |
954 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); | |
955 | |
956 return ngx_http_spdy_state_headers_error(sc, pos, end); | |
957 | |
958 default: /* NGX_HTTP_PARSE_INVALID_HEADER */ | |
959 | |
960 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
961 "client sent invalid HEADERS spdy frame"); | |
962 ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); | |
963 return ngx_http_spdy_state_protocol_error(sc); | |
964 } | |
965 | |
966 /* a header line has been parsed successfully */ | |
967 | |
968 rc = ngx_http_spdy_handle_request_header(r); | |
969 | |
970 if (rc != NGX_OK) { | |
971 if (rc == NGX_HTTP_PARSE_INVALID_HEADER) { | |
972 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
973 "client sent invalid HEADERS spdy frame"); | |
974 ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); | |
975 return ngx_http_spdy_state_protocol_error(sc); | |
976 } | |
977 | |
978 if (rc == NGX_HTTP_PARSE_INVALID_REQUEST) { | |
979 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); | |
980 } | |
981 | |
982 return ngx_http_spdy_state_headers_error(sc, pos, end); | |
983 } | |
984 } | |
985 | |
986 if (buf->pos != buf->last) { | |
987 /* TODO: improve error message */ | |
988 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
989 "end %ui %p %p", complete, buf->pos, buf->last); | |
990 ngx_http_spdy_close_stream(sc->stream, NGX_HTTP_BAD_REQUEST); | |
991 return ngx_http_spdy_state_protocol_error(sc); | |
992 } | |
993 | |
994 if (!complete) { | |
995 return ngx_http_spdy_state_save(sc, pos, end, | |
996 ngx_http_spdy_state_headers); | |
997 } | |
998 | |
999 ngx_http_spdy_run_request(r); | |
1000 | |
1001 return ngx_http_spdy_state_complete(sc, pos, end); | |
1002 } | |
1003 | |
1004 | |
1005 static u_char * | |
1006 ngx_http_spdy_state_headers_error(ngx_http_spdy_connection_t *sc, u_char *pos, | |
1007 u_char *end) | |
1008 { | |
1009 if (sc->connection->error) { | |
1010 return ngx_http_spdy_state_internal_error(sc); | |
1011 } | |
1012 | |
1013 return ngx_http_spdy_state_headers_skip(sc, pos, end); | |
1014 } | |
1015 | |
1016 | |
1017 static u_char * | |
1018 ngx_http_spdy_state_headers_skip(ngx_http_spdy_connection_t *sc, u_char *pos, | |
1019 u_char *end) | |
1020 { | |
1021 int n; | |
1022 size_t size; | |
1023 u_char buffer[NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE]; | |
1024 | |
1025 if (sc->length == 0) { | |
1026 return ngx_http_spdy_state_complete(sc, pos, end); | |
1027 } | |
1028 | |
1029 size = end - pos; | |
1030 | |
1031 if (size == 0) { | |
1032 return ngx_http_spdy_state_save(sc, pos, end, | |
1033 ngx_http_spdy_state_headers_skip); | |
1034 } | |
1035 | |
1036 sc->zstream_in.next_in = pos; | |
1037 sc->zstream_in.avail_in = (size < sc->length) ? size : sc->length; | |
1038 | |
1039 while (sc->zstream_in.avail_in) { | |
1040 sc->zstream_in.next_out = buffer; | |
1041 sc->zstream_in.avail_out = NGX_SPDY_SKIP_HEADERS_BUFFER_SIZE; | |
1042 | |
1043 n = inflate(&sc->zstream_in, Z_NO_FLUSH); | |
1044 | |
1045 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
1046 "spdy inflate(): %d", n); | |
1047 | |
1048 if (n != Z_OK) { | |
1049 /* TODO: logging */ | |
1050 return ngx_http_spdy_state_protocol_error(sc); | |
1051 } | |
1052 } | |
1053 | |
1054 pos = sc->zstream_in.next_in; | |
1055 | |
1056 if (size < sc->length) { | |
1057 sc->length -= size; | |
1058 return ngx_http_spdy_state_save(sc, pos, end, | |
1059 ngx_http_spdy_state_headers_skip); | |
1060 } | |
1061 | |
1062 return ngx_http_spdy_state_complete(sc, pos, end); | |
1063 } | |
1064 | |
1065 | |
1066 static u_char * | |
1067 ngx_http_spdy_state_data(ngx_http_spdy_connection_t *sc, u_char *pos, | |
1068 u_char *end) | |
1069 { | |
1070 size_t size; | |
1071 ssize_t n; | |
1072 ngx_buf_t *buf; | |
1073 ngx_int_t rc; | |
1074 ngx_uint_t complete; | |
1075 ngx_temp_file_t *tf; | |
1076 ngx_http_request_t *r; | |
1077 ngx_http_spdy_stream_t *stream; | |
1078 ngx_http_request_body_t *rb; | |
1079 ngx_http_core_loc_conf_t *clcf; | |
1080 | |
1081 stream = sc->stream; | |
1082 | |
1083 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
1084 "spdy DATA frame"); | |
1085 | |
1086 if (stream == NULL) { | |
1087 return ngx_http_spdy_state_skip(sc, pos, end); | |
1088 } | |
1089 | |
1090 if (stream->in_closed) { | |
1091 /* TODO log */ | |
1092 return ngx_http_spdy_state_protocol_error(sc); | |
1093 } | |
1094 | |
1095 if (stream->skip_data) { | |
1096 | |
1097 if (sc->flags & NGX_SPDY_FLAG_FIN) { | |
1098 stream->in_closed = 1; | |
1099 } | |
1100 | |
1101 /* TODO log and accounting */ | |
1102 return ngx_http_spdy_state_skip(sc, pos, end); | |
1103 } | |
1104 | |
1105 size = end - pos; | |
1106 | |
1107 if (size >= sc->length) { | |
1108 size = sc->length; | |
1109 complete = 1; | |
1110 | |
1111 } else { | |
1112 sc->length -= size; | |
1113 complete = 0; | |
1114 } | |
1115 | |
1116 r = stream->request; | |
1117 | |
1118 if (r->request_body == NULL | |
1119 && ngx_http_spdy_init_request_body(r) != NGX_OK) | |
1120 { | |
1121 stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; | |
1122 return ngx_http_spdy_state_skip(sc, pos, end); | |
1123 } | |
1124 | |
1125 rb = r->request_body; | |
1126 tf = rb->temp_file; | |
1127 buf = rb->buf; | |
1128 | |
1129 if (size) { | |
1130 rb->rest += size; | |
1131 | |
1132 if (r->headers_in.content_length_n != -1 | |
1133 && r->headers_in.content_length_n < rb->rest) | |
1134 { | |
1135 /* TODO logging */ | |
1136 stream->skip_data = NGX_SPDY_DATA_ERROR; | |
1137 goto error; | |
1138 | |
1139 } else { | |
1140 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
1141 | |
1142 if (clcf->client_max_body_size | |
1143 && clcf->client_max_body_size < rb->rest) | |
1144 { | |
1145 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, | |
1146 "client intended to send too large chunked " | |
1147 "body: %O bytes", | |
1148 rb->rest); | |
1149 | |
1150 stream->skip_data = NGX_SPDY_DATA_ERROR; | |
1151 goto error; | |
1152 } | |
1153 } | |
1154 | |
1155 if (tf) { | |
1156 buf->start = pos; | |
1157 buf->pos = pos; | |
1158 | |
1159 pos += size; | |
1160 | |
1161 buf->end = pos; | |
1162 buf->last = pos; | |
1163 | |
1164 n = ngx_write_chain_to_temp_file(tf, rb->bufs); | |
1165 | |
1166 /* TODO: n == 0 or not complete and level event */ | |
1167 | |
1168 if (n == NGX_ERROR) { | |
1169 stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; | |
1170 goto error; | |
1171 } | |
1172 | |
1173 tf->offset += n; | |
1174 | |
1175 } else { | |
1176 buf->last = ngx_cpymem(buf->last, pos, size); | |
1177 pos += size; | |
1178 } | |
1179 | |
1180 r->request_length += size; | |
1181 } | |
1182 | |
1183 if (!complete) { | |
1184 return ngx_http_spdy_state_save(sc, pos, end, | |
1185 ngx_http_spdy_state_data); | |
1186 } | |
1187 | |
1188 if (sc->flags & NGX_SPDY_FLAG_FIN) { | |
1189 | |
1190 stream->in_closed = 1; | |
1191 | |
1192 if (tf) { | |
1193 ngx_memzero(buf, sizeof(ngx_buf_t)); | |
1194 | |
1195 buf->in_file = 1; | |
1196 buf->file_last = tf->file.offset; | |
1197 buf->file = &tf->file; | |
1198 | |
1199 rb->buf = NULL; | |
1200 } | |
1201 | |
1202 if (r->headers_in.content_length_n < 0) { | |
1203 r->headers_in.content_length_n = rb->rest; | |
1204 } | |
1205 | |
1206 if (rb->post_handler) { | |
1207 rb->post_handler(r); | |
1208 } | |
1209 } | |
1210 | |
1211 return ngx_http_spdy_state_complete(sc, pos, end); | |
1212 | |
1213 error: | |
1214 | |
1215 if (rb->post_handler) { | |
1216 | |
1217 if (stream->skip_data == NGX_SPDY_DATA_ERROR) { | |
1218 rc = (r->headers_in.content_length_n == -1) | |
1219 ? NGX_HTTP_REQUEST_ENTITY_TOO_LARGE | |
1220 : NGX_HTTP_BAD_REQUEST; | |
1221 | |
1222 } else { | |
1223 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
1224 } | |
1225 | |
1226 ngx_http_finalize_request(r, rc); | |
1227 } | |
1228 | |
1229 return ngx_http_spdy_state_skip(sc, pos, end); | |
1230 } | |
1231 | |
1232 | |
1233 static u_char * | |
1234 ngx_http_spdy_state_rst_stream(ngx_http_spdy_connection_t *sc, u_char *pos, | |
1235 u_char *end) | |
1236 { | |
1237 ngx_uint_t sid, status; | |
1238 ngx_event_t *ev; | |
1239 ngx_connection_t *fc; | |
1240 ngx_http_request_t *r; | |
1241 ngx_http_spdy_stream_t *stream; | |
1242 | |
1243 if (end - pos < NGX_SPDY_RST_STREAM_SIZE) { | |
1244 return ngx_http_spdy_state_save(sc, pos, end, | |
1245 ngx_http_spdy_state_rst_stream); | |
1246 } | |
1247 | |
1248 if (sc->length != NGX_SPDY_RST_STREAM_SIZE) { | |
1249 /* TODO logging */ | |
1250 return ngx_http_spdy_state_protocol_error(sc); | |
1251 } | |
1252 | |
1253 sid = ngx_spdy_frame_parse_sid(pos); | |
1254 | |
1255 pos += NGX_SPDY_SID_SIZE; | |
1256 | |
1257 status = ngx_spdy_frame_parse_uint32(pos); | |
1258 | |
1259 pos += sizeof(uint32_t); | |
1260 | |
1261 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
1262 "spdy RST_STREAM sid:%ui st:%ui", sid, status); | |
1263 | |
1264 | |
1265 switch (status) { | |
1266 | |
1267 case NGX_SPDY_PROTOCOL_ERROR: | |
1268 /* TODO logging */ | |
1269 return ngx_http_spdy_state_protocol_error(sc); | |
1270 | |
1271 case NGX_SPDY_INVALID_STREAM: | |
1272 /* TODO */ | |
1273 break; | |
1274 | |
1275 case NGX_SPDY_REFUSED_STREAM: | |
1276 /* TODO */ | |
1277 break; | |
1278 | |
1279 case NGX_SPDY_UNSUPPORTED_VERSION: | |
1280 /* TODO logging */ | |
1281 return ngx_http_spdy_state_protocol_error(sc); | |
1282 | |
1283 case NGX_SPDY_CANCEL: | |
1284 case NGX_SPDY_INTERNAL_ERROR: | |
1285 stream = ngx_http_spdy_get_stream_by_id(sc, sid); | |
1286 if (stream == NULL) { | |
1287 /* TODO false cancel */ | |
1288 break; | |
1289 } | |
1290 | |
1291 stream->in_closed = 1; | |
1292 stream->out_closed = 1; | |
1293 | |
1294 r = stream->request; | |
1295 | |
1296 fc = r->connection; | |
1297 fc->error = 1; | |
1298 | |
1299 ev = fc->read; | |
1300 ev->handler(ev); | |
1301 | |
1302 break; | |
1303 | |
1304 case NGX_SPDY_FLOW_CONTROL_ERROR: | |
1305 /* TODO logging */ | |
1306 return ngx_http_spdy_state_protocol_error(sc); | |
1307 | |
1308 default: | |
1309 /* TODO */ | |
1310 return ngx_http_spdy_state_protocol_error(sc); | |
1311 } | |
1312 | |
1313 return ngx_http_spdy_state_complete(sc, pos, end); | |
1314 } | |
1315 | |
1316 | |
1317 static u_char * | |
1318 ngx_http_spdy_state_ping(ngx_http_spdy_connection_t *sc, u_char *pos, | |
1319 u_char *end) | |
1320 { | |
1321 u_char *p; | |
1322 ngx_buf_t *buf; | |
1323 ngx_http_spdy_out_frame_t *frame; | |
1324 | |
1325 if (end - pos < NGX_SPDY_PING_SIZE) { | |
1326 return ngx_http_spdy_state_save(sc, pos, end, | |
1327 ngx_http_spdy_state_ping); | |
1328 } | |
1329 | |
1330 if (sc->length != NGX_SPDY_PING_SIZE) { | |
1331 /* TODO logging */ | |
1332 return ngx_http_spdy_state_protocol_error(sc); | |
1333 } | |
1334 | |
1335 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
1336 "spdy PING frame"); | |
1337 | |
1338 frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_PING_SIZE, | |
1339 NGX_SPDY_HIGHEST_PRIORITY); | |
1340 if (frame == NULL) { | |
1341 return ngx_http_spdy_state_internal_error(sc); | |
1342 } | |
1343 | |
1344 buf = frame->first->buf; | |
1345 | |
1346 p = buf->pos; | |
1347 | |
1348 p = ngx_spdy_frame_write_head(p, NGX_SPDY_PING); | |
1349 p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_PING_SIZE); | |
1350 | |
1351 p = ngx_cpymem(p, pos, NGX_SPDY_PING_SIZE); | |
1352 | |
1353 buf->last = p; | |
1354 | |
1355 ngx_http_spdy_queue_frame(sc, frame); | |
1356 | |
1357 pos += NGX_SPDY_PING_SIZE; | |
1358 | |
1359 return ngx_http_spdy_state_complete(sc, pos, end); | |
1360 } | |
1361 | |
1362 | |
1363 static u_char * | |
1364 ngx_http_spdy_state_skip(ngx_http_spdy_connection_t *sc, u_char *pos, | |
1365 u_char *end) | |
1366 { | |
1367 size_t size; | |
1368 | |
1369 size = end - pos; | |
1370 | |
1371 if (size < sc->length) { | |
1372 sc->length -= size; | |
1373 return ngx_http_spdy_state_save(sc, end, end, | |
1374 ngx_http_spdy_state_skip); | |
1375 } | |
1376 | |
1377 return ngx_http_spdy_state_complete(sc, pos + sc->length, end); | |
1378 } | |
1379 | |
1380 | |
1381 static u_char * | |
1382 ngx_http_spdy_state_settings(ngx_http_spdy_connection_t *sc, u_char *pos, | |
1383 u_char *end) | |
1384 { | |
1385 ngx_uint_t v; | |
1386 ngx_http_spdy_srv_conf_t *sscf; | |
1387 | |
1388 if (sc->headers == 0) { | |
1389 | |
1390 if (end - pos < NGX_SPDY_SETTINGS_NUM_SIZE) { | |
1391 return ngx_http_spdy_state_save(sc, pos, end, | |
1392 ngx_http_spdy_state_settings); | |
1393 } | |
1394 | |
1395 sc->headers = ngx_spdy_frame_parse_uint32(pos); | |
1396 | |
1397 pos += NGX_SPDY_SETTINGS_NUM_SIZE; | |
1398 sc->length -= NGX_SPDY_SETTINGS_NUM_SIZE; | |
1399 | |
1400 if (sc->length < sc->headers * NGX_SPDY_SETTINGS_PAIR_SIZE) { | |
1401 /* TODO logging */ | |
1402 return ngx_http_spdy_state_protocol_error(sc); | |
1403 } | |
1404 | |
1405 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
1406 "spdy SETTINGS frame consists of %ui entries", | |
1407 sc->headers); | |
1408 } | |
1409 | |
1410 while (sc->headers) { | |
1411 if (end - pos < NGX_SPDY_SETTINGS_PAIR_SIZE) { | |
1412 return ngx_http_spdy_state_save(sc, pos, end, | |
1413 ngx_http_spdy_state_settings); | |
1414 } | |
1415 | |
1416 sc->headers--; | |
1417 | |
1418 if (pos[0] != NGX_SPDY_SETTINGS_MAX_STREAMS) { | |
1419 pos += NGX_SPDY_SETTINGS_PAIR_SIZE; | |
1420 sc->length -= NGX_SPDY_SETTINGS_PAIR_SIZE; | |
1421 continue; | |
1422 } | |
1423 | |
1424 v = ngx_spdy_frame_parse_uint32(pos + NGX_SPDY_SETTINGS_IDF_SIZE); | |
1425 | |
1426 sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
1427 ngx_http_spdy_module); | |
1428 | |
1429 if (v != sscf->concurrent_streams) { | |
1430 ngx_http_spdy_send_settings(sc); | |
1431 } | |
1432 | |
1433 return ngx_http_spdy_state_skip(sc, pos, end); | |
1434 } | |
1435 | |
1436 ngx_http_spdy_send_settings(sc); | |
1437 | |
1438 return ngx_http_spdy_state_complete(sc, pos, end); | |
1439 } | |
1440 | |
1441 | |
1442 static u_char * | |
1443 ngx_http_spdy_state_noop(ngx_http_spdy_connection_t *sc, u_char *pos, | |
1444 u_char *end) | |
1445 { | |
1446 if (sc->length) { | |
1447 /* TODO logging */ | |
1448 return ngx_http_spdy_state_protocol_error(sc); | |
1449 } | |
1450 | |
1451 return ngx_http_spdy_state_complete(sc, pos, end); | |
1452 } | |
1453 | |
1454 | |
1455 static u_char * | |
1456 ngx_http_spdy_state_complete(ngx_http_spdy_connection_t *sc, u_char *pos, | |
1457 u_char *end) | |
1458 { | |
1459 sc->handler = ngx_http_spdy_state_head; | |
1460 return pos; | |
1461 } | |
1462 | |
1463 | |
1464 static u_char * | |
1465 ngx_http_spdy_state_save(ngx_http_spdy_connection_t *sc, | |
1466 u_char *pos, u_char *end, ngx_http_spdy_handler_pt handler) | |
1467 { | |
1468 #if (NGX_DEBUG) | |
1469 if (end - pos > NGX_SPDY_STATE_BUFFER_SIZE) { | |
1470 ngx_log_error(NGX_LOG_ALERT, sc->connection->log, 0, | |
1471 "spdy state buffer overflow: " | |
1472 "%i bytes required", end - pos); | |
1473 return ngx_http_spdy_state_internal_error(sc); | |
1474 } | |
1475 #endif | |
1476 | |
1477 ngx_memcpy(sc->buffer, pos, NGX_SPDY_STATE_BUFFER_SIZE); | |
1478 | |
1479 sc->buffer_used = end - pos; | |
1480 sc->handler = handler; | |
1481 sc->waiting = 1; | |
1482 | |
1483 return end; | |
1484 } | |
1485 | |
1486 | |
1487 static u_char * | |
1488 ngx_http_spdy_state_protocol_error(ngx_http_spdy_connection_t *sc) | |
1489 { | |
1490 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
1491 "spdy state protocol error"); | |
1492 | |
1493 /* TODO */ | |
1494 ngx_http_spdy_finalize_connection(sc, NGX_HTTP_CLIENT_CLOSED_REQUEST); | |
1495 return NULL; | |
1496 } | |
1497 | |
1498 | |
1499 static u_char * | |
1500 ngx_http_spdy_state_internal_error(ngx_http_spdy_connection_t *sc) | |
1501 { | |
1502 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
1503 "spdy state internal error"); | |
1504 | |
1505 /* TODO */ | |
1506 ngx_http_spdy_finalize_connection(sc, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
1507 return NULL; | |
1508 } | |
1509 | |
1510 | |
1511 static ngx_int_t | |
1512 ngx_http_spdy_send_rst_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t sid, | |
1513 ngx_uint_t status, ngx_uint_t priority) | |
1514 { | |
1515 u_char *p; | |
1516 ngx_buf_t *buf; | |
1517 ngx_http_spdy_out_frame_t *frame; | |
1518 | |
1519 if (sc->connection->error) { | |
1520 return NGX_OK; | |
1521 } | |
1522 | |
1523 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
1524 "spdy write RST_STREAM sid:%ui st:%ui", sid, status); | |
1525 | |
1526 frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_RST_STREAM_SIZE, | |
1527 priority); | |
1528 if (frame == NULL) { | |
1529 return NGX_ERROR; | |
1530 } | |
1531 | |
1532 buf = frame->first->buf; | |
1533 | |
1534 p = buf->pos; | |
1535 | |
1536 p = ngx_spdy_frame_write_head(p, NGX_SPDY_RST_STREAM); | |
1537 p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_RST_STREAM_SIZE); | |
1538 | |
1539 p = ngx_spdy_frame_write_sid(p, sid); | |
1540 p = ngx_spdy_frame_aligned_write_uint32(p, status); | |
1541 | |
1542 buf->last = p; | |
1543 | |
1544 ngx_http_spdy_queue_frame(sc, frame); | |
1545 | |
1546 return NGX_OK; | |
1547 } | |
1548 | |
1549 | |
1550 #if 0 | |
1551 static ngx_int_t | |
1552 ngx_http_spdy_send_goaway(ngx_http_spdy_connection_t *sc) | |
1553 { | |
1554 u_char *p; | |
1555 ngx_buf_t *buf; | |
1556 ngx_http_spdy_out_frame_t *frame; | |
1557 | |
1558 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
1559 "spdy create GOAWAY sid:%ui", sc->last_sid); | |
1560 | |
1561 frame = ngx_http_spdy_get_ctl_frame(sc, NGX_SPDY_GOAWAY_SIZE, | |
1562 NGX_SPDY_HIGHEST_PRIORITY); | |
1563 if (frame == NULL) { | |
1564 return NGX_ERROR; | |
1565 } | |
1566 | |
1567 buf = frame->first->buf; | |
1568 | |
1569 p = buf->pos; | |
1570 | |
1571 p = ngx_spdy_frame_write_head(p, NGX_SPDY_GOAWAY); | |
1572 p = ngx_spdy_frame_write_flags_and_len(p, 0, NGX_SPDY_GOAWAY_SIZE); | |
1573 | |
1574 p = ngx_spdy_frame_write_sid(p, sc->last_sid); | |
1575 | |
1576 buf->last = p; | |
1577 | |
1578 ngx_http_spdy_queue_frame(sc, frame); | |
1579 | |
1580 return NGX_OK; | |
1581 } | |
1582 #endif | |
1583 | |
1584 | |
1585 static ngx_int_t | |
1586 ngx_http_spdy_send_settings(ngx_http_spdy_connection_t *sc) | |
1587 { | |
1588 u_char *p; | |
1589 ngx_buf_t *buf; | |
1590 ngx_pool_t *pool; | |
1591 ngx_chain_t *cl; | |
1592 ngx_http_spdy_srv_conf_t *sscf; | |
1593 ngx_http_spdy_out_frame_t *frame; | |
1594 | |
1595 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
1596 "spdy create SETTINGS frame"); | |
1597 | |
1598 pool = sc->connection->pool; | |
1599 | |
1600 frame = ngx_palloc(pool, sizeof(ngx_http_spdy_out_frame_t)); | |
1601 if (frame == NULL) { | |
1602 return NGX_ERROR; | |
1603 } | |
1604 | |
1605 cl = ngx_alloc_chain_link(pool); | |
1606 if (cl == NULL) { | |
1607 return NGX_ERROR; | |
1608 } | |
1609 | |
1610 buf = ngx_create_temp_buf(pool, NGX_SPDY_FRAME_HEADER_SIZE | |
1611 + NGX_SPDY_SETTINGS_NUM_SIZE | |
1612 + NGX_SPDY_SETTINGS_PAIR_SIZE); | |
1613 if (buf == NULL) { | |
1614 return NGX_ERROR; | |
1615 } | |
1616 | |
1617 buf->last_buf = 1; | |
1618 | |
1619 cl->buf = buf; | |
1620 cl->next = NULL; | |
1621 | |
1622 frame->first = cl; | |
1623 frame->last = cl; | |
1624 frame->handler = ngx_http_spdy_settings_frame_handler; | |
1625 #if (NGX_DEBUG) | |
1626 frame->stream = NULL; | |
1627 frame->size = NGX_SPDY_FRAME_HEADER_SIZE | |
1628 + NGX_SPDY_SETTINGS_NUM_SIZE | |
1629 + NGX_SPDY_SETTINGS_PAIR_SIZE; | |
1630 #endif | |
1631 frame->priority = NGX_SPDY_HIGHEST_PRIORITY; | |
1632 frame->blocked = 0; | |
1633 | |
1634 p = buf->pos; | |
1635 | |
1636 p = ngx_spdy_frame_write_head(p, NGX_SPDY_SETTINGS); | |
1637 p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_CLEAR_SETTINGS, | |
1638 NGX_SPDY_SETTINGS_NUM_SIZE | |
1639 + NGX_SPDY_SETTINGS_PAIR_SIZE); | |
1640 | |
1641 p = ngx_spdy_frame_aligned_write_uint32(p, 1); | |
1642 p = ngx_spdy_frame_aligned_write_uint32(p, | |
1643 NGX_SPDY_SETTINGS_MAX_STREAMS << 24 | |
1644 | NGX_SPDY_SETTINGS_FLAG_PERSIST); | |
1645 | |
1646 sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
1647 ngx_http_spdy_module); | |
1648 | |
1649 p = ngx_spdy_frame_aligned_write_uint32(p, sscf->concurrent_streams); | |
1650 | |
1651 buf->last = p; | |
1652 | |
1653 ngx_http_spdy_queue_frame(sc, frame); | |
1654 | |
1655 return NGX_OK; | |
1656 } | |
1657 | |
1658 | |
1659 ngx_int_t | |
1660 ngx_http_spdy_settings_frame_handler(ngx_http_spdy_connection_t *sc, | |
1661 ngx_http_spdy_out_frame_t *frame) | |
1662 { | |
1663 ngx_buf_t *buf; | |
1664 | |
1665 buf = frame->first->buf; | |
1666 | |
1667 if (buf->pos != buf->last) { | |
1668 return NGX_AGAIN; | |
1669 } | |
1670 | |
1671 ngx_free_chain(sc->pool, frame->first); | |
1672 | |
1673 return NGX_OK; | |
1674 } | |
1675 | |
1676 | |
1677 static ngx_http_spdy_out_frame_t * | |
1678 ngx_http_spdy_get_ctl_frame(ngx_http_spdy_connection_t *sc, size_t size, | |
1679 ngx_uint_t priority) | |
1680 { | |
1681 ngx_chain_t *cl; | |
1682 ngx_http_spdy_out_frame_t *frame; | |
1683 | |
1684 frame = sc->free_ctl_frames; | |
1685 | |
1686 if (frame) { | |
1687 sc->free_ctl_frames = frame->free; | |
1688 | |
1689 cl = frame->first; | |
1690 cl->buf->pos = cl->buf->start; | |
1691 | |
1692 } else { | |
1693 frame = ngx_palloc(sc->pool, sizeof(ngx_http_spdy_out_frame_t)); | |
1694 if (frame == NULL) { | |
1695 return NULL; | |
1696 } | |
1697 | |
1698 cl = ngx_alloc_chain_link(sc->pool); | |
1699 if (cl == NULL) { | |
1700 return NULL; | |
1701 } | |
1702 | |
1703 cl->buf = ngx_create_temp_buf(sc->pool, | |
1704 NGX_SPDY_CTL_FRAME_BUFFER_SIZE); | |
1705 if (cl->buf == NULL) { | |
1706 return NULL; | |
1707 } | |
1708 | |
1709 cl->buf->last_buf = 1; | |
1710 | |
1711 frame->first = cl; | |
1712 frame->last = cl; | |
1713 frame->handler = ngx_http_spdy_ctl_frame_handler; | |
1714 } | |
1715 | |
1716 frame->free = NULL; | |
1717 | |
1718 #if (NGX_DEBUG) | |
1719 if (size > NGX_SPDY_CTL_FRAME_BUFFER_SIZE - NGX_SPDY_FRAME_HEADER_SIZE) { | |
1720 ngx_log_error(NGX_LOG_ALERT, sc->pool->log, 0, | |
1721 "requested control frame is too big: %z", size); | |
1722 return NULL; | |
1723 } | |
1724 | |
1725 frame->stream = NULL; | |
1726 frame->size = size; | |
1727 #endif | |
1728 | |
1729 frame->priority = priority; | |
1730 frame->blocked = 0; | |
1731 | |
1732 return frame; | |
1733 } | |
1734 | |
1735 | |
1736 static ngx_int_t | |
1737 ngx_http_spdy_ctl_frame_handler(ngx_http_spdy_connection_t *sc, | |
1738 ngx_http_spdy_out_frame_t *frame) | |
1739 { | |
1740 ngx_buf_t *buf; | |
1741 | |
1742 buf = frame->first->buf; | |
1743 | |
1744 if (buf->pos != buf->last) { | |
1745 return NGX_AGAIN; | |
1746 } | |
1747 | |
1748 frame->free = sc->free_ctl_frames; | |
1749 sc->free_ctl_frames = frame; | |
1750 | |
1751 return NGX_OK; | |
1752 } | |
1753 | |
1754 | |
1755 static ngx_http_spdy_stream_t * | |
1756 ngx_http_spdy_create_stream(ngx_http_spdy_connection_t *sc, ngx_uint_t id, | |
1757 ngx_uint_t priority) | |
1758 { | |
1759 ngx_log_t *log; | |
1760 ngx_uint_t index; | |
1761 ngx_event_t *rev, *wev; | |
1762 ngx_connection_t *fc; | |
1763 ngx_http_log_ctx_t *ctx; | |
1764 ngx_http_request_t *r; | |
1765 ngx_http_spdy_stream_t *stream; | |
1766 ngx_http_core_srv_conf_t *cscf; | |
1767 ngx_http_spdy_srv_conf_t *sscf; | |
1768 | |
1769 fc = sc->free_fake_connections; | |
1770 | |
1771 if (fc) { | |
1772 sc->free_fake_connections = fc->data; | |
1773 | |
1774 rev = fc->read; | |
1775 wev = fc->write; | |
1776 log = fc->log; | |
1777 ctx = log->data; | |
1778 | |
1779 } else { | |
1780 fc = ngx_palloc(sc->pool, sizeof(ngx_connection_t)); | |
1781 if (fc == NULL) { | |
1782 return NULL; | |
1783 } | |
1784 | |
1785 rev = ngx_palloc(sc->pool, sizeof(ngx_event_t)); | |
1786 if (rev == NULL) { | |
1787 return NULL; | |
1788 } | |
1789 | |
1790 wev = ngx_palloc(sc->pool, sizeof(ngx_event_t)); | |
1791 if (wev == NULL) { | |
1792 return NULL; | |
1793 } | |
1794 | |
1795 log = ngx_palloc(sc->pool, sizeof(ngx_log_t)); | |
1796 if (log == NULL) { | |
1797 return NULL; | |
1798 } | |
1799 | |
1800 ctx = ngx_palloc(sc->pool, sizeof(ngx_http_log_ctx_t)); | |
1801 if (ctx == NULL) { | |
1802 return NULL; | |
1803 } | |
1804 | |
1805 ctx->connection = fc; | |
1806 ctx->request = NULL; | |
1807 } | |
1808 | |
1809 ngx_memcpy(log, sc->connection->log, sizeof(ngx_log_t)); | |
1810 | |
1811 log->data = ctx; | |
1812 | |
1813 ngx_memzero(rev, sizeof(ngx_event_t)); | |
1814 | |
1815 rev->data = fc; | |
1816 rev->ready = 1; | |
1817 rev->handler = ngx_http_empty_handler; | |
1818 rev->log = log; | |
1819 | |
1820 ngx_memcpy(wev, rev, sizeof(ngx_event_t)); | |
1821 | |
1822 wev->write = 1; | |
1823 | |
1824 ngx_memcpy(fc, sc->connection, sizeof(ngx_connection_t)); | |
1825 | |
1826 fc->data = sc->http_connection; | |
1827 fc->read = rev; | |
1828 fc->write = wev; | |
1829 fc->sent = 0; | |
1830 fc->log = log; | |
1831 fc->buffered = 0; | |
1832 fc->sndlowat = 1; | |
1833 | |
1834 r = ngx_http_create_request(fc); | |
1835 if (r == NULL) { | |
1836 return NULL; | |
1837 } | |
1838 | |
1839 r->valid_location = 1; | |
1840 | |
1841 fc->data = r; | |
1842 sc->connection->requests++; | |
1843 | |
1844 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
1845 | |
1846 r->header_in = ngx_create_temp_buf(r->pool, | |
1847 cscf->client_header_buffer_size); | |
1848 if (r->header_in == NULL) { | |
1849 ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
1850 return NULL; | |
1851 } | |
1852 | |
1853 r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE; | |
1854 | |
1855 stream = ngx_pcalloc(r->pool, sizeof(ngx_http_spdy_stream_t)); | |
1856 if (stream == NULL) { | |
1857 ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
1858 return NULL; | |
1859 } | |
1860 | |
1861 r->spdy_stream = stream; | |
1862 | |
1863 stream->id = id; | |
1864 stream->request = r; | |
1865 stream->connection = sc; | |
1866 stream->priority = priority; | |
1867 | |
1868 sscf = ngx_http_get_module_srv_conf(r, ngx_http_spdy_module); | |
1869 | |
1870 index = ngx_http_spdy_stream_index(sscf, id); | |
1871 | |
1872 stream->index = sc->streams_index[index]; | |
1873 sc->streams_index[index] = stream; | |
1874 | |
1875 sc->processing++; | |
1876 | |
1877 return stream; | |
1878 } | |
1879 | |
1880 | |
1881 static ngx_http_spdy_stream_t * | |
1882 ngx_http_spdy_get_stream_by_id(ngx_http_spdy_connection_t *sc, | |
1883 ngx_uint_t sid) | |
1884 { | |
1885 ngx_http_spdy_stream_t *stream; | |
1886 ngx_http_spdy_srv_conf_t *sscf; | |
1887 | |
1888 sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
1889 ngx_http_spdy_module); | |
1890 | |
1891 stream = sc->streams_index[ngx_http_spdy_stream_index(sscf, sid)]; | |
1892 | |
1893 while (stream) { | |
1894 if (stream->id == sid) { | |
1895 return stream; | |
1896 } | |
1897 | |
1898 stream = stream->index; | |
1899 } | |
1900 | |
1901 return NULL; | |
1902 } | |
1903 | |
1904 | |
1905 static ngx_int_t | |
1906 ngx_http_spdy_parse_header(ngx_http_request_t *r) | |
1907 { | |
1908 u_char *p, *end, ch; | |
1909 ngx_uint_t len, hash; | |
1910 ngx_http_core_srv_conf_t *cscf; | |
1911 | |
1912 enum { | |
1913 sw_name_len = 0, | |
1914 sw_name, | |
1915 sw_value_len, | |
1916 sw_value | |
1917 } state; | |
1918 | |
1919 state = r->state; | |
1920 | |
1921 p = r->header_in->pos; | |
1922 end = r->header_in->last; | |
1923 | |
1924 switch (state) { | |
1925 | |
1926 case sw_name_len: | |
1927 | |
1928 if (end - p < NGX_SPDY_NV_NLEN_SIZE) { | |
1929 return NGX_AGAIN; | |
1930 } | |
1931 | |
1932 len = ngx_spdy_frame_parse_uint16(p); | |
1933 | |
1934 if (!len) { | |
1935 return NGX_HTTP_PARSE_INVALID_HEADER; | |
1936 } | |
1937 | |
1938 p += NGX_SPDY_NV_NLEN_SIZE; | |
1939 | |
1940 r->header_name_end = p + len; | |
1941 r->lowcase_index = len; | |
1942 r->invalid_header = 0; | |
1943 | |
1944 state = sw_name; | |
1945 | |
1946 /* fall through */ | |
1947 | |
1948 case sw_name: | |
1949 | |
1950 if (r->header_name_end > end) { | |
1951 break; | |
1952 } | |
1953 | |
1954 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
1955 | |
1956 r->header_name_start = p; | |
1957 | |
1958 hash = 0; | |
1959 | |
1960 for ( /* void */ ; p != r->header_name_end; p++) { | |
1961 | |
1962 ch = *p; | |
1963 | |
1964 hash = ngx_hash(hash, ch); | |
1965 | |
1966 if ((ch >= 'a' && ch <= 'z') | |
1967 || (ch == '-') | |
1968 || (ch >= '0' && ch <= '9') | |
1969 || (ch == '_' && cscf->underscores_in_headers)) | |
1970 { | |
1971 continue; | |
1972 } | |
1973 | |
1974 switch (ch) { | |
1975 case '\0': | |
1976 case LF: | |
1977 case CR: | |
1978 case ':': | |
1979 return NGX_HTTP_PARSE_INVALID_REQUEST; | |
1980 } | |
1981 | |
1982 if (ch >= 'A' && ch <= 'Z') { | |
1983 return NGX_HTTP_PARSE_INVALID_HEADER; | |
1984 } | |
1985 | |
1986 r->invalid_header = 1; | |
1987 } | |
1988 | |
1989 r->header_hash = hash; | |
1990 | |
1991 state = sw_value_len; | |
1992 | |
1993 /* fall through */ | |
1994 | |
1995 case sw_value_len: | |
1996 | |
1997 if (end - p < NGX_SPDY_NV_VLEN_SIZE) { | |
1998 break; | |
1999 } | |
2000 | |
2001 len = ngx_spdy_frame_parse_uint16(p); | |
2002 | |
2003 if (!len) { | |
2004 return NGX_ERROR; | |
2005 } | |
2006 | |
2007 p += NGX_SPDY_NV_VLEN_SIZE; | |
2008 | |
2009 r->header_end = p + len; | |
2010 | |
2011 state = sw_value; | |
2012 | |
2013 /* fall through */ | |
2014 | |
2015 case sw_value: | |
2016 | |
2017 if (r->header_end > end) { | |
2018 break; | |
2019 } | |
2020 | |
2021 r->header_start = p; | |
2022 | |
2023 for ( /* void */ ; p != r->header_end; p++) { | |
2024 | |
2025 ch = *p; | |
2026 | |
2027 if (ch == '\0') { | |
2028 | |
2029 if (p == r->header_start) { | |
2030 return NGX_ERROR; | |
2031 } | |
2032 | |
2033 r->header_size = p - r->header_start; | |
2034 r->header_in->pos = p + 1; | |
2035 | |
2036 return NGX_OK; | |
2037 } | |
2038 | |
2039 if (ch == CR || ch == LF) { | |
2040 return NGX_HTTP_PARSE_INVALID_HEADER; | |
2041 } | |
2042 } | |
2043 | |
2044 r->header_size = p - r->header_start; | |
2045 r->header_in->pos = p; | |
2046 | |
2047 r->state = 0; | |
2048 | |
2049 return NGX_DONE; | |
2050 } | |
2051 | |
2052 r->header_in->pos = p; | |
2053 r->state = state; | |
2054 | |
2055 return NGX_AGAIN; | |
2056 } | |
2057 | |
2058 | |
2059 static ngx_int_t | |
2060 ngx_http_spdy_alloc_large_header_buffer(ngx_http_request_t *r) | |
2061 { | |
2062 u_char *old, *new; | |
2063 size_t rest; | |
2064 ngx_buf_t *buf; | |
2065 ngx_http_spdy_stream_t *stream; | |
2066 ngx_http_core_srv_conf_t *cscf; | |
2067 | |
2068 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2069 "spdy alloc large header buffer"); | |
2070 | |
2071 stream = r->spdy_stream; | |
2072 | |
2073 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
2074 | |
2075 if (stream->header_buffers | |
2076 == (ngx_uint_t) cscf->large_client_header_buffers.num) | |
2077 { | |
2078 return NGX_DECLINED; | |
2079 } | |
2080 | |
2081 rest = r->header_in->last - r->header_in->pos; | |
2082 | |
2083 if (rest >= cscf->large_client_header_buffers.size) { | |
2084 return NGX_DECLINED; | |
2085 } | |
2086 | |
2087 buf = ngx_create_temp_buf(r->pool, cscf->large_client_header_buffers.size); | |
2088 if (buf == NULL) { | |
2089 return NGX_ERROR; | |
2090 } | |
2091 | |
2092 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2093 "spdy large header alloc: %p %uz", | |
2094 buf->pos, buf->end - buf->last); | |
2095 | |
2096 old = r->header_in->pos; | |
2097 new = buf->pos; | |
2098 | |
2099 if (rest) { | |
2100 buf->last = ngx_cpymem(new, old, rest); | |
2101 } | |
2102 | |
2103 if (r->header_name_end > old) { | |
2104 r->header_name_end = new + (r->header_name_end - old); | |
2105 | |
2106 } else if (r->header_end > old) { | |
2107 r->header_end = new + (r->header_end - old); | |
2108 } | |
2109 | |
2110 r->header_in = buf; | |
2111 | |
2112 stream->header_buffers++; | |
2113 | |
2114 return NGX_OK; | |
2115 } | |
2116 | |
2117 | |
2118 static ngx_int_t | |
2119 ngx_http_spdy_handle_request_header(ngx_http_request_t *r) | |
2120 { | |
2121 ngx_uint_t i; | |
2122 ngx_table_elt_t *h; | |
2123 ngx_http_core_srv_conf_t *cscf; | |
2124 ngx_http_spdy_request_header_t *sh; | |
2125 | |
2126 if (r->invalid_header) { | |
2127 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); | |
2128 | |
2129 if (cscf->ignore_invalid_headers) { | |
2130 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
2131 "client sent invalid header: \"%*s\"", | |
2132 r->header_end - r->header_name_start, | |
2133 r->header_name_start); | |
2134 return NGX_OK; | |
2135 } | |
2136 | |
2137 } else { | |
2138 for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { | |
2139 sh = &ngx_http_spdy_request_headers[i]; | |
2140 | |
2141 if (sh->hash != r->header_hash | |
2142 || sh->len != r->lowcase_index | |
2143 || ngx_strncmp(sh->header, r->header_name_start, | |
2144 r->lowcase_index) | |
2145 != 0) | |
2146 { | |
2147 continue; | |
2148 } | |
2149 | |
2150 return sh->handler(r); | |
2151 } | |
2152 } | |
2153 | |
2154 h = ngx_list_push(&r->headers_in.headers); | |
2155 if (h == NULL) { | |
2156 ngx_http_spdy_close_stream(r->spdy_stream, | |
2157 NGX_HTTP_INTERNAL_SERVER_ERROR); | |
2158 return NGX_ERROR; | |
2159 } | |
2160 | |
2161 h->hash = r->header_hash; | |
2162 | |
2163 h->key.len = r->lowcase_index; | |
2164 h->key.data = r->header_name_start; | |
2165 h->key.data[h->key.len] = '\0'; | |
2166 | |
2167 h->value.len = r->header_size; | |
2168 h->value.data = r->header_start; | |
2169 h->value.data[h->value.len] = '\0'; | |
2170 | |
2171 h->lowcase_key = h->key.data; | |
2172 | |
2173 return NGX_OK; | |
2174 } | |
2175 | |
2176 | |
2177 void | |
2178 ngx_http_spdy_request_headers_init() | |
2179 { | |
2180 ngx_uint_t i; | |
2181 ngx_http_spdy_request_header_t *h; | |
2182 | |
2183 for (i = 0; i < NGX_SPDY_REQUEST_HEADERS; i++) { | |
2184 h = &ngx_http_spdy_request_headers[i]; | |
2185 h->hash = ngx_hash_key(h->header, h->len); | |
2186 } | |
2187 } | |
2188 | |
2189 | |
2190 static ngx_int_t | |
2191 ngx_http_spdy_parse_method(ngx_http_request_t *r) | |
2192 { | |
2193 size_t k, len; | |
2194 ngx_uint_t n; | |
2195 const u_char *p, *m; | |
2196 | |
2197 /* | |
2198 * This array takes less than 256 sequential bytes, | |
2199 * and if typical CPU cache line size is 64 bytes, | |
2200 * it is prefetched for 4 load operations. | |
2201 */ | |
2202 static const struct { | |
2203 u_char len; | |
2204 const u_char method[11]; | |
2205 uint32_t value; | |
2206 } tests[] = { | |
2207 { 3, "GET", NGX_HTTP_GET }, | |
2208 { 4, "POST", NGX_HTTP_POST }, | |
2209 { 4, "HEAD", NGX_HTTP_HEAD }, | |
2210 { 7, "OPTIONS", NGX_HTTP_OPTIONS }, | |
2211 { 8, "PROPFIND", NGX_HTTP_PROPFIND }, | |
2212 { 3, "PUT", NGX_HTTP_PUT }, | |
2213 { 5, "MKCOL", NGX_HTTP_MKCOL }, | |
2214 { 6, "DELETE", NGX_HTTP_DELETE }, | |
2215 { 4, "COPY", NGX_HTTP_COPY }, | |
2216 { 4, "MOVE", NGX_HTTP_MOVE }, | |
2217 { 9, "PROPPATCH", NGX_HTTP_PROPPATCH }, | |
2218 { 4, "LOCK", NGX_HTTP_LOCK }, | |
2219 { 6, "UNLOCK", NGX_HTTP_UNLOCK }, | |
2220 { 5, "PATCH", NGX_HTTP_PATCH }, | |
2221 { 5, "TRACE", NGX_HTTP_TRACE } | |
2222 }, *test; | |
2223 | |
2224 if (r->method_name.len) { | |
2225 return NGX_HTTP_PARSE_INVALID_HEADER; | |
2226 } | |
2227 | |
2228 len = r->header_size; | |
2229 | |
2230 r->method_name.len = len; | |
2231 r->method_name.data = r->header_start; | |
2232 | |
2233 test = tests; | |
2234 n = sizeof(tests) / sizeof(tests[0]); | |
2235 | |
2236 do { | |
2237 if (len == test->len) { | |
2238 p = r->method_name.data; | |
2239 m = test->method; | |
2240 k = len; | |
2241 | |
2242 do { | |
2243 if (*p++ != *m++) { | |
2244 goto next; | |
2245 } | |
2246 } while (--k); | |
2247 | |
2248 r->method = test->value; | |
2249 return NGX_OK; | |
2250 } | |
2251 | |
2252 next: | |
2253 test++; | |
2254 | |
2255 } while (--n); | |
2256 | |
2257 p = r->method_name.data; | |
2258 | |
2259 do { | |
2260 if ((*p < 'A' || *p > 'Z') && *p != '_') { | |
2261 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, | |
2262 "client sent invalid method"); | |
2263 return NGX_HTTP_PARSE_INVALID_REQUEST; | |
2264 } | |
2265 | |
2266 p++; | |
2267 | |
2268 } while (--len); | |
2269 | |
2270 return NGX_OK; | |
2271 } | |
2272 | |
2273 | |
2274 static ngx_int_t | |
2275 ngx_http_spdy_parse_scheme(ngx_http_request_t *r) | |
2276 { | |
2277 if (r->schema_start) { | |
2278 return NGX_HTTP_PARSE_INVALID_HEADER; | |
2279 } | |
2280 | |
2281 r->schema_start = r->header_start; | |
2282 r->schema_end = r->header_end; | |
2283 | |
2284 return NGX_OK; | |
2285 } | |
2286 | |
2287 | |
2288 static ngx_int_t | |
2289 ngx_http_spdy_parse_url(ngx_http_request_t *r) | |
2290 { | |
2291 if (r->unparsed_uri.len) { | |
2292 return NGX_HTTP_PARSE_INVALID_HEADER; | |
2293 } | |
2294 | |
2295 r->uri_start = r->header_start; | |
2296 r->uri_end = r->header_end; | |
2297 | |
2298 if (ngx_http_parse_uri(r) != NGX_OK) { | |
2299 return NGX_HTTP_PARSE_INVALID_REQUEST; | |
2300 } | |
2301 | |
2302 if (ngx_http_process_request_uri(r) != NGX_OK) { | |
2303 return NGX_ERROR; | |
2304 } | |
2305 | |
2306 return NGX_OK; | |
2307 } | |
2308 | |
2309 | |
2310 static ngx_int_t | |
2311 ngx_http_spdy_parse_version(ngx_http_request_t *r) | |
2312 { | |
2313 u_char *p, ch; | |
2314 | |
2315 if (r->http_protocol.len) { | |
2316 return NGX_HTTP_PARSE_INVALID_HEADER; | |
2317 } | |
2318 | |
2319 p = r->header_start; | |
2320 | |
2321 if (r->header_size < 8 || !(ngx_str5cmp(p, 'H', 'T', 'T', 'P', '/'))) { | |
2322 return NGX_HTTP_PARSE_INVALID_REQUEST; | |
2323 } | |
2324 | |
2325 ch = *(p + 5); | |
2326 | |
2327 if (ch < '1' || ch > '9') { | |
2328 return NGX_HTTP_PARSE_INVALID_REQUEST; | |
2329 } | |
2330 | |
2331 r->http_major = ch - '0'; | |
2332 | |
2333 for (p += 6; p != r->header_end - 2; p++) { | |
2334 | |
2335 ch = *p; | |
2336 | |
2337 if (ch < '0' || ch > '9') { | |
2338 return NGX_HTTP_PARSE_INVALID_REQUEST; | |
2339 } | |
2340 | |
2341 r->http_major = r->http_major * 10 + ch - '0'; | |
2342 } | |
2343 | |
2344 if (*p != '.') { | |
2345 return NGX_HTTP_PARSE_INVALID_REQUEST; | |
2346 } | |
2347 | |
2348 ch = *(p + 1); | |
2349 | |
2350 if (ch < '0' || ch > '9') { | |
2351 return NGX_HTTP_PARSE_INVALID_REQUEST; | |
2352 } | |
2353 | |
2354 r->http_minor = ch - '0'; | |
2355 | |
2356 for (p += 2; p != r->header_end; p++) { | |
2357 | |
2358 ch = *p; | |
2359 | |
2360 if (ch < '0' || ch > '9') { | |
2361 return NGX_HTTP_PARSE_INVALID_REQUEST; | |
2362 } | |
2363 | |
2364 r->http_minor = r->http_minor * 10 + ch - '0'; | |
2365 } | |
2366 | |
2367 r->http_protocol.len = r->header_size; | |
2368 r->http_protocol.data = r->header_start; | |
2369 r->http_version = r->http_major * 1000 + r->http_minor; | |
2370 | |
2371 return NGX_OK; | |
2372 } | |
2373 | |
2374 | |
2375 static ngx_int_t | |
2376 ngx_http_spdy_construct_request_line(ngx_http_request_t *r) | |
2377 { | |
2378 u_char *p; | |
2379 | |
2380 if (r->method_name.len == 0 | |
2381 || r->unparsed_uri.len == 0 | |
2382 || r->http_protocol.len == 0) | |
2383 { | |
2384 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); | |
2385 return NGX_ERROR; | |
2386 } | |
2387 | |
2388 r->request_line.len = r->method_name.len + 1 | |
2389 + r->unparsed_uri.len + 1 | |
2390 + r->http_protocol.len; | |
2391 | |
2392 p = ngx_pnalloc(r->pool, r->request_line.len + 1); | |
2393 if (p == NULL) { | |
2394 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
2395 return NGX_ERROR; | |
2396 } | |
2397 | |
2398 r->request_line.data = p; | |
2399 | |
2400 p = ngx_cpymem(p, r->method_name.data, r->method_name.len); | |
2401 | |
2402 *p++ = ' '; | |
2403 | |
2404 p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len); | |
2405 | |
2406 *p++ = ' '; | |
2407 | |
2408 ngx_memcpy(p, r->http_protocol.data, r->http_protocol.len + 1); | |
2409 | |
2410 /* some modules expect the space character after method name */ | |
2411 r->method_name.data = r->request_line.data; | |
2412 | |
2413 return NGX_OK; | |
2414 } | |
2415 | |
2416 | |
2417 static void | |
2418 ngx_http_spdy_run_request(ngx_http_request_t *r) | |
2419 { | |
2420 ngx_uint_t i; | |
2421 ngx_list_part_t *part; | |
2422 ngx_table_elt_t *h; | |
2423 ngx_http_header_t *hh; | |
2424 ngx_http_core_main_conf_t *cmcf; | |
2425 | |
2426 if (ngx_http_spdy_construct_request_line(r) != NGX_OK) { | |
2427 return; | |
2428 } | |
2429 | |
2430 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2431 "spdy http request line: \"%V\"", &r->request_line); | |
2432 | |
2433 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); | |
2434 | |
2435 part = &r->headers_in.headers.part; | |
2436 h = part->elts; | |
2437 | |
2438 for (i = 0 ;; i++) { | |
2439 | |
2440 if (i >= part->nelts) { | |
2441 if (part->next == NULL) { | |
2442 break; | |
2443 } | |
2444 | |
2445 part = part->next; | |
2446 h = part->elts; | |
2447 i = 0; | |
2448 } | |
2449 | |
2450 hh = ngx_hash_find(&cmcf->headers_in_hash, h[i].hash, | |
2451 h[i].lowcase_key, h[i].key.len); | |
2452 | |
2453 if (hh && hh->handler(r, &h[i], hh->offset) != NGX_OK) { | |
2454 return; | |
2455 } | |
2456 | |
2457 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2458 "http header: \"%V: %V\"", &h[i].key, &h[i].value); | |
2459 } | |
2460 | |
2461 r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; | |
2462 | |
2463 if (ngx_http_process_request_header(r) != NGX_OK) { | |
2464 return; | |
2465 } | |
2466 | |
2467 ngx_http_process_request(r); | |
2468 } | |
2469 | |
2470 | |
2471 static ngx_int_t | |
2472 ngx_http_spdy_init_request_body(ngx_http_request_t *r) | |
2473 { | |
2474 ngx_buf_t *buf; | |
2475 ngx_temp_file_t *tf; | |
2476 ngx_http_request_body_t *rb; | |
2477 ngx_http_core_loc_conf_t *clcf; | |
2478 | |
2479 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); | |
2480 if (rb == NULL) { | |
2481 return NGX_ERROR; | |
2482 } | |
2483 | |
2484 r->request_body = rb; | |
2485 | |
2486 if (r->spdy_stream->in_closed) { | |
2487 return NGX_OK; | |
2488 } | |
2489 | |
2490 rb->rest = r->headers_in.content_length_n; | |
2491 | |
2492 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | |
2493 | |
2494 if (r->request_body_in_file_only | |
2495 || rb->rest > (off_t) clcf->client_body_buffer_size | |
2496 || rb->rest < 0) | |
2497 { | |
2498 tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); | |
2499 if (tf == NULL) { | |
2500 return NGX_ERROR; | |
2501 } | |
2502 | |
2503 tf->file.fd = NGX_INVALID_FILE; | |
2504 tf->file.log = r->connection->log; | |
2505 tf->path = clcf->client_body_temp_path; | |
2506 tf->pool = r->pool; | |
2507 tf->warn = "a client request body is buffered to a temporary file"; | |
2508 tf->log_level = r->request_body_file_log_level; | |
2509 tf->persistent = r->request_body_in_persistent_file; | |
2510 tf->clean = r->request_body_in_clean_file; | |
2511 | |
2512 if (r->request_body_file_group_access) { | |
2513 tf->access = 0660; | |
2514 } | |
2515 | |
2516 rb->temp_file = tf; | |
2517 | |
2518 if (r->spdy_stream->in_closed | |
2519 && ngx_create_temp_file(&tf->file, tf->path, tf->pool, | |
2520 tf->persistent, tf->clean, tf->access) | |
2521 != NGX_OK) | |
2522 { | |
2523 return NGX_ERROR; | |
2524 } | |
2525 | |
2526 buf = ngx_calloc_buf(r->pool); | |
2527 if (buf == NULL) { | |
2528 return NGX_ERROR; | |
2529 } | |
2530 | |
2531 if (rb->rest == 0) { | |
2532 buf->in_file = 1; | |
2533 buf->file = &tf->file; | |
2534 } else { | |
2535 rb->buf = buf; | |
2536 } | |
2537 | |
2538 } else { | |
2539 | |
2540 if (rb->rest == 0) { | |
2541 return NGX_OK; | |
2542 } | |
2543 | |
2544 buf = ngx_create_temp_buf(r->pool, (size_t) rb->rest); | |
2545 if (buf == NULL) { | |
2546 return NGX_ERROR; | |
2547 } | |
2548 | |
2549 rb->buf = buf; | |
2550 } | |
2551 | |
2552 rb->bufs = ngx_alloc_chain_link(r->pool); | |
2553 if (rb->bufs == NULL) { | |
2554 return NGX_ERROR; | |
2555 } | |
2556 | |
2557 rb->bufs->buf = buf; | |
2558 rb->bufs->next = NULL; | |
2559 | |
2560 rb->rest = 0; | |
2561 | |
2562 return NGX_OK; | |
2563 } | |
2564 | |
2565 | |
2566 ngx_int_t | |
2567 ngx_http_spdy_read_request_body(ngx_http_request_t *r, | |
2568 ngx_http_client_body_handler_pt post_handler) | |
2569 { | |
2570 ngx_http_spdy_stream_t *stream; | |
2571 | |
2572 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
2573 "spdy read request body"); | |
2574 | |
2575 stream = r->spdy_stream; | |
2576 | |
2577 switch (stream->skip_data) { | |
2578 | |
2579 case NGX_SPDY_DATA_DISCARD: | |
2580 post_handler(r); | |
2581 return NGX_OK; | |
2582 | |
2583 case NGX_SPDY_DATA_ERROR: | |
2584 if (r->headers_in.content_length_n == -1) { | |
2585 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; | |
2586 } else { | |
2587 return NGX_HTTP_BAD_REQUEST; | |
2588 } | |
2589 | |
2590 case NGX_SPDY_DATA_INTERNAL_ERROR: | |
2591 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
2592 } | |
2593 | |
2594 if (!r->request_body && ngx_http_spdy_init_request_body(r) != NGX_OK) { | |
2595 stream->skip_data = NGX_SPDY_DATA_INTERNAL_ERROR; | |
2596 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
2597 } | |
2598 | |
2599 if (stream->in_closed) { | |
2600 post_handler(r); | |
2601 return NGX_OK; | |
2602 } | |
2603 | |
2604 r->request_body->post_handler = post_handler; | |
2605 | |
2606 return NGX_AGAIN; | |
2607 } | |
2608 | |
2609 | |
2610 void | |
2611 ngx_http_spdy_close_stream(ngx_http_spdy_stream_t *stream, ngx_int_t rc) | |
2612 { | |
2613 ngx_event_t *ev; | |
2614 ngx_connection_t *fc; | |
2615 ngx_http_spdy_stream_t **index, *s; | |
2616 ngx_http_spdy_srv_conf_t *sscf; | |
2617 ngx_http_spdy_connection_t *sc; | |
2618 | |
2619 sc = stream->connection; | |
2620 | |
2621 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
2622 "spdy close stream %ui, processing %ui", | |
2623 stream->id, sc->processing); | |
2624 | |
2625 if (!stream->out_closed) { | |
2626 if (ngx_http_spdy_send_rst_stream(sc, stream->id, | |
2627 NGX_SPDY_INTERNAL_ERROR, | |
2628 stream->priority) | |
2629 != NGX_OK) | |
2630 { | |
2631 sc->connection->error = 1; | |
2632 } | |
2633 } | |
2634 | |
2635 sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
2636 ngx_http_spdy_module); | |
2637 | |
2638 index = sc->streams_index + ngx_http_spdy_stream_index(sscf, stream->id); | |
2639 | |
2640 for ( ;; ) { | |
2641 s = *index; | |
2642 | |
2643 if (s == NULL) { | |
2644 break; | |
2645 } | |
2646 | |
2647 if (s == stream) { | |
2648 *index = s->index; | |
2649 break; | |
2650 } | |
2651 | |
2652 index = &s->index; | |
2653 } | |
2654 | |
2655 fc = stream->request->connection; | |
2656 | |
2657 ngx_http_free_request(stream->request, rc); | |
2658 | |
2659 ev = fc->read; | |
2660 | |
2661 if (ev->active || ev->disabled) { | |
2662 ngx_del_event(ev, NGX_READ_EVENT, 0); | |
2663 } | |
2664 | |
2665 if (ev->timer_set) { | |
2666 ngx_del_timer(ev); | |
2667 } | |
2668 | |
2669 if (ev->prev) { | |
2670 ngx_delete_posted_event(ev); | |
2671 } | |
2672 | |
2673 ev = fc->write; | |
2674 | |
2675 if (ev->active || ev->disabled) { | |
2676 ngx_del_event(ev, NGX_WRITE_EVENT, 0); | |
2677 } | |
2678 | |
2679 if (ev->timer_set) { | |
2680 ngx_del_timer(ev); | |
2681 } | |
2682 | |
2683 if (ev->prev) { | |
2684 ngx_delete_posted_event(ev); | |
2685 } | |
2686 | |
2687 fc->data = sc->free_fake_connections; | |
2688 sc->free_fake_connections = fc; | |
2689 | |
2690 sc->processing--; | |
2691 | |
2692 if (sc->processing || sc->blocked) { | |
2693 return; | |
2694 } | |
2695 | |
2696 ev = sc->connection->read; | |
2697 | |
2698 ev->handler = ngx_http_spdy_handle_connection_handler; | |
2699 ngx_post_event(ev, &ngx_posted_events); | |
2700 } | |
2701 | |
2702 | |
2703 static void | |
2704 ngx_http_spdy_handle_connection_handler(ngx_event_t *rev) | |
2705 { | |
2706 ngx_connection_t *c; | |
2707 | |
2708 rev->handler = ngx_http_spdy_read_handler; | |
2709 | |
2710 if (rev->ready) { | |
2711 ngx_http_spdy_read_handler(rev); | |
2712 return; | |
2713 } | |
2714 | |
2715 c = rev->data; | |
2716 | |
2717 ngx_http_spdy_handle_connection(c->data); | |
2718 } | |
2719 | |
2720 | |
2721 static void | |
2722 ngx_http_spdy_keepalive_handler(ngx_event_t *rev) | |
2723 { | |
2724 ngx_connection_t *c; | |
2725 ngx_http_spdy_srv_conf_t *sscf; | |
2726 ngx_http_spdy_connection_t *sc; | |
2727 | |
2728 c = rev->data; | |
2729 | |
2730 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "spdy keepalive handler"); | |
2731 | |
2732 if (rev->timedout || c->close) { | |
2733 ngx_http_close_connection(c); | |
2734 return; | |
2735 } | |
2736 | |
2737 #if (NGX_HAVE_KQUEUE) | |
2738 | |
2739 if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { | |
2740 if (rev->pending_eof) { | |
2741 c->log->handler = NULL; | |
2742 ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno, | |
2743 "kevent() reported that client %V closed " | |
2744 "keepalive connection", &c->addr_text); | |
2745 #if (NGX_HTTP_SSL) | |
2746 if (c->ssl) { | |
2747 c->ssl->no_send_shutdown = 1; | |
2748 } | |
2749 #endif | |
2750 ngx_http_close_connection(c); | |
2751 return; | |
2752 } | |
2753 } | |
2754 | |
2755 #endif | |
2756 | |
2757 c->destroyed = 0; | |
2758 c->idle = 0; | |
2759 ngx_reusable_connection(c, 0); | |
2760 | |
2761 sc = c->data; | |
2762 | |
2763 sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
2764 ngx_http_spdy_module); | |
2765 | |
2766 sc->pool = ngx_create_pool(sscf->pool_size, sc->connection->log); | |
2767 if (sc->pool == NULL) { | |
2768 ngx_http_close_connection(c); | |
2769 return; | |
2770 } | |
2771 | |
2772 sc->streams_index = ngx_pcalloc(sc->pool, | |
2773 ngx_http_spdy_streams_index_size(sscf) | |
2774 * sizeof(ngx_http_spdy_stream_t *)); | |
2775 if (sc->streams_index == NULL) { | |
2776 ngx_http_close_connection(c); | |
2777 return; | |
2778 } | |
2779 | |
2780 c->write->handler = ngx_http_spdy_write_handler; | |
2781 | |
2782 rev->handler = ngx_http_spdy_read_handler; | |
2783 ngx_http_spdy_read_handler(rev); | |
2784 } | |
2785 | |
2786 | |
2787 static void | |
2788 ngx_http_spdy_finalize_connection(ngx_http_spdy_connection_t *sc, | |
2789 ngx_int_t rc) | |
2790 { | |
2791 ngx_uint_t i, size; | |
2792 ngx_event_t *ev; | |
2793 ngx_connection_t *c, *fc; | |
2794 ngx_http_request_t *r; | |
2795 ngx_http_spdy_stream_t *stream; | |
2796 ngx_http_spdy_srv_conf_t *sscf; | |
2797 | |
2798 c = sc->connection; | |
2799 | |
2800 if (!sc->processing) { | |
2801 ngx_http_close_connection(c); | |
2802 return; | |
2803 } | |
2804 | |
2805 c->error = 1; | |
2806 c->read->handler = ngx_http_empty_handler; | |
2807 | |
2808 sc->last_out = NULL; | |
2809 | |
2810 sc->blocked = 1; | |
2811 | |
2812 sscf = ngx_http_get_module_srv_conf(sc->http_connection->conf_ctx, | |
2813 ngx_http_spdy_module); | |
2814 | |
2815 size = ngx_http_spdy_streams_index_size(sscf); | |
2816 | |
2817 for (i = 0; i < size; i++) { | |
2818 stream = sc->streams_index[i]; | |
2819 | |
2820 while (stream) { | |
2821 r = stream->request; | |
2822 | |
2823 fc = r->connection; | |
2824 fc->error = 1; | |
2825 | |
2826 if (stream->waiting) { | |
2827 r->blocked -= stream->waiting; | |
2828 stream->waiting = 0; | |
2829 ev = fc->write; | |
2830 | |
2831 } else { | |
2832 ev = fc->read; | |
2833 } | |
2834 | |
2835 stream = stream->index; | |
2836 | |
2837 ev->eof = 1; | |
2838 ev->handler(ev); | |
2839 } | |
2840 } | |
2841 | |
2842 sc->blocked = 0; | |
2843 | |
2844 if (sc->processing) { | |
2845 return; | |
2846 } | |
2847 | |
2848 ngx_http_close_connection(c); | |
2849 } | |
2850 | |
2851 | |
2852 static void | |
2853 ngx_http_spdy_pool_cleanup(void *data) | |
2854 { | |
2855 ngx_http_spdy_connection_t *sc = data; | |
2856 | |
2857 if (sc->pool) { | |
2858 ngx_destroy_pool(sc->pool); | |
2859 } | |
2860 } | |
2861 | |
2862 | |
2863 static void * | |
2864 ngx_http_spdy_zalloc(void *opaque, u_int items, u_int size) | |
2865 { | |
2866 ngx_http_spdy_connection_t *sc = opaque; | |
2867 | |
2868 return ngx_palloc(sc->connection->pool, items * size); | |
2869 } | |
2870 | |
2871 | |
2872 static void | |
2873 ngx_http_spdy_zfree(void *opaque, void *address) | |
2874 { | |
2875 #if 0 | |
2876 ngx_http_spdy_connection_t *sc = opaque; | |
2877 | |
2878 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | |
2879 "spdy zfree: %p", address); | |
2880 #endif | |
2881 } |