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 }