comparison src/http/ngx_http_spdy_filter_module.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 5776804fff04
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 <nginx.h>
12 #include <ngx_http_spdy_module.h>
13
14 #include <zlib.h>
15
16
17 #define NGX_SPDY_WRITE_BUFFERED NGX_HTTP_WRITE_BUFFERED
18
19 #define ngx_http_spdy_nv_nsize(h) (NGX_SPDY_NV_NLEN_SIZE + sizeof(h) - 1)
20 #define ngx_http_spdy_nv_vsize(h) (NGX_SPDY_NV_VLEN_SIZE + sizeof(h) - 1)
21
22 #define ngx_http_spdy_nv_write_num ngx_spdy_frame_write_uint16
23 #define ngx_http_spdy_nv_write_nlen ngx_spdy_frame_write_uint16
24 #define ngx_http_spdy_nv_write_vlen ngx_spdy_frame_write_uint16
25
26 #define ngx_http_spdy_nv_write_name(p, h) \
27 ngx_cpymem(ngx_http_spdy_nv_write_nlen(p, sizeof(h) - 1), h, sizeof(h) - 1)
28
29 #define ngx_http_spdy_nv_write_val(p, h) \
30 ngx_cpymem(ngx_http_spdy_nv_write_vlen(p, sizeof(h) - 1), h, sizeof(h) - 1)
31
32 static ngx_inline ngx_int_t ngx_http_spdy_filter_send(
33 ngx_connection_t *fc, ngx_http_spdy_stream_t *stream);
34
35 static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame(
36 ngx_http_spdy_stream_t *stream, size_t len, ngx_uint_t flags,
37 ngx_chain_t *first, ngx_chain_t *last);
38
39 static ngx_int_t ngx_http_spdy_syn_frame_handler(
40 ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
41 static ngx_int_t ngx_http_spdy_data_frame_handler(
42 ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame);
43 static ngx_inline void ngx_http_spdy_handle_frame(
44 ngx_http_spdy_stream_t *stream, ngx_http_spdy_out_frame_t *frame);
45 static ngx_inline void ngx_http_spdy_handle_stream(
46 ngx_http_spdy_connection_t *sc, ngx_http_spdy_stream_t *stream);
47
48 static void ngx_http_spdy_filter_cleanup(void *data);
49
50 static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf);
51
52
53 static ngx_http_module_t ngx_http_spdy_filter_module_ctx = {
54 NULL, /* preconfiguration */
55 ngx_http_spdy_filter_init, /* postconfiguration */
56
57 NULL, /* create main configuration */
58 NULL, /* init main configuration */
59
60 NULL, /* create server configuration */
61 NULL, /* merge server configuration */
62
63 NULL, /* create location configuration */
64 NULL /* merge location configuration */
65 };
66
67
68 ngx_module_t ngx_http_spdy_filter_module = {
69 NGX_MODULE_V1,
70 &ngx_http_spdy_filter_module_ctx, /* module context */
71 NULL, /* module directives */
72 NGX_HTTP_MODULE, /* module type */
73 NULL, /* init master */
74 NULL, /* init module */
75 NULL, /* init process */
76 NULL, /* init thread */
77 NULL, /* exit thread */
78 NULL, /* exit process */
79 NULL, /* exit master */
80 NGX_MODULE_V1_PADDING
81 };
82
83
84 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
85 static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
86
87
88 static ngx_int_t
89 ngx_http_spdy_header_filter(ngx_http_request_t *r)
90 {
91 int rc;
92 size_t len;
93 u_char *p, *buf, *last;
94 ngx_buf_t *b;
95 ngx_str_t host;
96 ngx_uint_t i, j, count, port;
97 ngx_chain_t *cl;
98 ngx_list_part_t *part, *pt;
99 ngx_table_elt_t *header, *h;
100 ngx_connection_t *c;
101 ngx_http_cleanup_t *cln;
102 ngx_http_core_loc_conf_t *clcf;
103 ngx_http_core_srv_conf_t *cscf;
104 ngx_http_spdy_stream_t *stream;
105 ngx_http_spdy_out_frame_t *frame;
106 ngx_http_spdy_connection_t *sc;
107 struct sockaddr_in *sin;
108 #if (NGX_HAVE_INET6)
109 struct sockaddr_in6 *sin6;
110 #endif
111 u_char addr[NGX_SOCKADDR_STRLEN];
112
113 if (!r->spdy_stream) {
114 return ngx_http_next_header_filter(r);
115 }
116
117 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
118 "spdy header filter");
119
120 if (r->header_sent) {
121 return NGX_OK;
122 }
123
124 r->header_sent = 1;
125
126 if (r != r->main) {
127 return NGX_OK;
128 }
129
130 c = r->connection;
131
132 if (r->method == NGX_HTTP_HEAD) {
133 r->header_only = 1;
134 }
135
136 switch (r->headers_out.status) {
137
138 case NGX_HTTP_OK:
139 case NGX_HTTP_PARTIAL_CONTENT:
140 break;
141
142 case NGX_HTTP_NOT_MODIFIED:
143 r->header_only = 1;
144 break;
145
146 case NGX_HTTP_NO_CONTENT:
147 r->header_only = 1;
148
149 ngx_str_null(&r->headers_out.content_type);
150
151 r->headers_out.content_length = NULL;
152 r->headers_out.content_length_n = -1;
153
154 /* fall through */
155
156 default:
157 r->headers_out.last_modified_time = -1;
158 r->headers_out.last_modified = NULL;
159 }
160
161 len = NGX_SPDY_NV_NUM_SIZE
162 + ngx_http_spdy_nv_nsize("version")
163 + ngx_http_spdy_nv_vsize("HTTP/1.1")
164 + ngx_http_spdy_nv_nsize("status")
165 + ngx_http_spdy_nv_vsize("418");
166
167 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
168
169 if (r->headers_out.server == NULL) {
170 len += ngx_http_spdy_nv_nsize("server");
171 len += clcf->server_tokens ? ngx_http_spdy_nv_vsize(NGINX_VER)
172 : ngx_http_spdy_nv_vsize("nginx");
173 }
174
175 if (r->headers_out.date == NULL) {
176 len += ngx_http_spdy_nv_nsize("date")
177 + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
178 }
179
180 if (r->headers_out.content_type.len) {
181 len += ngx_http_spdy_nv_nsize("content-type")
182 + NGX_SPDY_NV_VLEN_SIZE + r->headers_out.content_type.len;
183
184 if (r->headers_out.content_type_len == r->headers_out.content_type.len
185 && r->headers_out.charset.len)
186 {
187 len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
188 }
189 }
190
191 if (r->headers_out.content_length == NULL
192 && r->headers_out.content_length_n >= 0)
193 {
194 len += ngx_http_spdy_nv_nsize("content-length")
195 + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN;
196 }
197
198 if (r->headers_out.last_modified == NULL
199 && r->headers_out.last_modified_time != -1)
200 {
201 len += ngx_http_spdy_nv_nsize("last-modified")
202 + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
203 }
204
205 if (r->headers_out.location
206 && r->headers_out.location->value.len
207 && r->headers_out.location->value.data[0] == '/')
208 {
209 r->headers_out.location->hash = 0;
210
211 if (clcf->server_name_in_redirect) {
212 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
213 host = cscf->server_name;
214
215 } else if (r->headers_in.server.len) {
216 host = r->headers_in.server;
217
218 } else {
219 host.len = NGX_SOCKADDR_STRLEN;
220 host.data = addr;
221
222 if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
223 return NGX_ERROR;
224 }
225 }
226
227 switch (c->local_sockaddr->sa_family) {
228
229 #if (NGX_HAVE_INET6)
230 case AF_INET6:
231 sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
232 port = ntohs(sin6->sin6_port);
233 break;
234 #endif
235 #if (NGX_HAVE_UNIX_DOMAIN)
236 case AF_UNIX:
237 port = 0;
238 break;
239 #endif
240 default: /* AF_INET */
241 sin = (struct sockaddr_in *) c->local_sockaddr;
242 port = ntohs(sin->sin_port);
243 break;
244 }
245
246 len += ngx_http_spdy_nv_nsize("location")
247 + ngx_http_spdy_nv_vsize("https://")
248 + host.len
249 + r->headers_out.location->value.len;
250
251 if (clcf->port_in_redirect) {
252
253 #if (NGX_HTTP_SSL)
254 if (c->ssl)
255 port = (port == 443) ? 0 : port;
256 else
257 #endif
258 port = (port == 80) ? 0 : port;
259
260 } else {
261 port = 0;
262 }
263
264 if (port) {
265 len += sizeof(":65535") - 1;
266 }
267
268 } else {
269 ngx_str_null(&host);
270 port = 0;
271 }
272
273 part = &r->headers_out.headers.part;
274 header = part->elts;
275
276 for (i = 0; /* void */; i++) {
277
278 if (i >= part->nelts) {
279 if (part->next == NULL) {
280 break;
281 }
282
283 part = part->next;
284 header = part->elts;
285 i = 0;
286 }
287
288 if (header[i].hash == 0) {
289 continue;
290 }
291
292 len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len
293 + NGX_SPDY_NV_VLEN_SIZE + header[i].value.len;
294 }
295
296 buf = ngx_alloc(len, r->pool->log);
297 if (buf == NULL) {
298 return NGX_ERROR;
299 }
300
301 last = buf + NGX_SPDY_NV_NUM_SIZE;
302
303 last = ngx_http_spdy_nv_write_name(last, "version");
304 last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1");
305
306 last = ngx_http_spdy_nv_write_name(last, "status");
307 last = ngx_spdy_frame_write_uint16(last, 3);
308 last = ngx_sprintf(last, "%03ui", r->headers_out.status);
309
310 count = 2;
311
312 if (r->headers_out.server == NULL) {
313 last = ngx_http_spdy_nv_write_name(last, "server");
314 last = clcf->server_tokens
315 ? ngx_http_spdy_nv_write_val(last, NGINX_VER)
316 : ngx_http_spdy_nv_write_val(last, "nginx");
317
318 count++;
319 }
320
321 if (r->headers_out.date == NULL) {
322 last = ngx_http_spdy_nv_write_name(last, "date");
323
324 last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len);
325
326 last = ngx_cpymem(last, ngx_cached_http_time.data,
327 ngx_cached_http_time.len);
328
329 count++;
330 }
331
332 if (r->headers_out.content_type.len) {
333
334 last = ngx_http_spdy_nv_write_name(last, "content-type");
335
336 p = last + NGX_SPDY_NV_VLEN_SIZE;
337
338 last = ngx_cpymem(p, r->headers_out.content_type.data,
339 r->headers_out.content_type.len);
340
341 if (r->headers_out.content_type_len == r->headers_out.content_type.len
342 && r->headers_out.charset.len)
343 {
344 last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1);
345
346 last = ngx_cpymem(last, r->headers_out.charset.data,
347 r->headers_out.charset.len);
348
349 /* update r->headers_out.content_type for possible logging */
350
351 r->headers_out.content_type.len = last - p;
352 r->headers_out.content_type.data = p;
353 }
354
355 (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
356 r->headers_out.content_type.len);
357
358 count++;
359 }
360
361 if (r->headers_out.content_length == NULL
362 && r->headers_out.content_length_n >= 0)
363 {
364 last = ngx_http_spdy_nv_write_name(last, "content-length");
365
366 p = last + NGX_SPDY_NV_VLEN_SIZE;
367
368 last = ngx_sprintf(p, "%O", r->headers_out.content_length_n);
369
370 (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
371 last - p);
372
373 count++;
374 }
375
376 if (r->headers_out.last_modified == NULL
377 && r->headers_out.last_modified_time != -1)
378 {
379 last = ngx_http_spdy_nv_write_name(last, "last-modified");
380
381 p = last + NGX_SPDY_NV_VLEN_SIZE;
382
383 last = ngx_http_time(p, r->headers_out.last_modified_time);
384
385 (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
386 last - p);
387
388 count++;
389 }
390
391 if (host.data) {
392
393 last = ngx_http_spdy_nv_write_name(last, "location");
394
395 p = last + NGX_SPDY_NV_VLEN_SIZE;
396
397 last = ngx_cpymem(p, "http", sizeof("http") - 1);
398
399 #if (NGX_HTTP_SSL)
400 if (c->ssl) {
401 *last++ ='s';
402 }
403 #endif
404
405 *last++ = ':'; *last++ = '/'; *last++ = '/';
406
407 last = ngx_cpymem(last, host.data, host.len);
408
409 if (port) {
410 last = ngx_sprintf(last, ":%ui", port);
411 }
412
413 last = ngx_cpymem(last, r->headers_out.location->value.data,
414 r->headers_out.location->value.len);
415
416 /* update r->headers_out.location->value for possible logging */
417
418 r->headers_out.location->value.len = last - p;
419 r->headers_out.location->value.data = p;
420 ngx_str_set(&r->headers_out.location->key, "location");
421
422 (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
423 r->headers_out.location->value.len);
424
425 count++;
426 }
427
428 part = &r->headers_out.headers.part;
429 header = part->elts;
430
431 for (i = 0; /* void */; i++) {
432
433 if (i >= part->nelts) {
434 if (part->next == NULL) {
435 break;
436 }
437
438 part = part->next;
439 header = part->elts;
440 i = 0;
441 }
442
443 if (header[i].hash == 0 || header[i].hash == 2) {
444 continue;
445 }
446
447 if ((header[i].key.len == 6
448 && ngx_strncasecmp(header[i].key.data,
449 (u_char *) "status", 6) == 0)
450 || (header[i].key.len == 7
451 && ngx_strncasecmp(header[i].key.data,
452 (u_char *) "version", 7) == 0))
453 {
454 header[i].hash = 0;
455 continue;
456 }
457
458 last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len);
459
460 ngx_strlow(last, header[i].key.data, header[i].key.len);
461 last += header[i].key.len;
462
463 p = last + NGX_SPDY_NV_VLEN_SIZE;
464
465 last = ngx_cpymem(p, header[i].value.data, header[i].value.len);
466
467 pt = part;
468 h = header;
469
470 for (j = i + 1; /* void */; j++) {
471
472 if (j >= pt->nelts) {
473 if (pt->next == NULL) {
474 break;
475 }
476
477 pt = pt->next;
478 h = pt->elts;
479 j = 0;
480 }
481
482 if (h[j].hash == 0 || h[j].hash == 2
483 || h[j].key.len != header[i].key.len
484 || ngx_strncasecmp(header[i].key.data, h[j].key.data,
485 header[i].key.len))
486 {
487 continue;
488 }
489
490 *last++ = '\0';
491
492 last = ngx_cpymem(last, h[j].value.data, h[j].value.len);
493
494 h[j].hash = 2;
495 }
496
497 (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
498 last - p);
499
500 count++;
501 }
502
503 (void) ngx_spdy_frame_write_uint16(buf, count);
504
505 stream = r->spdy_stream;
506 sc = stream->connection;
507
508 len = last - buf;
509
510 b = ngx_create_temp_buf(r->pool, NGX_SPDY_FRAME_HEADER_SIZE
511 + NGX_SPDY_SYN_REPLY_SIZE
512 + deflateBound(&sc->zstream_out, len));
513 if (b == NULL) {
514 ngx_free(buf);
515 return NGX_ERROR;
516 }
517
518 b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE;
519
520 sc->zstream_out.next_in = buf;
521 sc->zstream_out.avail_in = len;
522 sc->zstream_out.next_out = b->last;
523 sc->zstream_out.avail_out = b->end - b->last;
524
525 rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH);
526
527 ngx_free(buf);
528
529 if (rc != Z_OK) {
530 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
531 "spdy deflate() failed: %d", rc);
532 return NGX_ERROR;
533 }
534
535 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,
536 "spdy deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
537 sc->zstream_out.next_in, sc->zstream_out.next_out,
538 sc->zstream_out.avail_in, sc->zstream_out.avail_out,
539 rc);
540
541 b->last = sc->zstream_out.next_out;
542
543 p = b->pos;
544 p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY);
545
546 len = b->last - b->pos;
547
548 r->header_size = len;
549
550 if (r->header_only) {
551 b->last_buf = 1;
552 p = ngx_spdy_frame_write_flags_and_len(p, NGX_SPDY_FLAG_FIN,
553 len - NGX_SPDY_FRAME_HEADER_SIZE);
554 } else {
555 p = ngx_spdy_frame_write_flags_and_len(p, 0,
556 len - NGX_SPDY_FRAME_HEADER_SIZE);
557 }
558
559 (void) ngx_spdy_frame_write_sid(p, stream->id);
560
561 cl = ngx_alloc_chain_link(r->pool);
562 if (cl == NULL) {
563 return NGX_ERROR;
564 }
565
566 cl->buf = b;
567 cl->next = NULL;
568
569 frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t));
570 if (frame == NULL) {
571 return NGX_ERROR;
572 }
573
574 frame->first = cl;
575 frame->last = cl;
576 frame->handler = ngx_http_spdy_syn_frame_handler;
577 frame->free = NULL;
578 frame->stream = stream;
579 frame->size = len;
580 frame->priority = stream->priority;
581 frame->blocked = 1;
582 frame->fin = r->header_only;
583
584 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
585 "spdy:%ui create SYN_REPLY frame %p: size:%uz",
586 stream->id, frame, frame->size);
587
588 ngx_http_spdy_queue_blocked_frame(sc, frame);
589
590 r->blocked++;
591
592 cln = ngx_http_cleanup_add(r, 0);
593 if (cln == NULL) {
594 return NGX_ERROR;
595 }
596
597 cln->handler = ngx_http_spdy_filter_cleanup;
598 cln->data = stream;
599
600 stream->waiting = 1;
601
602 return ngx_http_spdy_filter_send(c, stream);
603 }
604
605
606 static ngx_int_t
607 ngx_http_spdy_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
608 {
609 off_t size;
610 ngx_buf_t *b;
611 ngx_chain_t *cl, *ll, *out, **ln;
612 ngx_http_spdy_stream_t *stream;
613 ngx_http_spdy_out_frame_t *frame;
614
615 stream = r->spdy_stream;
616
617 if (stream == NULL) {
618 return ngx_http_next_body_filter(r, in);
619 }
620
621 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
622 "spdy body filter \"%V?%V\"", &r->uri, &r->args);
623
624 if (in == NULL || r->header_only) {
625
626 if (stream->waiting) {
627 return NGX_AGAIN;
628 }
629
630 r->connection->buffered &= ~NGX_SPDY_WRITE_BUFFERED;
631
632 return NGX_OK;
633 }
634
635 size = 0;
636 ln = &out;
637 ll = in;
638
639 for ( ;; ) {
640 b = ll->buf;
641 #if 1
642 if (ngx_buf_size(b) == 0 && !ngx_buf_special(b)) {
643 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
644 "zero size buf in spdy body filter "
645 "t:%d r:%d f:%d %p %p-%p %p %O-%O",
646 b->temporary,
647 b->recycled,
648 b->in_file,
649 b->start,
650 b->pos,
651 b->last,
652 b->file,
653 b->file_pos,
654 b->file_last);
655
656 ngx_debug_point();
657 return NGX_ERROR;
658 }
659 #endif
660 cl = ngx_alloc_chain_link(r->pool);
661 if (cl == NULL) {
662 return NGX_ERROR;
663 }
664
665 size += ngx_buf_size(b);
666 cl->buf = b;
667
668 *ln = cl;
669 ln = &cl->next;
670
671 if (ll->next == NULL) {
672 break;
673 }
674
675 ll = ll->next;
676 }
677
678 if (size > NGX_SPDY_MAX_FRAME_SIZE) {
679 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
680 "FIXME: chain too big in spdy filter: %O", size);
681 return NGX_ERROR;
682 }
683
684 frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size,
685 b->last_buf, out, cl);
686 if (frame == NULL) {
687 return NGX_ERROR;
688 }
689
690 ngx_http_spdy_queue_frame(stream->connection, frame);
691
692 stream->waiting++;
693
694 r->main->blocked++;
695
696 return ngx_http_spdy_filter_send(r->connection, stream);
697 }
698
699
700 static ngx_http_spdy_out_frame_t *
701 ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream,
702 size_t len, ngx_uint_t fin, ngx_chain_t *first, ngx_chain_t *last)
703 {
704 u_char *p;
705 ngx_buf_t *buf;
706 ngx_uint_t flags;
707 ngx_chain_t *cl;
708 ngx_http_spdy_out_frame_t *frame;
709
710
711 frame = stream->free_frames;
712
713 if (frame) {
714 stream->free_frames = frame->free;
715
716 } else {
717 frame = ngx_palloc(stream->request->pool,
718 sizeof(ngx_http_spdy_out_frame_t));
719 if (frame == NULL) {
720 return NULL;
721 }
722 }
723
724 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
725 "spdy:%ui create DATA frame %p: len:%uz fin:%ui",
726 stream->id, frame, len, fin);
727
728 if (len || fin) {
729
730 flags = fin ? NGX_SPDY_FLAG_FIN : 0;
731
732 cl = ngx_chain_get_free_buf(stream->request->pool,
733 &stream->free_data_headers);
734 if (cl == NULL) {
735 return NULL;
736 }
737
738 buf = cl->buf;
739
740 if (buf->start) {
741 p = buf->start;
742 buf->pos = p;
743
744 p += sizeof(uint32_t);
745
746 (void) ngx_spdy_frame_write_flags_and_len(p, flags, len);
747
748 } else {
749 p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE);
750 if (p == NULL) {
751 return NULL;
752 }
753
754 buf->pos = p;
755 buf->start = p;
756
757 p = ngx_spdy_frame_write_sid(p, stream->id);
758 p = ngx_spdy_frame_write_flags_and_len(p, flags, len);
759
760 buf->last = p;
761 buf->end = p;
762
763 buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_module;
764 buf->memory = 1;
765 }
766
767 cl->next = first;
768 first = cl;
769 }
770
771 frame->first = first;
772 frame->last = last;
773 frame->handler = ngx_http_spdy_data_frame_handler;
774 frame->free = NULL;
775 frame->stream = stream;
776 frame->size = NGX_SPDY_FRAME_HEADER_SIZE + len;
777 frame->priority = stream->priority;
778 frame->blocked = 0;
779 frame->fin = fin;
780
781 return frame;
782 }
783
784
785 static ngx_inline ngx_int_t
786 ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream)
787 {
788 if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) {
789 fc->error = 1;
790 return NGX_ERROR;
791 }
792
793 if (stream->waiting) {
794 fc->buffered |= NGX_SPDY_WRITE_BUFFERED;
795 fc->write->delayed = 1;
796 return NGX_AGAIN;
797 }
798
799 fc->buffered &= ~NGX_SPDY_WRITE_BUFFERED;
800
801 return NGX_OK;
802 }
803
804
805 static ngx_int_t
806 ngx_http_spdy_syn_frame_handler(ngx_http_spdy_connection_t *sc,
807 ngx_http_spdy_out_frame_t *frame)
808 {
809 ngx_buf_t *buf;
810 ngx_http_spdy_stream_t *stream;
811
812 buf = frame->first->buf;
813
814 if (buf->pos != buf->last) {
815 return NGX_AGAIN;
816 }
817
818 stream = frame->stream;
819
820 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
821 "spdy:%ui SYN_REPLY frame %p was sent", stream->id, frame);
822
823 ngx_free_chain(stream->request->pool, frame->first);
824
825 ngx_http_spdy_handle_frame(stream, frame);
826
827 ngx_http_spdy_handle_stream(sc, stream);
828
829 return NGX_OK;
830 }
831
832
833 static ngx_int_t
834 ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc,
835 ngx_http_spdy_out_frame_t *frame)
836 {
837 ngx_chain_t *cl, *ln;
838 ngx_http_spdy_stream_t *stream;
839
840 stream = frame->stream;
841
842 cl = frame->first;
843
844 if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_module) {
845
846 if (cl->buf->pos != cl->buf->last) {
847 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
848 "spdy:%ui DATA frame %p was sent partially",
849 stream->id, frame);
850
851 return NGX_AGAIN;
852 }
853
854 ln = cl->next;
855
856 cl->next = stream->free_data_headers;
857 stream->free_data_headers = cl;
858
859 if (cl == frame->last) {
860 goto done;
861 }
862
863 cl = ln;
864 }
865
866 for ( ;; ) {
867 if (ngx_buf_size(cl->buf) != 0) {
868
869 if (cl != frame->first) {
870 frame->first = cl;
871 ngx_http_spdy_handle_stream(sc, stream);
872 }
873
874 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
875 "spdy:%ui DATA frame %p was sent partially",
876 stream->id, frame);
877
878 return NGX_AGAIN;
879 }
880
881 ln = cl->next;
882
883 ngx_free_chain(stream->request->pool, cl);
884
885 if (cl == frame->last) {
886 goto done;
887 }
888
889 cl = ln;
890 }
891
892 done:
893
894 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
895 "spdy:%ui DATA frame %p was sent", stream->id, frame);
896
897 stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE;
898
899 ngx_http_spdy_handle_frame(stream, frame);
900
901 ngx_http_spdy_handle_stream(sc, stream);
902
903 return NGX_OK;
904 }
905
906
907 static ngx_inline void
908 ngx_http_spdy_handle_frame(ngx_http_spdy_stream_t *stream,
909 ngx_http_spdy_out_frame_t *frame)
910 {
911 ngx_http_request_t *r;
912
913 r = stream->request;
914
915 r->connection->sent += frame->size;
916 r->blocked--;
917
918 if (frame->fin) {
919 stream->out_closed = 1;
920 }
921
922 frame->free = stream->free_frames;
923 stream->free_frames = frame;
924
925 stream->waiting--;
926 }
927
928
929 static ngx_inline void
930 ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc,
931 ngx_http_spdy_stream_t *stream)
932 {
933 ngx_connection_t *fc;
934
935 fc = stream->request->connection;
936
937 fc->write->delayed = 0;
938
939 if (stream->handled) {
940 return;
941 }
942
943 if (sc->blocked == 2) {
944 stream->handled = 1;
945
946 stream->next = sc->last_stream;
947 sc->last_stream = stream;
948 }
949 }
950
951
952 static void
953 ngx_http_spdy_filter_cleanup(void *data)
954 {
955 ngx_http_spdy_stream_t *stream = data;
956
957 ngx_http_request_t *r;
958 ngx_http_spdy_out_frame_t *frame, **fn;
959
960 if (stream->waiting == 0) {
961 return;
962 }
963
964 r = stream->request;
965
966 fn = &stream->connection->last_out;
967
968 for ( ;; ) {
969 frame = *fn;
970
971 if (frame == NULL) {
972 break;
973 }
974
975 if (frame->stream == stream && !frame->blocked) {
976
977 stream->waiting--;
978 r->blocked--;
979
980 *fn = frame->next;
981 continue;
982 }
983
984 fn = &frame->next;
985 }
986 }
987
988
989 static ngx_int_t
990 ngx_http_spdy_filter_init(ngx_conf_t *cf)
991 {
992 ngx_http_next_header_filter = ngx_http_top_header_filter;
993 ngx_http_top_header_filter = ngx_http_spdy_header_filter;
994
995 ngx_http_next_body_filter = ngx_http_top_body_filter;
996 ngx_http_top_body_filter = ngx_http_spdy_body_filter;
997
998 return NGX_OK;
999 }