comparison src/http/v2/ngx_http_v2_filter_module.c @ 6246:257b51c37c5a

The HTTP/2 implementation (RFC 7240, 7241). The SPDY support is removed, as it's incompatible with the new module.
author Valentin Bartenev <vbart@nginx.com>
date Fri, 11 Sep 2015 20:13:06 +0300
parents
children cbb8c32f78b5
comparison
equal deleted inserted replaced
6245:3cf25d33886a 6246:257b51c37c5a
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_v2_module.h>
13
14
15 #define ngx_http_v2_integer_octets(v) (((v) + 127) / 128)
16
17 #define ngx_http_v2_literal_size(h) \
18 (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)
19
20 #define ngx_http_v2_indexed(i) (128 + (i))
21 #define ngx_http_v2_inc_indexed(i) (64 + (i))
22
23
24 #define NGX_HTTP_V2_STATUS_INDEX 8
25 #define NGX_HTTP_V2_STATUS_200_INDEX 8
26 #define NGX_HTTP_V2_STATUS_204_INDEX 9
27 #define NGX_HTTP_V2_STATUS_206_INDEX 10
28 #define NGX_HTTP_V2_STATUS_304_INDEX 11
29 #define NGX_HTTP_V2_STATUS_400_INDEX 12
30 #define NGX_HTTP_V2_STATUS_404_INDEX 13
31 #define NGX_HTTP_V2_STATUS_500_INDEX 14
32
33 #define NGX_HTTP_V2_CONTENT_LENGTH_INDEX 28
34 #define NGX_HTTP_V2_CONTENT_TYPE_INDEX 31
35 #define NGX_HTTP_V2_DATE_INDEX 33
36 #define NGX_HTTP_V2_LAST_MODIFIED_INDEX 44
37 #define NGX_HTTP_V2_LOCATION_INDEX 46
38 #define NGX_HTTP_V2_SERVER_INDEX 54
39 #define NGX_HTTP_V2_VARY_INDEX 59
40
41
42 static u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,
43 ngx_uint_t value);
44 static void ngx_http_v2_write_headers_head(u_char *pos, size_t length,
45 ngx_uint_t sid, ngx_uint_t end_headers, ngx_uint_t end_stream);
46 static void ngx_http_v2_write_continuation_head(u_char *pos, size_t length,
47 ngx_uint_t sid, ngx_uint_t end_headers);
48
49 static ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc,
50 ngx_chain_t *in, off_t limit);
51
52 static ngx_chain_t *ngx_http_v2_filter_get_shadow(
53 ngx_http_v2_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);
54 static ngx_http_v2_out_frame_t *ngx_http_v2_filter_get_data_frame(
55 ngx_http_v2_stream_t *stream, size_t len, ngx_chain_t *first,
56 ngx_chain_t *last);
57
58 static ngx_inline ngx_int_t ngx_http_v2_flow_control(
59 ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);
60 static void ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
61 ngx_http_v2_stream_t *stream);
62
63 static ngx_inline ngx_int_t ngx_http_v2_filter_send(
64 ngx_connection_t *fc, ngx_http_v2_stream_t *stream);
65
66 static ngx_int_t ngx_http_v2_headers_frame_handler(
67 ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
68 static ngx_int_t ngx_http_v2_data_frame_handler(
69 ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);
70 static ngx_inline void ngx_http_v2_handle_frame(
71 ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame);
72 static ngx_inline void ngx_http_v2_handle_stream(
73 ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);
74
75 static void ngx_http_v2_filter_cleanup(void *data);
76
77 static ngx_int_t ngx_http_v2_filter_init(ngx_conf_t *cf);
78
79
80 static ngx_http_module_t ngx_http_v2_filter_module_ctx = {
81 NULL, /* preconfiguration */
82 ngx_http_v2_filter_init, /* postconfiguration */
83
84 NULL, /* create main configuration */
85 NULL, /* init main configuration */
86
87 NULL, /* create server configuration */
88 NULL, /* merge server configuration */
89
90 NULL, /* create location configuration */
91 NULL /* merge location configuration */
92 };
93
94
95 ngx_module_t ngx_http_v2_filter_module = {
96 NGX_MODULE_V1,
97 &ngx_http_v2_filter_module_ctx, /* module context */
98 NULL, /* module directives */
99 NGX_HTTP_MODULE, /* module type */
100 NULL, /* init master */
101 NULL, /* init module */
102 NULL, /* init process */
103 NULL, /* init thread */
104 NULL, /* exit thread */
105 NULL, /* exit process */
106 NULL, /* exit master */
107 NGX_MODULE_V1_PADDING
108 };
109
110
111 static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
112
113
114 static ngx_int_t
115 ngx_http_v2_header_filter(ngx_http_request_t *r)
116 {
117 u_char status, *p, *head;
118 size_t len, rest, frame_size;
119 ngx_buf_t *b;
120 ngx_str_t host, location;
121 ngx_uint_t i, port, continuation;
122 ngx_chain_t *cl;
123 ngx_list_part_t *part;
124 ngx_table_elt_t *header;
125 ngx_connection_t *fc;
126 ngx_http_cleanup_t *cln;
127 ngx_http_v2_stream_t *stream;
128 ngx_http_v2_out_frame_t *frame;
129 ngx_http_core_loc_conf_t *clcf;
130 ngx_http_core_srv_conf_t *cscf;
131 struct sockaddr_in *sin;
132 #if (NGX_HAVE_INET6)
133 struct sockaddr_in6 *sin6;
134 #endif
135 u_char addr[NGX_SOCKADDR_STRLEN];
136
137
138 if (!r->stream) {
139 return ngx_http_next_header_filter(r);
140 }
141
142 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
143 "http2 header filter");
144
145 if (r->header_sent) {
146 return NGX_OK;
147 }
148
149 r->header_sent = 1;
150
151 if (r != r->main) {
152 return NGX_OK;
153 }
154
155 if (r->method == NGX_HTTP_HEAD) {
156 r->header_only = 1;
157 }
158
159 switch (r->headers_out.status) {
160
161 case NGX_HTTP_OK:
162 status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_200_INDEX);
163 break;
164
165 case NGX_HTTP_NO_CONTENT:
166 r->header_only = 1;
167
168 ngx_str_null(&r->headers_out.content_type);
169
170 r->headers_out.content_length = NULL;
171 r->headers_out.content_length_n = -1;
172
173 r->headers_out.last_modified_time = -1;
174 r->headers_out.last_modified = NULL;
175
176 status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_204_INDEX);
177 break;
178
179 case NGX_HTTP_PARTIAL_CONTENT:
180 status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_206_INDEX);
181 break;
182
183 case NGX_HTTP_NOT_MODIFIED:
184 r->header_only = 1;
185 status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_304_INDEX);
186 break;
187
188 default:
189 r->headers_out.last_modified_time = -1;
190 r->headers_out.last_modified = NULL;
191
192 switch (r->headers_out.status) {
193
194 case NGX_HTTP_BAD_REQUEST:
195 status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_400_INDEX);
196 break;
197
198 case NGX_HTTP_NOT_FOUND:
199 status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_404_INDEX);
200 break;
201
202 case NGX_HTTP_INTERNAL_SERVER_ERROR:
203 status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_500_INDEX);
204 break;
205
206 default:
207 status = 0;
208 }
209 }
210
211 len = status ? 1 : 1 + ngx_http_v2_literal_size("418");
212
213 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
214
215 if (r->headers_out.server == NULL) {
216 len += 1 + clcf->server_tokens ? ngx_http_v2_literal_size(NGINX_VER)
217 : ngx_http_v2_literal_size("nginx");
218 }
219
220 if (r->headers_out.date == NULL) {
221 len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT");
222 }
223
224 if (r->headers_out.content_type.len) {
225 len += NGX_HTTP_V2_INT_OCTETS + r->headers_out.content_type.len;
226
227 if (r->headers_out.content_type_len == r->headers_out.content_type.len
228 && r->headers_out.charset.len)
229 {
230 len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
231 }
232 }
233
234 if (r->headers_out.content_length == NULL
235 && r->headers_out.content_length_n >= 0)
236 {
237 len += 1 + ngx_http_v2_integer_octets(NGX_OFF_T_LEN) + NGX_OFF_T_LEN;
238 }
239
240 if (r->headers_out.last_modified == NULL
241 && r->headers_out.last_modified_time != -1)
242 {
243 len += 1 + ngx_http_v2_literal_size("Wed, 31 Dec 1986 18:00:00 GMT");
244 }
245
246 fc = r->connection;
247
248 if (r->headers_out.location && r->headers_out.location->value.len) {
249
250 if (r->headers_out.location->value.data[0] == '/') {
251 if (clcf->server_name_in_redirect) {
252 cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
253 host = cscf->server_name;
254
255 } else if (r->headers_in.server.len) {
256 host = r->headers_in.server;
257
258 } else {
259 host.len = NGX_SOCKADDR_STRLEN;
260 host.data = addr;
261
262 if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {
263 return NGX_ERROR;
264 }
265 }
266
267 switch (fc->local_sockaddr->sa_family) {
268
269 #if (NGX_HAVE_INET6)
270 case AF_INET6:
271 sin6 = (struct sockaddr_in6 *) fc->local_sockaddr;
272 port = ntohs(sin6->sin6_port);
273 break;
274 #endif
275 #if (NGX_HAVE_UNIX_DOMAIN)
276 case AF_UNIX:
277 port = 0;
278 break;
279 #endif
280 default: /* AF_INET */
281 sin = (struct sockaddr_in *) fc->local_sockaddr;
282 port = ntohs(sin->sin_port);
283 break;
284 }
285
286 location.len = sizeof("https://") - 1 + host.len
287 + r->headers_out.location->value.len;
288
289 if (clcf->port_in_redirect) {
290
291 #if (NGX_HTTP_SSL)
292 if (fc->ssl)
293 port = (port == 443) ? 0 : port;
294 else
295 #endif
296 port = (port == 80) ? 0 : port;
297
298 } else {
299 port = 0;
300 }
301
302 if (port) {
303 location.len += sizeof(":65535") - 1;
304 }
305
306 location.data = ngx_pnalloc(r->pool, location.len);
307 if (location.data == NULL) {
308 return NGX_ERROR;
309 }
310
311 p = ngx_cpymem(location.data, "http", sizeof("http") - 1);
312
313 #if (NGX_HTTP_SSL)
314 if (fc->ssl) {
315 *p++ = 's';
316 }
317 #endif
318
319 *p++ = ':'; *p++ = '/'; *p++ = '/';
320 p = ngx_cpymem(p, host.data, host.len);
321
322 if (port) {
323 p = ngx_sprintf(p, ":%ui", port);
324 }
325
326 p = ngx_cpymem(p, r->headers_out.location->value.data,
327 r->headers_out.location->value.len);
328
329 /* update r->headers_out.location->value for possible logging */
330
331 r->headers_out.location->value.len = p - location.data;
332 r->headers_out.location->value.data = location.data;
333 ngx_str_set(&r->headers_out.location->key, "Location");
334 }
335
336 r->headers_out.location->hash = 0;
337
338 len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len;
339 }
340
341 #if (NGX_HTTP_GZIP)
342 if (r->gzip_vary) {
343 if (clcf->gzip_vary) {
344 len += 1 + ngx_http_v2_literal_size("Accept-Encoding");
345
346 } else {
347 r->gzip_vary = 0;
348 }
349 }
350 #endif
351
352 part = &r->headers_out.headers.part;
353 header = part->elts;
354
355 for (i = 0; /* void */; i++) {
356
357 if (i >= part->nelts) {
358 if (part->next == NULL) {
359 break;
360 }
361
362 part = part->next;
363 header = part->elts;
364 i = 0;
365 }
366
367 if (header[i].hash == 0) {
368 continue;
369 }
370
371 if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {
372 ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
373 "too long response header name: \"%V\"",
374 &header[i].key);
375 return NGX_ERROR;
376 }
377
378 if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {
379 ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
380 "too long response header value: \"%V: %V\"",
381 &header[i].key, &header[i].value);
382 return NGX_ERROR;
383 }
384
385 len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len
386 + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;
387 }
388
389 stream = r->stream;
390 frame_size = stream->connection->frame_size;
391
392 len += NGX_HTTP_V2_FRAME_HEADER_SIZE
393 * ((len + frame_size - 1) / frame_size);
394
395 b = ngx_create_temp_buf(r->pool, len);
396 if (b == NULL) {
397 return NGX_ERROR;
398 }
399
400 b->last_buf = r->header_only;
401
402 b->last += NGX_HTTP_V2_FRAME_HEADER_SIZE;
403
404 if (status) {
405 *b->last++ = status;
406
407 } else {
408 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);
409 *b->last++ = 3;
410 b->last = ngx_sprintf(b->last, "%03ui", r->headers_out.status);
411 }
412
413 if (r->headers_out.server == NULL) {
414 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);
415
416 if (clcf->server_tokens) {
417 *b->last++ = sizeof(NGINX_VER) - 1;
418 b->last = ngx_cpymem(b->last, NGINX_VER, sizeof(NGINX_VER) - 1);
419
420 } else {
421 *b->last++ = sizeof("nginx") - 1;
422 b->last = ngx_cpymem(b->last, "nginx", sizeof("nginx") - 1);
423 }
424 }
425
426 if (r->headers_out.date == NULL) {
427 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);
428 *b->last++ = (u_char) ngx_cached_http_time.len;
429
430 b->last = ngx_cpymem(b->last, ngx_cached_http_time.data,
431 ngx_cached_http_time.len);
432 }
433
434 if (r->headers_out.content_type.len) {
435 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);
436
437 if (r->headers_out.content_type_len == r->headers_out.content_type.len
438 && r->headers_out.charset.len)
439 {
440 *b->last = 0;
441 b->last = ngx_http_v2_write_int(b->last, ngx_http_v2_prefix(7),
442 r->headers_out.content_type.len
443 + sizeof("; charset=") - 1
444 + r->headers_out.charset.len);
445
446 p = b->last;
447
448 b->last = ngx_cpymem(p, r->headers_out.content_type.data,
449 r->headers_out.content_type.len);
450
451 b->last = ngx_cpymem(b->last, "; charset=",
452 sizeof("; charset=") - 1);
453
454 b->last = ngx_cpymem(b->last, r->headers_out.charset.data,
455 r->headers_out.charset.len);
456
457 /* update r->headers_out.content_type for possible logging */
458
459 r->headers_out.content_type.len = b->last - p;
460 r->headers_out.content_type.data = p;
461
462 } else {
463 *b->last = 0;
464 b->last = ngx_http_v2_write_int(b->last, ngx_http_v2_prefix(7),
465 r->headers_out.content_type.len);
466 b->last = ngx_cpymem(b->last, r->headers_out.content_type.data,
467 r->headers_out.content_type.len);
468 }
469 }
470
471 if (r->headers_out.content_length == NULL
472 && r->headers_out.content_length_n >= 0)
473 {
474 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);
475
476 p = b->last;
477 b->last = ngx_sprintf(b->last + 1, "%O",
478 r->headers_out.content_length_n);
479 *p = (u_char) (b->last - p - 1);
480 }
481
482 if (r->headers_out.last_modified == NULL
483 && r->headers_out.last_modified_time != -1)
484 {
485 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);
486
487 p = b->last;
488 b->last = ngx_http_time(b->last + 1, r->headers_out.last_modified_time);
489 *p = (u_char) (b->last - p - 1);
490 }
491
492 if (r->headers_out.location && r->headers_out.location->value.len) {
493 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);
494
495 *b->last = 0;
496 b->last = ngx_http_v2_write_int(b->last, ngx_http_v2_prefix(7),
497 r->headers_out.location->value.len);
498 b->last = ngx_cpymem(b->last, r->headers_out.location->value.data,
499 r->headers_out.location->value.len);
500 }
501
502 #if (NGX_HTTP_GZIP)
503 if (r->gzip_vary) {
504 *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);
505 *b->last++ = sizeof("Accept-Encoding") - 1;
506 b->last = ngx_cpymem(b->last, "Accept-Encoding",
507 sizeof("Accept-Encoding") - 1);
508 }
509 #endif
510
511 continuation = 0;
512 head = b->pos;
513
514 len = b->last - head - NGX_HTTP_V2_FRAME_HEADER_SIZE;
515 rest = frame_size - len;
516
517 part = &r->headers_out.headers.part;
518 header = part->elts;
519
520 for (i = 0; /* void */; i++) {
521
522 if (i >= part->nelts) {
523 if (part->next == NULL) {
524 break;
525 }
526
527 part = part->next;
528 header = part->elts;
529 i = 0;
530 }
531
532 if (header[i].hash == 0) {
533 continue;
534 }
535
536 len = 1 + NGX_HTTP_V2_INT_OCTETS * 2
537 + header[i].key.len
538 + header[i].value.len;
539
540 if (len > rest) {
541 len = b->last - head - NGX_HTTP_V2_FRAME_HEADER_SIZE;
542
543 if (continuation) {
544 ngx_http_v2_write_continuation_head(head, len,
545 stream->node->id, 0);
546 } else {
547 continuation = 1;
548 ngx_http_v2_write_headers_head(head, len, stream->node->id, 0,
549 r->header_only);
550 }
551
552 rest = frame_size;
553 head = b->last;
554
555 b->last += NGX_HTTP_V2_FRAME_HEADER_SIZE;
556 }
557
558 p = b->last;
559
560 *p++ = 0;
561
562 *p = 0;
563 p = ngx_http_v2_write_int(p, ngx_http_v2_prefix(7), header[i].key.len);
564 ngx_strlow(p, header[i].key.data, header[i].key.len);
565 p += header[i].key.len;
566
567 *p = 0;
568 p = ngx_http_v2_write_int(p, ngx_http_v2_prefix(7),
569 header[i].value.len);
570 p = ngx_cpymem(p, header[i].value.data, header[i].value.len);
571
572 rest -= p - b->last;
573 b->last = p;
574 }
575
576 len = b->last - head - NGX_HTTP_V2_FRAME_HEADER_SIZE;
577
578 if (continuation) {
579 ngx_http_v2_write_continuation_head(head, len, stream->node->id, 1);
580
581 } else {
582 ngx_http_v2_write_headers_head(head, len, stream->node->id, 1,
583 r->header_only);
584 }
585
586 cl = ngx_alloc_chain_link(r->pool);
587 if (cl == NULL) {
588 return NGX_ERROR;
589 }
590
591 cl->buf = b;
592 cl->next = NULL;
593
594 frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));
595 if (frame == NULL) {
596 return NGX_ERROR;
597 }
598
599 frame->first = cl;
600 frame->last = cl;
601 frame->handler = ngx_http_v2_headers_frame_handler;
602 frame->stream = stream;
603 frame->length = b->last - b->pos - NGX_HTTP_V2_FRAME_HEADER_SIZE;
604 frame->blocked = 1;
605 frame->fin = r->header_only;
606
607 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
608 "http2:%ui create HEADERS frame %p: len:%uz",
609 stream->node->id, frame, frame->length);
610
611 ngx_http_v2_queue_blocked_frame(stream->connection, frame);
612
613 cln = ngx_http_cleanup_add(r, 0);
614 if (cln == NULL) {
615 return NGX_ERROR;
616 }
617
618 cln->handler = ngx_http_v2_filter_cleanup;
619 cln->data = stream;
620
621 stream->queued = 1;
622
623 fc->send_chain = ngx_http_v2_send_chain;
624 fc->need_last_buf = 1;
625
626 return ngx_http_v2_filter_send(fc, stream);
627 }
628
629
630 static u_char *
631 ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)
632 {
633 if (value < prefix) {
634 *pos++ |= value;
635 return pos;
636 }
637
638 *pos++ |= prefix;
639 value -= prefix;
640
641 while (value >= 128) {
642 *pos++ = value % 128 + 128;
643 value /= 128;
644 }
645
646 *pos++ = (u_char) value;
647
648 return pos;
649 }
650
651
652 static void
653 ngx_http_v2_write_headers_head(u_char *pos, size_t length, ngx_uint_t sid,
654 ngx_uint_t end_headers, ngx_uint_t end_stream)
655 {
656 u_char flags;
657
658 pos = ngx_http_v2_write_len_and_type(pos, length,
659 NGX_HTTP_V2_HEADERS_FRAME);
660
661 flags = NGX_HTTP_V2_NO_FLAG;
662
663 if (end_headers) {
664 flags |= NGX_HTTP_V2_END_HEADERS_FLAG;
665 }
666
667 if (end_stream) {
668 flags |= NGX_HTTP_V2_END_STREAM_FLAG;
669 }
670
671 *pos++ = flags;
672
673 (void) ngx_http_v2_write_sid(pos, sid);
674 }
675
676
677 static void
678 ngx_http_v2_write_continuation_head(u_char *pos, size_t length, ngx_uint_t sid,
679 ngx_uint_t end_headers)
680 {
681 pos = ngx_http_v2_write_len_and_type(pos, length,
682 NGX_HTTP_V2_CONTINUATION_FRAME);
683
684 *pos++ = end_headers ? NGX_HTTP_V2_END_HEADERS_FLAG : NGX_HTTP_V2_NO_FLAG;
685
686 (void) ngx_http_v2_write_sid(pos, sid);
687 }
688
689
690 static ngx_chain_t *
691 ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
692 {
693 off_t size, offset;
694 size_t rest, frame_size;
695 ngx_chain_t *cl, *out, **ln;
696 ngx_http_request_t *r;
697 ngx_http_v2_stream_t *stream;
698 ngx_http_v2_loc_conf_t *h2lcf;
699 ngx_http_v2_out_frame_t *frame;
700 ngx_http_v2_connection_t *h2c;
701
702 r = fc->data;
703 stream = r->stream;
704
705 #if (NGX_SUPPRESS_WARN)
706 size = 0;
707 #endif
708
709 while (in) {
710 size = ngx_buf_size(in->buf);
711
712 if (size || in->buf->last_buf) {
713 break;
714 }
715
716 in = in->next;
717 }
718
719 if (in == NULL) {
720
721 if (stream->queued) {
722 fc->write->delayed = 1;
723 } else {
724 fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
725 }
726
727 return NULL;
728 }
729
730 h2c = stream->connection;
731
732 if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
733 fc->write->delayed = 1;
734 return in;
735 }
736
737 if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
738 cl = ngx_alloc_chain_link(r->pool);
739 if (cl == NULL) {
740 return NGX_CHAIN_ERROR;
741 }
742
743 cl->buf = in->buf;
744 in->buf = cl->buf->shadow;
745
746 offset = ngx_buf_in_memory(in->buf)
747 ? (cl->buf->pos - in->buf->pos)
748 : (cl->buf->file_pos - in->buf->file_pos);
749
750 cl->next = stream->free_bufs;
751 stream->free_bufs = cl;
752
753 } else {
754 offset = 0;
755 }
756
757 if (limit == 0 || limit > (off_t) h2c->send_window) {
758 limit = h2c->send_window;
759 }
760
761 if (limit > stream->send_window) {
762 limit = (stream->send_window > 0) ? stream->send_window : 0;
763 }
764
765 h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);
766
767 frame_size = (h2lcf->chunk_size < h2c->frame_size)
768 ? h2lcf->chunk_size : h2c->frame_size;
769
770 #if (NGX_SUPPRESS_WARN)
771 cl = NULL;
772 #endif
773
774 for ( ;; ) {
775 if ((off_t) frame_size > limit) {
776 frame_size = (size_t) limit;
777 }
778
779 ln = &out;
780 rest = frame_size;
781
782 while ((off_t) rest >= size) {
783
784 if (offset) {
785 cl = ngx_http_v2_filter_get_shadow(stream, in->buf,
786 offset, size);
787 if (cl == NULL) {
788 return NGX_CHAIN_ERROR;
789 }
790
791 offset = 0;
792
793 } else {
794 cl = ngx_alloc_chain_link(r->pool);
795 if (cl == NULL) {
796 return NGX_CHAIN_ERROR;
797 }
798
799 cl->buf = in->buf;
800 }
801
802 *ln = cl;
803 ln = &cl->next;
804
805 rest -= (size_t) size;
806 in = in->next;
807
808 if (in == NULL) {
809 frame_size -= rest;
810 rest = 0;
811 break;
812 }
813
814 size = ngx_buf_size(in->buf);
815 }
816
817 if (rest) {
818 cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest);
819 if (cl == NULL) {
820 return NGX_CHAIN_ERROR;
821 }
822
823 cl->buf->flush = 0;
824 cl->buf->last_buf = 0;
825
826 *ln = cl;
827
828 offset += rest;
829 size -= rest;
830 }
831
832 frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, cl);
833 if (frame == NULL) {
834 return NGX_CHAIN_ERROR;
835 }
836
837 ngx_http_v2_queue_frame(h2c, frame);
838
839 h2c->send_window -= frame_size;
840
841 stream->send_window -= frame_size;
842 stream->queued++;
843
844 if (in == NULL) {
845 break;
846 }
847
848 limit -= frame_size;
849
850 if (limit == 0) {
851 break;
852 }
853 }
854
855 if (offset) {
856 cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size);
857 if (cl == NULL) {
858 return NGX_CHAIN_ERROR;
859 }
860
861 in->buf = cl->buf;
862 ngx_free_chain(r->pool, cl);
863 }
864
865 if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {
866 return NGX_CHAIN_ERROR;
867 }
868
869 if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {
870 fc->write->delayed = 1;
871 }
872
873 return in;
874 }
875
876
877 static ngx_chain_t *
878 ngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf,
879 off_t offset, off_t size)
880 {
881 ngx_buf_t *chunk;
882 ngx_chain_t *cl;
883
884 cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);
885 if (cl == NULL) {
886 return NULL;
887 }
888
889 chunk = cl->buf;
890
891 ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));
892
893 chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow;
894 chunk->shadow = buf;
895
896 if (ngx_buf_in_memory(chunk)) {
897 chunk->pos += offset;
898 chunk->last = chunk->pos + size;
899 }
900
901 if (chunk->in_file) {
902 chunk->file_pos += offset;
903 chunk->file_last = chunk->file_pos + size;
904 }
905
906 return cl;
907 }
908
909
910 static ngx_http_v2_out_frame_t *
911 ngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream,
912 size_t len, ngx_chain_t *first, ngx_chain_t *last)
913 {
914 u_char flags;
915 ngx_buf_t *buf;
916 ngx_chain_t *cl;
917 ngx_http_v2_out_frame_t *frame;
918
919
920 frame = stream->free_frames;
921
922 if (frame) {
923 stream->free_frames = frame->next;
924
925 } else {
926 frame = ngx_palloc(stream->request->pool,
927 sizeof(ngx_http_v2_out_frame_t));
928 if (frame == NULL) {
929 return NULL;
930 }
931 }
932
933 flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0;
934
935 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,
936 "http2:%ui create DATA frame %p: len:%uz flags:%ui",
937 stream->node->id, frame, len, (ngx_uint_t) flags);
938
939 cl = ngx_chain_get_free_buf(stream->request->pool,
940 &stream->free_data_headers);
941 if (cl == NULL) {
942 return NULL;
943 }
944
945 buf = cl->buf;
946
947 if (!buf->start) {
948 buf->start = ngx_palloc(stream->request->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE);
949 if (buf->start == NULL) {
950 return NULL;
951 }
952
953 buf->end = buf->start + NGX_HTTP_V2_FRAME_HEADER_SIZE;
954 buf->last = buf->end;
955
956 buf->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_data_frame;
957 buf->memory = 1;
958 }
959
960 buf->pos = buf->start;
961 buf->last = buf->pos;
962
963 buf->last = ngx_http_v2_write_len_and_type(buf->last, len,
964 NGX_HTTP_V2_DATA_FRAME);
965 *buf->last++ = flags;
966
967 buf->last = ngx_http_v2_write_sid(buf->last, stream->node->id);
968
969 cl->next = first;
970 first = cl;
971
972 last->buf->flush = 1;
973
974 frame->first = first;
975 frame->last = last;
976 frame->handler = ngx_http_v2_data_frame_handler;
977 frame->stream = stream;
978 frame->length = len;
979 frame->blocked = 0;
980 frame->fin = last->buf->last_buf;
981
982 return frame;
983 }
984
985
986 static ngx_inline ngx_int_t
987 ngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)
988 {
989 stream->blocked = 1;
990
991 if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {
992 fc->error = 1;
993 return NGX_ERROR;
994 }
995
996 stream->blocked = 0;
997
998 if (stream->queued) {
999 fc->buffered |= NGX_HTTP_V2_BUFFERED;
1000 fc->write->delayed = 1;
1001 return NGX_AGAIN;
1002 }
1003
1004 fc->buffered &= ~NGX_HTTP_V2_BUFFERED;
1005
1006 return NGX_OK;
1007 }
1008
1009
1010 static ngx_inline ngx_int_t
1011 ngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c,
1012 ngx_http_v2_stream_t *stream)
1013 {
1014 if (stream->send_window <= 0) {
1015 stream->exhausted = 1;
1016 return NGX_DECLINED;
1017 }
1018
1019 if (h2c->send_window == 0) {
1020 ngx_http_v2_waiting_queue(h2c, stream);
1021 return NGX_DECLINED;
1022 }
1023
1024 return NGX_OK;
1025 }
1026
1027
1028 static void
1029 ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,
1030 ngx_http_v2_stream_t *stream)
1031 {
1032 ngx_queue_t *q;
1033 ngx_http_v2_stream_t *s;
1034
1035 if (stream->handled) {
1036 return;
1037 }
1038
1039 stream->handled = 1;
1040
1041 for (q = ngx_queue_last(&h2c->waiting);
1042 q != ngx_queue_sentinel(&h2c->waiting);
1043 q = ngx_queue_prev(q))
1044 {
1045 s = ngx_queue_data(q, ngx_http_v2_stream_t, queue);
1046
1047 if (s->node->rank < stream->node->rank
1048 || (s->node->rank == stream->node->rank
1049 && s->node->rel_weight >= stream->node->rel_weight))
1050 {
1051 break;
1052 }
1053 }
1054
1055 ngx_queue_insert_after(q, &stream->queue);
1056 }
1057
1058
1059
1060 static ngx_int_t
1061 ngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c,
1062 ngx_http_v2_out_frame_t *frame)
1063 {
1064 ngx_buf_t *buf;
1065 ngx_http_v2_stream_t *stream;
1066
1067 buf = frame->first->buf;
1068
1069 if (buf->pos != buf->last) {
1070 return NGX_AGAIN;
1071 }
1072
1073 stream = frame->stream;
1074
1075 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1076 "http2:%ui HEADERS frame %p was sent",
1077 stream->node->id, frame);
1078
1079 ngx_free_chain(stream->request->pool, frame->first);
1080
1081 ngx_http_v2_handle_frame(stream, frame);
1082
1083 ngx_http_v2_handle_stream(h2c, stream);
1084
1085 return NGX_OK;
1086 }
1087
1088
1089 static ngx_int_t
1090 ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,
1091 ngx_http_v2_out_frame_t *frame)
1092 {
1093 ngx_buf_t *buf;
1094 ngx_chain_t *cl, *ln;
1095 ngx_http_v2_stream_t *stream;
1096
1097 stream = frame->stream;
1098
1099 cl = frame->first;
1100
1101 if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_data_frame) {
1102
1103 if (cl->buf->pos != cl->buf->last) {
1104 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1105 "http2:%ui DATA frame %p was sent partially",
1106 stream->node->id, frame);
1107
1108 return NGX_AGAIN;
1109 }
1110
1111 ln = cl->next;
1112
1113 cl->next = stream->free_data_headers;
1114 stream->free_data_headers = cl;
1115
1116 if (cl == frame->last) {
1117 goto done;
1118 }
1119
1120 cl = ln;
1121 }
1122
1123 for ( ;; ) {
1124 if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1125 buf = cl->buf->shadow;
1126
1127 if (ngx_buf_in_memory(buf)) {
1128 buf->pos = cl->buf->pos;
1129 }
1130
1131 if (buf->in_file) {
1132 buf->file_pos = cl->buf->file_pos;
1133 }
1134 }
1135
1136 if (ngx_buf_size(cl->buf) != 0) {
1137
1138 if (cl != frame->first) {
1139 frame->first = cl;
1140 ngx_http_v2_handle_stream(h2c, stream);
1141 }
1142
1143 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1144 "http2:%ui DATA frame %p was sent partially",
1145 stream->node->id, frame);
1146
1147 return NGX_AGAIN;
1148 }
1149
1150 ln = cl->next;
1151
1152 if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {
1153 cl->next = stream->free_bufs;
1154 stream->free_bufs = cl;
1155
1156 } else {
1157 ngx_free_chain(stream->request->pool, cl);
1158 }
1159
1160 if (cl == frame->last) {
1161 goto done;
1162 }
1163
1164 cl = ln;
1165 }
1166
1167 done:
1168
1169 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
1170 "http2:%ui DATA frame %p was sent",
1171 stream->node->id, frame);
1172
1173 stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE;
1174
1175 ngx_http_v2_handle_frame(stream, frame);
1176
1177 ngx_http_v2_handle_stream(h2c, stream);
1178
1179 return NGX_OK;
1180 }
1181
1182
1183 static ngx_inline void
1184 ngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream,
1185 ngx_http_v2_out_frame_t *frame)
1186 {
1187 ngx_http_request_t *r;
1188
1189 r = stream->request;
1190
1191 r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;
1192
1193 if (frame->fin) {
1194 stream->out_closed = 1;
1195 }
1196
1197 frame->next = stream->free_frames;
1198 stream->free_frames = frame;
1199
1200 stream->queued--;
1201 }
1202
1203
1204 static ngx_inline void
1205 ngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c,
1206 ngx_http_v2_stream_t *stream)
1207 {
1208 ngx_event_t *wev;
1209
1210 if (stream->handled || stream->blocked || stream->exhausted) {
1211 return;
1212 }
1213
1214 wev = stream->request->connection->write;
1215
1216 /*
1217 * This timer can only be set if the stream was delayed because of rate
1218 * limit. In that case the event should be triggered by the timer.
1219 */
1220
1221 if (!wev->timer_set) {
1222 wev->delayed = 0;
1223
1224 stream->handled = 1;
1225 ngx_queue_insert_tail(&h2c->posted, &stream->queue);
1226 }
1227 }
1228
1229
1230 static void
1231 ngx_http_v2_filter_cleanup(void *data)
1232 {
1233 ngx_http_v2_stream_t *stream = data;
1234
1235 size_t window;
1236 ngx_http_v2_out_frame_t *frame, **fn;
1237 ngx_http_v2_connection_t *h2c;
1238
1239 if (stream->handled) {
1240 stream->handled = 0;
1241 ngx_queue_remove(&stream->queue);
1242 }
1243
1244 if (stream->queued == 0) {
1245 return;
1246 }
1247
1248 window = 0;
1249 h2c = stream->connection;
1250 fn = &h2c->last_out;
1251
1252 for ( ;; ) {
1253 frame = *fn;
1254
1255 if (frame == NULL) {
1256 break;
1257 }
1258
1259 if (frame->stream == stream && !frame->blocked) {
1260 *fn = frame->next;
1261
1262 window += frame->length;
1263
1264 if (--stream->queued == 0) {
1265 break;
1266 }
1267
1268 continue;
1269 }
1270
1271 fn = &frame->next;
1272 }
1273
1274 if (h2c->send_window == 0 && window && !ngx_queue_empty(&h2c->waiting)) {
1275 ngx_queue_add(&h2c->posted, &h2c->waiting);
1276 ngx_queue_init(&h2c->waiting);
1277 }
1278
1279 h2c->send_window += window;
1280 }
1281
1282
1283 static ngx_int_t
1284 ngx_http_v2_filter_init(ngx_conf_t *cf)
1285 {
1286 ngx_http_next_header_filter = ngx_http_top_header_filter;
1287 ngx_http_top_header_filter = ngx_http_v2_header_filter;
1288
1289 return NGX_OK;
1290 }