Update and clean Tomato RAF files
[tomato.git] / release / src / router / nginx / src / http / ngx_http_spdy_filter_module.c
blob8fe46b2e9546af4c1a82dc2fd2afb3df2bf0fda0
2 /*
3 * Copyright (C) Nginx, Inc.
4 * Copyright (C) Valentin V. Bartenev
5 */
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>
14 #include <zlib.h>
17 #define NGX_SPDY_WRITE_BUFFERED NGX_HTTP_WRITE_BUFFERED
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)
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
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)
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)
32 static ngx_inline ngx_int_t ngx_http_spdy_filter_send(
33 ngx_connection_t *fc, ngx_http_spdy_stream_t *stream);
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);
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);
48 static void ngx_http_spdy_filter_cleanup(void *data);
50 static ngx_int_t ngx_http_spdy_filter_init(ngx_conf_t *cf);
53 static ngx_http_module_t ngx_http_spdy_filter_module_ctx = {
54 NULL, /* preconfiguration */
55 ngx_http_spdy_filter_init, /* postconfiguration */
57 NULL, /* create main configuration */
58 NULL, /* init main configuration */
60 NULL, /* create server configuration */
61 NULL, /* merge server configuration */
63 NULL, /* create location configuration */
64 NULL /* merge location configuration */
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
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;
88 static ngx_int_t
89 ngx_http_spdy_header_filter(ngx_http_request_t *r)
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];
113 if (!r->spdy_stream) {
114 return ngx_http_next_header_filter(r);
117 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
118 "spdy header filter");
120 if (r->header_sent) {
121 return NGX_OK;
124 r->header_sent = 1;
126 if (r != r->main) {
127 return NGX_OK;
130 c = r->connection;
132 if (r->method == NGX_HTTP_HEAD) {
133 r->header_only = 1;
136 switch (r->headers_out.status) {
138 case NGX_HTTP_OK:
139 case NGX_HTTP_PARTIAL_CONTENT:
140 break;
142 case NGX_HTTP_NOT_MODIFIED:
143 r->header_only = 1;
144 break;
146 case NGX_HTTP_NO_CONTENT:
147 r->header_only = 1;
149 ngx_str_null(&r->headers_out.content_type);
151 r->headers_out.content_length = NULL;
152 r->headers_out.content_length_n = -1;
154 /* fall through */
156 default:
157 r->headers_out.last_modified_time = -1;
158 r->headers_out.last_modified = NULL;
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");
167 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
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");
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");
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;
184 if (r->headers_out.content_type_len == r->headers_out.content_type.len
185 && r->headers_out.charset.len)
187 len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
191 if (r->headers_out.content_length == NULL
192 && r->headers_out.content_length_n >= 0)
194 len += ngx_http_spdy_nv_nsize("content-length")
195 + NGX_SPDY_NV_VLEN_SIZE + NGX_OFF_T_LEN;
198 if (r->headers_out.last_modified == NULL
199 && r->headers_out.last_modified_time != -1)
201 len += ngx_http_spdy_nv_nsize("last-modified")
202 + ngx_http_spdy_nv_vsize("Wed, 31 Dec 1986 10:00:00 GMT");
205 if (r->headers_out.location
206 && r->headers_out.location->value.len
207 && r->headers_out.location->value.data[0] == '/')
209 r->headers_out.location->hash = 0;
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;
215 } else if (r->headers_in.server.len) {
216 host = r->headers_in.server;
218 } else {
219 host.len = NGX_SOCKADDR_STRLEN;
220 host.data = addr;
222 if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
223 return NGX_ERROR;
227 switch (c->local_sockaddr->sa_family) {
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;
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;
251 if (clcf->port_in_redirect) {
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;
260 } else {
261 port = 0;
264 if (port) {
265 len += sizeof(":65535") - 1;
268 } else {
269 ngx_str_null(&host);
270 port = 0;
273 part = &r->headers_out.headers.part;
274 header = part->elts;
276 for (i = 0; /* void */; i++) {
278 if (i >= part->nelts) {
279 if (part->next == NULL) {
280 break;
283 part = part->next;
284 header = part->elts;
285 i = 0;
288 if (header[i].hash == 0) {
289 continue;
292 len += NGX_SPDY_NV_NLEN_SIZE + header[i].key.len
293 + NGX_SPDY_NV_VLEN_SIZE + header[i].value.len;
296 buf = ngx_alloc(len, r->pool->log);
297 if (buf == NULL) {
298 return NGX_ERROR;
301 last = buf + NGX_SPDY_NV_NUM_SIZE;
303 last = ngx_http_spdy_nv_write_name(last, "version");
304 last = ngx_http_spdy_nv_write_val(last, "HTTP/1.1");
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);
310 count = 2;
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");
318 count++;
321 if (r->headers_out.date == NULL) {
322 last = ngx_http_spdy_nv_write_name(last, "date");
324 last = ngx_http_spdy_nv_write_vlen(last, ngx_cached_http_time.len);
326 last = ngx_cpymem(last, ngx_cached_http_time.data,
327 ngx_cached_http_time.len);
329 count++;
332 if (r->headers_out.content_type.len) {
334 last = ngx_http_spdy_nv_write_name(last, "content-type");
336 p = last + NGX_SPDY_NV_VLEN_SIZE;
338 last = ngx_cpymem(p, r->headers_out.content_type.data,
339 r->headers_out.content_type.len);
341 if (r->headers_out.content_type_len == r->headers_out.content_type.len
342 && r->headers_out.charset.len)
344 last = ngx_cpymem(last, "; charset=", sizeof("; charset=") - 1);
346 last = ngx_cpymem(last, r->headers_out.charset.data,
347 r->headers_out.charset.len);
349 /* update r->headers_out.content_type for possible logging */
351 r->headers_out.content_type.len = last - p;
352 r->headers_out.content_type.data = p;
355 (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
356 r->headers_out.content_type.len);
358 count++;
361 if (r->headers_out.content_length == NULL
362 && r->headers_out.content_length_n >= 0)
364 last = ngx_http_spdy_nv_write_name(last, "content-length");
366 p = last + NGX_SPDY_NV_VLEN_SIZE;
368 last = ngx_sprintf(p, "%O", r->headers_out.content_length_n);
370 (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
371 last - p);
373 count++;
376 if (r->headers_out.last_modified == NULL
377 && r->headers_out.last_modified_time != -1)
379 last = ngx_http_spdy_nv_write_name(last, "last-modified");
381 p = last + NGX_SPDY_NV_VLEN_SIZE;
383 last = ngx_http_time(p, r->headers_out.last_modified_time);
385 (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
386 last - p);
388 count++;
391 if (host.data) {
393 last = ngx_http_spdy_nv_write_name(last, "location");
395 p = last + NGX_SPDY_NV_VLEN_SIZE;
397 last = ngx_cpymem(p, "http", sizeof("http") - 1);
399 #if (NGX_HTTP_SSL)
400 if (c->ssl) {
401 *last++ ='s';
403 #endif
405 *last++ = ':'; *last++ = '/'; *last++ = '/';
407 last = ngx_cpymem(last, host.data, host.len);
409 if (port) {
410 last = ngx_sprintf(last, ":%ui", port);
413 last = ngx_cpymem(last, r->headers_out.location->value.data,
414 r->headers_out.location->value.len);
416 /* update r->headers_out.location->value for possible logging */
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");
422 (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
423 r->headers_out.location->value.len);
425 count++;
428 part = &r->headers_out.headers.part;
429 header = part->elts;
431 for (i = 0; /* void */; i++) {
433 if (i >= part->nelts) {
434 if (part->next == NULL) {
435 break;
438 part = part->next;
439 header = part->elts;
440 i = 0;
443 if (header[i].hash == 0 || header[i].hash == 2) {
444 continue;
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))
454 header[i].hash = 0;
455 continue;
458 last = ngx_http_spdy_nv_write_nlen(last, header[i].key.len);
460 ngx_strlow(last, header[i].key.data, header[i].key.len);
461 last += header[i].key.len;
463 p = last + NGX_SPDY_NV_VLEN_SIZE;
465 last = ngx_cpymem(p, header[i].value.data, header[i].value.len);
467 pt = part;
468 h = header;
470 for (j = i + 1; /* void */; j++) {
472 if (j >= pt->nelts) {
473 if (pt->next == NULL) {
474 break;
477 pt = pt->next;
478 h = pt->elts;
479 j = 0;
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))
487 continue;
490 *last++ = '\0';
492 last = ngx_cpymem(last, h[j].value.data, h[j].value.len);
494 h[j].hash = 2;
497 (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
498 last - p);
500 count++;
503 (void) ngx_spdy_frame_write_uint16(buf, count);
505 stream = r->spdy_stream;
506 sc = stream->connection;
508 len = last - buf;
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;
518 b->last += NGX_SPDY_FRAME_HEADER_SIZE + NGX_SPDY_SYN_REPLY_SIZE;
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;
525 rc = deflate(&sc->zstream_out, Z_SYNC_FLUSH);
527 ngx_free(buf);
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;
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);
541 b->last = sc->zstream_out.next_out;
543 p = b->pos;
544 p = ngx_spdy_frame_write_head(p, NGX_SPDY_SYN_REPLY);
546 len = b->last - b->pos;
548 r->header_size = len;
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);
559 (void) ngx_spdy_frame_write_sid(p, stream->id);
561 cl = ngx_alloc_chain_link(r->pool);
562 if (cl == NULL) {
563 return NGX_ERROR;
566 cl->buf = b;
567 cl->next = NULL;
569 frame = ngx_palloc(r->pool, sizeof(ngx_http_spdy_out_frame_t));
570 if (frame == NULL) {
571 return NGX_ERROR;
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;
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);
588 ngx_http_spdy_queue_blocked_frame(sc, frame);
590 r->blocked++;
592 cln = ngx_http_cleanup_add(r, 0);
593 if (cln == NULL) {
594 return NGX_ERROR;
597 cln->handler = ngx_http_spdy_filter_cleanup;
598 cln->data = stream;
600 stream->waiting = 1;
602 return ngx_http_spdy_filter_send(c, stream);
606 static ngx_int_t
607 ngx_http_spdy_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
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;
615 stream = r->spdy_stream;
617 if (stream == NULL) {
618 return ngx_http_next_body_filter(r, in);
621 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
622 "spdy body filter \"%V?%V\"", &r->uri, &r->args);
624 if (in == NULL || r->header_only) {
626 if (stream->waiting) {
627 return NGX_AGAIN;
630 r->connection->buffered &= ~NGX_SPDY_WRITE_BUFFERED;
632 return NGX_OK;
635 size = 0;
636 ln = &out;
637 ll = in;
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);
656 ngx_debug_point();
657 return NGX_ERROR;
659 #endif
660 cl = ngx_alloc_chain_link(r->pool);
661 if (cl == NULL) {
662 return NGX_ERROR;
665 size += ngx_buf_size(b);
666 cl->buf = b;
668 *ln = cl;
669 ln = &cl->next;
671 if (ll->next == NULL) {
672 break;
675 ll = ll->next;
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;
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;
690 ngx_http_spdy_queue_frame(stream->connection, frame);
692 stream->waiting++;
694 r->main->blocked++;
696 return ngx_http_spdy_filter_send(r->connection, stream);
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)
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;
711 frame = stream->free_frames;
713 if (frame) {
714 stream->free_frames = frame->free;
716 } else {
717 frame = ngx_palloc(stream->request->pool,
718 sizeof(ngx_http_spdy_out_frame_t));
719 if (frame == NULL) {
720 return NULL;
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);
728 if (len || fin) {
730 flags = fin ? NGX_SPDY_FLAG_FIN : 0;
732 cl = ngx_chain_get_free_buf(stream->request->pool,
733 &stream->free_data_headers);
734 if (cl == NULL) {
735 return NULL;
738 buf = cl->buf;
740 if (buf->start) {
741 p = buf->start;
742 buf->pos = p;
744 p += sizeof(uint32_t);
746 (void) ngx_spdy_frame_write_flags_and_len(p, flags, len);
748 } else {
749 p = ngx_palloc(stream->request->pool, NGX_SPDY_FRAME_HEADER_SIZE);
750 if (p == NULL) {
751 return NULL;
754 buf->pos = p;
755 buf->start = p;
757 p = ngx_spdy_frame_write_sid(p, stream->id);
758 p = ngx_spdy_frame_write_flags_and_len(p, flags, len);
760 buf->last = p;
761 buf->end = p;
763 buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_module;
764 buf->memory = 1;
767 cl->next = first;
768 first = cl;
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;
781 return frame;
785 static ngx_inline ngx_int_t
786 ngx_http_spdy_filter_send(ngx_connection_t *fc, ngx_http_spdy_stream_t *stream)
788 if (ngx_http_spdy_send_output_queue(stream->connection) == NGX_ERROR) {
789 fc->error = 1;
790 return NGX_ERROR;
793 if (stream->waiting) {
794 fc->buffered |= NGX_SPDY_WRITE_BUFFERED;
795 fc->write->delayed = 1;
796 return NGX_AGAIN;
799 fc->buffered &= ~NGX_SPDY_WRITE_BUFFERED;
801 return NGX_OK;
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)
809 ngx_buf_t *buf;
810 ngx_http_spdy_stream_t *stream;
812 buf = frame->first->buf;
814 if (buf->pos != buf->last) {
815 return NGX_AGAIN;
818 stream = frame->stream;
820 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
821 "spdy:%ui SYN_REPLY frame %p was sent", stream->id, frame);
823 ngx_free_chain(stream->request->pool, frame->first);
825 ngx_http_spdy_handle_frame(stream, frame);
827 ngx_http_spdy_handle_stream(sc, stream);
829 return NGX_OK;
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)
837 ngx_chain_t *cl, *ln;
838 ngx_http_spdy_stream_t *stream;
840 stream = frame->stream;
842 cl = frame->first;
844 if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_module) {
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);
851 return NGX_AGAIN;
854 ln = cl->next;
856 cl->next = stream->free_data_headers;
857 stream->free_data_headers = cl;
859 if (cl == frame->last) {
860 goto done;
863 cl = ln;
866 for ( ;; ) {
867 if (ngx_buf_size(cl->buf) != 0) {
869 if (cl != frame->first) {
870 frame->first = cl;
871 ngx_http_spdy_handle_stream(sc, stream);
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);
878 return NGX_AGAIN;
881 ln = cl->next;
883 ngx_free_chain(stream->request->pool, cl);
885 if (cl == frame->last) {
886 goto done;
889 cl = ln;
892 done:
894 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0,
895 "spdy:%ui DATA frame %p was sent", stream->id, frame);
897 stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE;
899 ngx_http_spdy_handle_frame(stream, frame);
901 ngx_http_spdy_handle_stream(sc, stream);
903 return NGX_OK;
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)
911 ngx_http_request_t *r;
913 r = stream->request;
915 r->connection->sent += frame->size;
916 r->blocked--;
918 if (frame->fin) {
919 stream->out_closed = 1;
922 frame->free = stream->free_frames;
923 stream->free_frames = frame;
925 stream->waiting--;
929 static ngx_inline void
930 ngx_http_spdy_handle_stream(ngx_http_spdy_connection_t *sc,
931 ngx_http_spdy_stream_t *stream)
933 ngx_connection_t *fc;
935 fc = stream->request->connection;
937 fc->write->delayed = 0;
939 if (stream->handled) {
940 return;
943 if (sc->blocked == 2) {
944 stream->handled = 1;
946 stream->next = sc->last_stream;
947 sc->last_stream = stream;
952 static void
953 ngx_http_spdy_filter_cleanup(void *data)
955 ngx_http_spdy_stream_t *stream = data;
957 ngx_http_request_t *r;
958 ngx_http_spdy_out_frame_t *frame, **fn;
960 if (stream->waiting == 0) {
961 return;
964 r = stream->request;
966 fn = &stream->connection->last_out;
968 for ( ;; ) {
969 frame = *fn;
971 if (frame == NULL) {
972 break;
975 if (frame->stream == stream && !frame->blocked) {
977 stream->waiting--;
978 r->blocked--;
980 *fn = frame->next;
981 continue;
984 fn = &frame->next;
989 static ngx_int_t
990 ngx_http_spdy_filter_init(ngx_conf_t *cf)
992 ngx_http_next_header_filter = ngx_http_top_header_filter;
993 ngx_http_top_header_filter = ngx_http_spdy_header_filter;
995 ngx_http_next_body_filter = ngx_http_top_body_filter;
996 ngx_http_top_body_filter = ngx_http_spdy_body_filter;
998 return NGX_OK;