comparison src/http/v2/ngx_http_v2.c @ 7201:641306096f5b

HTTP/2: server push. Resources to be pushed are configured with the "http2_push" directive. Also, preload links from the Link response headers, as described in https://www.w3.org/TR/preload/#server-push-http-2, can be pushed, if enabled with the "http2_push_preload" directive. Only relative URIs with absolute paths can be pushed. The number of concurrent pushes is normally limited by a client, but cannot exceed a hard limit set by the "http2_max_concurrent_pushes" directive.
author Ruslan Ermilov <ru@nginx.com>
date Thu, 08 Feb 2018 09:55:03 +0300
parents cadb43014c7c
children e44c297a6b95
comparison
equal deleted inserted replaced
7200:cadb43014c7c 7201:641306096f5b
33 #define NGX_HTTP_V2_PRIORITY_SIZE 5 33 #define NGX_HTTP_V2_PRIORITY_SIZE 5
34 #define NGX_HTTP_V2_PING_SIZE 8 34 #define NGX_HTTP_V2_PING_SIZE 8
35 #define NGX_HTTP_V2_GOAWAY_SIZE 8 35 #define NGX_HTTP_V2_GOAWAY_SIZE 8
36 #define NGX_HTTP_V2_WINDOW_UPDATE_SIZE 4 36 #define NGX_HTTP_V2_WINDOW_UPDATE_SIZE 4
37 37
38 #define NGX_HTTP_V2_STREAM_ID_SIZE 4
39
40 #define NGX_HTTP_V2_SETTINGS_PARAM_SIZE 6 38 #define NGX_HTTP_V2_SETTINGS_PARAM_SIZE 6
41 39
42 /* settings fields */ 40 /* settings fields */
43 #define NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING 0x1 41 #define NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING 0x1
42 #define NGX_HTTP_V2_ENABLE_PUSH_SETTING 0x2
44 #define NGX_HTTP_V2_MAX_STREAMS_SETTING 0x3 43 #define NGX_HTTP_V2_MAX_STREAMS_SETTING 0x3
45 #define NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING 0x4 44 #define NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING 0x4
46 #define NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING 0x5 45 #define NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING 0x5
47 46
48 #define NGX_HTTP_V2_FRAME_BUFFER_SIZE 24 47 #define NGX_HTTP_V2_FRAME_BUFFER_SIZE 24
119 118
120 static ngx_int_t ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c, 119 static ngx_int_t ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c,
121 u_char **pos, u_char *end, ngx_uint_t prefix); 120 u_char **pos, u_char *end, ngx_uint_t prefix);
122 121
123 static ngx_http_v2_stream_t *ngx_http_v2_create_stream( 122 static ngx_http_v2_stream_t *ngx_http_v2_create_stream(
124 ngx_http_v2_connection_t *h2c); 123 ngx_http_v2_connection_t *h2c, ngx_uint_t push);
125 static ngx_http_v2_node_t *ngx_http_v2_get_node_by_id( 124 static ngx_http_v2_node_t *ngx_http_v2_get_node_by_id(
126 ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t alloc); 125 ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t alloc);
127 static ngx_http_v2_node_t *ngx_http_v2_get_closed_node( 126 static ngx_http_v2_node_t *ngx_http_v2_get_closed_node(
128 ngx_http_v2_connection_t *h2c); 127 ngx_http_v2_connection_t *h2c);
129 #define ngx_http_v2_index_size(h2scf) (h2scf->streams_index_mask + 1) 128 #define ngx_http_v2_index_size(h2scf) (h2scf->streams_index_mask + 1)
160 static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r); 159 static ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r);
161 static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r, 160 static ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r,
162 ngx_http_v2_header_t *header); 161 ngx_http_v2_header_t *header);
163 static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r); 162 static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r);
164 static void ngx_http_v2_run_request(ngx_http_request_t *r); 163 static void ngx_http_v2_run_request(ngx_http_request_t *r);
164 static void ngx_http_v2_run_request_handler(ngx_event_t *ev);
165 static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r, 165 static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r,
166 u_char *pos, size_t size, ngx_uint_t last); 166 u_char *pos, size_t size, ngx_uint_t last);
167 static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r); 167 static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r);
168 static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r); 168 static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r);
169 169
247 247
248 h2c->table_update = 1; 248 h2c->table_update = 1;
249 249
250 h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module); 250 h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);
251 251
252 h2c->concurrent_pushes = h2scf->concurrent_pushes;
253
252 h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log); 254 h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log);
253 if (h2c->pool == NULL) { 255 if (h2c->pool == NULL) {
254 ngx_http_close_connection(c); 256 ngx_http_close_connection(c);
255 return; 257 return;
256 } 258 }
364 366
365 if (n == NGX_AGAIN) { 367 if (n == NGX_AGAIN) {
366 break; 368 break;
367 } 369 }
368 370
369 if (n == 0 && (h2c->state.incomplete || h2c->processing)) { 371 if (n == 0
372 && (h2c->state.incomplete || h2c->processing || h2c->pushing))
373 {
370 ngx_log_error(NGX_LOG_INFO, c->log, 0, 374 ngx_log_error(NGX_LOG_INFO, c->log, 0,
371 "client prematurely closed connection"); 375 "client prematurely closed connection");
372 } 376 }
373 377
374 if (n == 0 || n == NGX_ERROR) { 378 if (n == 0 || n == NGX_ERROR) {
403 return; 407 return;
404 } 408 }
405 409
406 h2c->blocked = 0; 410 h2c->blocked = 0;
407 411
408 if (h2c->processing) { 412 if (h2c->processing || h2c->pushing) {
409 if (rev->timer_set) { 413 if (rev->timer_set) {
410 ngx_del_timer(rev); 414 ngx_del_timer(rev);
411 } 415 }
412 416
413 return; 417 return;
587 { 591 {
588 ngx_int_t rc; 592 ngx_int_t rc;
589 ngx_connection_t *c; 593 ngx_connection_t *c;
590 ngx_http_v2_srv_conf_t *h2scf; 594 ngx_http_v2_srv_conf_t *h2scf;
591 595
592 if (h2c->last_out || h2c->processing) { 596 if (h2c->last_out || h2c->processing || h2c->pushing) {
593 return; 597 return;
594 } 598 }
595 599
596 c = h2c->connection; 600 c = h2c->connection;
597 601
1121 if (node->parent) { 1125 if (node->parent) {
1122 ngx_queue_remove(&node->reuse); 1126 ngx_queue_remove(&node->reuse);
1123 h2c->closed_nodes--; 1127 h2c->closed_nodes--;
1124 } 1128 }
1125 1129
1126 stream = ngx_http_v2_create_stream(h2c); 1130 stream = ngx_http_v2_create_stream(h2c, 0);
1127 if (stream == NULL) { 1131 if (stream == NULL) {
1128 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); 1132 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
1129 } 1133 }
1130 1134
1131 h2c->state.stream = stream; 1135 h2c->state.stream = stream;
1907 case NGX_HTTP_V2_CANCEL: 1911 case NGX_HTTP_V2_CANCEL:
1908 ngx_log_error(NGX_LOG_INFO, fc->log, 0, 1912 ngx_log_error(NGX_LOG_INFO, fc->log, 0,
1909 "client canceled stream %ui", h2c->state.sid); 1913 "client canceled stream %ui", h2c->state.sid);
1910 break; 1914 break;
1911 1915
1916 case NGX_HTTP_V2_REFUSED_STREAM:
1917 ngx_log_error(NGX_LOG_INFO, fc->log, 0,
1918 "client refused stream %ui", h2c->state.sid);
1919 break;
1920
1912 case NGX_HTTP_V2_INTERNAL_ERROR: 1921 case NGX_HTTP_V2_INTERNAL_ERROR:
1913 ngx_log_error(NGX_LOG_INFO, fc->log, 0, 1922 ngx_log_error(NGX_LOG_INFO, fc->log, 0,
1914 "client terminated stream %ui due to internal error", 1923 "client terminated stream %ui due to internal error",
1915 h2c->state.sid); 1924 h2c->state.sid);
1916 break; 1925 break;
1964 ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos, 1973 ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos,
1965 u_char *end) 1974 u_char *end)
1966 { 1975 {
1967 ssize_t window_delta; 1976 ssize_t window_delta;
1968 ngx_uint_t id, value; 1977 ngx_uint_t id, value;
1978 ngx_http_v2_srv_conf_t *h2scf;
1969 ngx_http_v2_out_frame_t *frame; 1979 ngx_http_v2_out_frame_t *frame;
1970 1980
1971 window_delta = 0; 1981 window_delta = 0;
1972 1982
1973 while (h2c->state.length) { 1983 while (h2c->state.length) {
2012 return ngx_http_v2_connection_error(h2c, 2022 return ngx_http_v2_connection_error(h2c,
2013 NGX_HTTP_V2_PROTOCOL_ERROR); 2023 NGX_HTTP_V2_PROTOCOL_ERROR);
2014 } 2024 }
2015 2025
2016 h2c->frame_size = value; 2026 h2c->frame_size = value;
2027 break;
2028
2029 case NGX_HTTP_V2_ENABLE_PUSH_SETTING:
2030
2031 if (value > 1) {
2032 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
2033 "client sent SETTINGS frame with incorrect "
2034 "ENABLE_PUSH value %ui", value);
2035
2036 return ngx_http_v2_connection_error(h2c,
2037 NGX_HTTP_V2_PROTOCOL_ERROR);
2038 }
2039
2040 h2c->push_disabled = !value;
2041 break;
2042
2043 case NGX_HTTP_V2_MAX_STREAMS_SETTING:
2044 h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
2045 ngx_http_v2_module);
2046
2047 h2c->concurrent_pushes = ngx_min(value, h2scf->concurrent_pushes);
2017 break; 2048 break;
2018 2049
2019 default: 2050 default:
2020 break; 2051 break;
2021 } 2052 }
2481 2512
2482 return NGX_AGAIN; 2513 return NGX_AGAIN;
2483 } 2514 }
2484 2515
2485 2516
2517 ngx_int_t
2518 ngx_http_v2_push_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t depend,
2519 size_t request_length, ngx_str_t *path, ngx_str_t *authority)
2520 {
2521 ngx_int_t rc;
2522 ngx_str_t value;
2523 ngx_connection_t *fc;
2524 ngx_http_request_t *r;
2525 ngx_http_v2_node_t *node;
2526 ngx_http_v2_stream_t *stream;
2527
2528 node = ngx_http_v2_get_node_by_id(h2c, h2c->last_push, 1);
2529
2530 if (node == NULL) {
2531 return NGX_ERROR;
2532 }
2533
2534 if (node->parent) {
2535 ngx_queue_remove(&node->reuse);
2536 h2c->closed_nodes--;
2537 }
2538
2539 stream = ngx_http_v2_create_stream(h2c, 1);
2540 if (stream == NULL) {
2541 return NGX_ERROR;
2542 }
2543
2544 stream->pool = ngx_create_pool(1024, h2c->connection->log);
2545 if (stream->pool == NULL) {
2546 return NGX_ERROR;
2547 }
2548
2549 r = stream->request;
2550 fc = r->connection;
2551
2552 r->request_length = request_length;
2553
2554 stream->in_closed = 1;
2555 stream->node = node;
2556
2557 node->stream = stream;
2558
2559 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
2560 "http2 push stream sid:%ui "
2561 "depends on %ui excl:0 weight:16",
2562 h2c->last_push, depend);
2563
2564 node->weight = NGX_HTTP_V2_DEFAULT_WEIGHT;
2565 ngx_http_v2_set_dependency(h2c, node, depend, 0);
2566
2567 r->method_name = ngx_http_core_get_method;
2568 r->method = NGX_HTTP_GET;
2569
2570 r->schema_start = (u_char *) "https";
2571
2572 #if (NGX_HTTP_SSL)
2573 if (fc->ssl) {
2574 r->schema_end = r->schema_start + 5;
2575
2576 } else
2577 #endif
2578 {
2579 r->schema_end = r->schema_start + 4;
2580 }
2581
2582 value.len = authority->len;
2583
2584 value.data = ngx_pstrdup(stream->pool, authority);
2585 if (value.data == NULL) {
2586 return NGX_ERROR;
2587 }
2588
2589 rc = ngx_http_v2_parse_authority(r, &value);
2590
2591 if (rc != NGX_OK) {
2592 goto error;
2593 }
2594
2595 value.len = path->len;
2596
2597 value.data = ngx_pstrdup(stream->pool, path);
2598 if (value.data == NULL) {
2599 return NGX_ERROR;
2600 }
2601
2602 rc = ngx_http_v2_parse_path(r, &value);
2603
2604 if (rc != NGX_OK) {
2605 goto error;
2606 }
2607
2608 fc->write->handler = ngx_http_v2_run_request_handler;
2609 ngx_post_event(fc->write, &ngx_posted_events);
2610
2611 return NGX_OK;
2612
2613 error:
2614
2615 if (rc == NGX_ABORT) {
2616 return NGX_ERROR;
2617 }
2618
2619 if (rc == NGX_DECLINED) {
2620 ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
2621 return NGX_ERROR;
2622 }
2623
2624 (void) ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
2625
2626 return NGX_ERROR;
2627 }
2628
2629
2486 static ngx_int_t 2630 static ngx_int_t
2487 ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c) 2631 ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c)
2488 { 2632 {
2489 size_t len; 2633 size_t len;
2490 ngx_buf_t *buf; 2634 ngx_buf_t *buf;
2741 return NGX_OK; 2885 return NGX_OK;
2742 } 2886 }
2743 2887
2744 2888
2745 static ngx_http_v2_stream_t * 2889 static ngx_http_v2_stream_t *
2746 ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c) 2890 ngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t push)
2747 { 2891 {
2748 ngx_log_t *log; 2892 ngx_log_t *log;
2749 ngx_event_t *rev, *wev; 2893 ngx_event_t *rev, *wev;
2750 ngx_connection_t *fc; 2894 ngx_connection_t *fc;
2751 ngx_http_log_ctx_t *ctx; 2895 ngx_http_log_ctx_t *ctx;
2796 } 2940 }
2797 2941
2798 ngx_memcpy(log, h2c->connection->log, sizeof(ngx_log_t)); 2942 ngx_memcpy(log, h2c->connection->log, sizeof(ngx_log_t));
2799 2943
2800 log->data = ctx; 2944 log->data = ctx;
2801 log->action = "reading client request headers"; 2945
2946 if (push) {
2947 log->action = "processing pushed request headers";
2948
2949 } else {
2950 log->action = "reading client request headers";
2951 }
2802 2952
2803 ngx_memzero(rev, sizeof(ngx_event_t)); 2953 ngx_memzero(rev, sizeof(ngx_event_t));
2804 2954
2805 rev->data = fc; 2955 rev->data = fc;
2806 rev->ready = 1; 2956 rev->ready = 1;
2868 h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); 3018 h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);
2869 3019
2870 stream->send_window = h2c->init_window; 3020 stream->send_window = h2c->init_window;
2871 stream->recv_window = h2scf->preread_size; 3021 stream->recv_window = h2scf->preread_size;
2872 3022
2873 h2c->processing++; 3023 if (push) {
3024 h2c->pushing++;
3025
3026 } else {
3027 h2c->processing++;
3028 }
2874 3029
2875 return stream; 3030 return stream;
2876 } 3031 }
2877 3032
2878 3033
3530 3685
3531 ngx_http_process_request(r); 3686 ngx_http_process_request(r);
3532 } 3687 }
3533 3688
3534 3689
3690 static void
3691 ngx_http_v2_run_request_handler(ngx_event_t *ev)
3692 {
3693 ngx_connection_t *fc;
3694 ngx_http_request_t *r;
3695
3696 fc = ev->data;
3697 r = fc->data;
3698
3699 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
3700 "http2 run request handler");
3701
3702 ngx_http_v2_run_request(r);
3703 }
3704
3705
3535 ngx_int_t 3706 ngx_int_t
3536 ngx_http_v2_read_request_body(ngx_http_request_t *r) 3707 ngx_http_v2_read_request_body(ngx_http_request_t *r)
3537 { 3708 {
3538 off_t len; 3709 off_t len;
3539 size_t size; 3710 size_t size;
4001 4172
4002 void 4173 void
4003 ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc) 4174 ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc)
4004 { 4175 {
4005 ngx_pool_t *pool; 4176 ngx_pool_t *pool;
4177 ngx_uint_t push;
4006 ngx_event_t *ev; 4178 ngx_event_t *ev;
4007 ngx_connection_t *fc; 4179 ngx_connection_t *fc;
4008 ngx_http_v2_node_t *node; 4180 ngx_http_v2_node_t *node;
4009 ngx_http_v2_connection_t *h2c; 4181 ngx_http_v2_connection_t *h2c;
4010 4182
4011 h2c = stream->connection; 4183 h2c = stream->connection;
4012 node = stream->node; 4184 node = stream->node;
4013 4185
4014 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, 4186 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
4015 "http2 close stream %ui, queued %ui, processing %ui", 4187 "http2 close stream %ui, queued %ui, "
4016 node->id, stream->queued, h2c->processing); 4188 "processing %ui, pushing %ui",
4189 node->id, stream->queued, h2c->processing, h2c->pushing);
4017 4190
4018 fc = stream->request->connection; 4191 fc = stream->request->connection;
4019 4192
4020 if (stream->queued) { 4193 if (stream->queued) {
4021 fc->write->handler = ngx_http_v2_close_stream_handler; 4194 fc->write->handler = ngx_http_v2_close_stream_handler;
4067 4240
4068 if (h2c->state.stream == stream) { 4241 if (h2c->state.stream == stream) {
4069 h2c->state.stream = NULL; 4242 h2c->state.stream = NULL;
4070 } 4243 }
4071 4244
4245 push = stream->node->id % 2 == 0;
4246
4072 node->stream = NULL; 4247 node->stream = NULL;
4073 4248
4074 ngx_queue_insert_tail(&h2c->closed, &node->reuse); 4249 ngx_queue_insert_tail(&h2c->closed, &node->reuse);
4075 h2c->closed_nodes++; 4250 h2c->closed_nodes++;
4076 4251
4114 } 4289 }
4115 4290
4116 fc->data = h2c->free_fake_connections; 4291 fc->data = h2c->free_fake_connections;
4117 h2c->free_fake_connections = fc; 4292 h2c->free_fake_connections = fc;
4118 4293
4119 h2c->processing--; 4294 if (push) {
4120 4295 h2c->pushing--;
4121 if (h2c->processing || h2c->blocked) { 4296
4297 } else {
4298 h2c->processing--;
4299 }
4300
4301 if (h2c->processing || h2c->pushing || h2c->blocked) {
4122 return; 4302 return;
4123 } 4303 }
4124 4304
4125 ev = h2c->connection->read; 4305 ev = h2c->connection->read;
4126 4306
4265 } 4445 }
4266 } 4446 }
4267 4447
4268 c->error = 1; 4448 c->error = 1;
4269 4449
4270 if (!h2c->processing) { 4450 if (!h2c->processing && !h2c->pushing) {
4271 ngx_http_close_connection(c); 4451 ngx_http_close_connection(c);
4272 return; 4452 return;
4273 } 4453 }
4274 4454
4275 c->read->handler = ngx_http_empty_handler; 4455 c->read->handler = ngx_http_empty_handler;
4314 } 4494 }
4315 } 4495 }
4316 4496
4317 h2c->blocked = 0; 4497 h2c->blocked = 0;
4318 4498
4319 if (h2c->processing) { 4499 if (h2c->processing || h2c->pushing) {
4320 return; 4500 return;
4321 } 4501 }
4322 4502
4323 ngx_http_close_connection(c); 4503 ngx_http_close_connection(c);
4324 } 4504 }