Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_spdy_filter_module.c
Go to the documentation of this file.
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(
41 static ngx_int_t ngx_http_spdy_data_frame_handler(
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 
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 */
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;
104  ngx_http_spdy_stream_t *stream;
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 
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:
140  break;
141 
143  r->header_only = 1;
144  break;
145 
146  case NGX_HTTP_NO_CONTENT:
147  r->header_only = 1;
148 
150 
151  r->headers_out.content_length = NULL;
153 
154  /* fall through */
155 
156  default:
158  r->headers_out.last_modified = NULL;
159  }
160 
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 
168 
169  if (r->headers_out.server == NULL) {
170  len += ngx_http_spdy_nv_nsize("server");
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")
183 
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")
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
207  && r->headers_out.location->value.data[0] == '/')
208  {
209  r->headers_out.location->hash = 0;
210 
211  if (clcf->server_name_in_redirect) {
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
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
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 
325 
326  last = ngx_cpymem(last, ngx_cached_http_time.data,
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 
340 
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;
353  }
354 
355  (void) ngx_http_spdy_nv_write_vlen(p - NGX_SPDY_NV_VLEN_SIZE,
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 
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,
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,
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 
512  + deflateBound(&sc->zstream_out, len));
513  if (b == NULL) {
514  ngx_free(buf);
515  return NGX_ERROR;
516  }
517 
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) {
531  "spdy deflate() failed: %d", rc);
532  return NGX_ERROR;
533  }
534 
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;
545 
546  len = b->last - b->pos;
547 
548  r->header_size = len;
549 
550  if (r->header_only) {
551  b->last_buf = 1;
554  } else {
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;
614 
615  stream = r->spdy_stream;
616 
617  if (stream == NULL) {
618  return ngx_http_next_body_filter(r, in);
619  }
620 
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 
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)) {
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) {
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 
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;
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) {
795  fc->write->delayed = 1;
796  return NGX_AGAIN;
797  }
798 
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,
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 
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,
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) {
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 
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 
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,
910 {
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 
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 }