Mercurial > hg > nginx-quic
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 } |