comparison src/http/v2/ngx_http_v2.c @ 6411:8ec349bb60b2

HTTP/2: always use temporary pool for processing headers. This is required for implementing per request timeouts. Previously, the temporary pool was used only during skipping of headers and the request pool was used otherwise. That required switching of pools if the request was closed while parsing. It wasn't a problem since the request could be closed only after the validation of the fully parsed header. With the per request timeouts, the request can be closed at any moment, and switching of pools in the middle of parsing header name or value becomes a problem. To overcome this, the temporary pool is now always created and used. Special checks are added to keep it when either the stream is being processed or until header block is fully parsed.
author Valentin Bartenev <vbart@nginx.com>
date Wed, 24 Feb 2016 16:05:47 +0300
parents c6ccc1ea9450
children 4ba91a4c66a3
comparison
equal deleted inserted replaced
6410:c6ccc1ea9450 6411:8ec349bb60b2
110 u_char *pos, u_char *end); 110 u_char *pos, u_char *end);
111 static u_char *ngx_http_v2_state_skip_padded(ngx_http_v2_connection_t *h2c, 111 static u_char *ngx_http_v2_state_skip_padded(ngx_http_v2_connection_t *h2c,
112 u_char *pos, u_char *end); 112 u_char *pos, u_char *end);
113 static u_char *ngx_http_v2_state_skip(ngx_http_v2_connection_t *h2c, 113 static u_char *ngx_http_v2_state_skip(ngx_http_v2_connection_t *h2c,
114 u_char *pos, u_char *end); 114 u_char *pos, u_char *end);
115 static u_char *ngx_http_v2_state_skip_headers(ngx_http_v2_connection_t *h2c,
116 u_char *pos, u_char *end);
117 static u_char *ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c, 115 static u_char *ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c,
118 u_char *pos, u_char *end, ngx_http_v2_handler_pt handler); 116 u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);
119 static u_char *ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c, 117 static u_char *ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c,
120 ngx_uint_t err); 118 ngx_uint_t err);
121 119
1131 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR); 1129 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);
1132 } 1130 }
1133 1131
1134 h2c->last_sid = h2c->state.sid; 1132 h2c->last_sid = h2c->state.sid;
1135 1133
1134 h2c->state.pool = ngx_create_pool(1024, h2c->connection->log);
1135 if (h2c->state.pool == NULL) {
1136 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
1137 }
1138
1136 if (depend == h2c->state.sid) { 1139 if (depend == h2c->state.sid) {
1137 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, 1140 ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
1138 "client sent HEADERS frame for stream %ui " 1141 "client sent HEADERS frame for stream %ui "
1139 "with incorrect dependency", h2c->state.sid); 1142 "with incorrect dependency", h2c->state.sid);
1140 1143
1144 { 1147 {
1145 return ngx_http_v2_connection_error(h2c, 1148 return ngx_http_v2_connection_error(h2c,
1146 NGX_HTTP_V2_INTERNAL_ERROR); 1149 NGX_HTTP_V2_INTERNAL_ERROR);
1147 } 1150 }
1148 1151
1149 return ngx_http_v2_state_skip_headers(h2c, pos, end); 1152 return ngx_http_v2_state_header_block(h2c, pos, end);
1150 } 1153 }
1151 1154
1152 h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, 1155 h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,
1153 ngx_http_v2_module); 1156 ngx_http_v2_module);
1154 1157
1164 { 1167 {
1165 return ngx_http_v2_connection_error(h2c, 1168 return ngx_http_v2_connection_error(h2c,
1166 NGX_HTTP_V2_INTERNAL_ERROR); 1169 NGX_HTTP_V2_INTERNAL_ERROR);
1167 } 1170 }
1168 1171
1169 return ngx_http_v2_state_skip_headers(h2c, pos, end); 1172 return ngx_http_v2_state_header_block(h2c, pos, end);
1170 } 1173 }
1171 1174
1172 node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1); 1175 node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1);
1173 1176
1174 if (node == NULL) { 1177 if (node == NULL) {
1183 stream = ngx_http_v2_create_stream(h2c); 1186 stream = ngx_http_v2_create_stream(h2c);
1184 if (stream == NULL) { 1187 if (stream == NULL) {
1185 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); 1188 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
1186 } 1189 }
1187 1190
1191 h2c->state.stream = stream;
1192
1193 stream->pool = h2c->state.pool;
1194 h2c->state.keep_pool = 1;
1195
1188 stream->request->request_length = h2c->state.length; 1196 stream->request->request_length = h2c->state.length;
1189 1197
1190 stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG; 1198 stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;
1191 stream->node = node; 1199 stream->node = node;
1192 1200
1193 node->stream = stream; 1201 node->stream = stream;
1194
1195 h2c->state.stream = stream;
1196 h2c->state.pool = stream->request->pool;
1197 1202
1198 if (priority || node->parent == NULL) { 1203 if (priority || node->parent == NULL) {
1199 node->weight = weight; 1204 node->weight = weight;
1200 ngx_http_v2_set_dependency(h2c, node, depend, excl); 1205 ngx_http_v2_set_dependency(h2c, node, depend, excl);
1201 } 1206 }
1684 return ngx_http_v2_state_header_complete(h2c, pos, end); 1689 return ngx_http_v2_state_header_complete(h2c, pos, end);
1685 1690
1686 error: 1691 error:
1687 1692
1688 h2c->state.stream = NULL; 1693 h2c->state.stream = NULL;
1689 h2c->state.pool = NULL;
1690 1694
1691 return ngx_http_v2_state_header_complete(h2c, pos, end); 1695 return ngx_http_v2_state_header_complete(h2c, pos, end);
1692 } 1696 }
1693 1697
1694 1698
1697 u_char *end) 1701 u_char *end)
1698 { 1702 {
1699 ngx_http_v2_stream_t *stream; 1703 ngx_http_v2_stream_t *stream;
1700 1704
1701 if (h2c->state.length) { 1705 if (h2c->state.length) {
1702 h2c->state.handler = h2c->state.pool ? ngx_http_v2_state_header_block 1706 h2c->state.handler = ngx_http_v2_state_header_block;
1703 : ngx_http_v2_state_skip_headers;
1704 return pos; 1707 return pos;
1705 } 1708 }
1706 1709
1707 if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)) { 1710 if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)) {
1708 return ngx_http_v2_handle_continuation(h2c, pos, end, 1711 return ngx_http_v2_handle_continuation(h2c, pos, end,
1711 1714
1712 stream = h2c->state.stream; 1715 stream = h2c->state.stream;
1713 1716
1714 if (stream) { 1717 if (stream) {
1715 ngx_http_v2_run_request(stream->request); 1718 ngx_http_v2_run_request(stream->request);
1716 1719 }
1717 } else if (h2c->state.pool) { 1720
1721 if (!h2c->state.keep_pool) {
1718 ngx_destroy_pool(h2c->state.pool); 1722 ngx_destroy_pool(h2c->state.pool);
1719 } 1723 }
1720 1724
1721 h2c->state.pool = NULL; 1725 h2c->state.pool = NULL;
1726 h2c->state.keep_pool = 0;
1722 1727
1723 if (h2c->state.padding) { 1728 if (h2c->state.padding) {
1724 return ngx_http_v2_state_skip_padded(h2c, pos, end); 1729 return ngx_http_v2_state_skip_padded(h2c, pos, end);
1725 } 1730 }
1726 1731
2331 2336
2332 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, 2337 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
2333 "http2 frame skip %uz", h2c->state.length); 2338 "http2 frame skip %uz", h2c->state.length);
2334 2339
2335 return ngx_http_v2_state_complete(h2c, pos + h2c->state.length, end); 2340 return ngx_http_v2_state_complete(h2c, pos + h2c->state.length, end);
2336 }
2337
2338
2339 static u_char *
2340 ngx_http_v2_state_skip_headers(ngx_http_v2_connection_t *h2c, u_char *pos,
2341 u_char *end)
2342 {
2343 h2c->state.pool = ngx_create_pool(1024, h2c->connection->log);
2344 if (h2c->state.pool == NULL) {
2345 return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);
2346 }
2347
2348 return ngx_http_v2_state_header_block(h2c, pos, end);
2349 } 2341 }
2350 2342
2351 2343
2352 static u_char * 2344 static u_char *
2353 ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end, 2345 ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end,
3631 3623
3632 3624
3633 void 3625 void
3634 ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc) 3626 ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc)
3635 { 3627 {
3628 ngx_pool_t *pool;
3636 ngx_event_t *ev; 3629 ngx_event_t *ev;
3637 ngx_connection_t *fc; 3630 ngx_connection_t *fc;
3638 ngx_http_v2_node_t *node; 3631 ngx_http_v2_node_t *node;
3639 ngx_http_v2_connection_t *h2c; 3632 ngx_http_v2_connection_t *h2c;
3640 3633
3668 node->stream = NULL; 3661 node->stream = NULL;
3669 3662
3670 ngx_queue_insert_tail(&h2c->closed, &node->reuse); 3663 ngx_queue_insert_tail(&h2c->closed, &node->reuse);
3671 h2c->closed_nodes++; 3664 h2c->closed_nodes++;
3672 3665
3666 /*
3667 * This pool keeps decoded request headers which can be used by log phase
3668 * handlers in ngx_http_free_request().
3669 *
3670 * The pointer is stored into local variable because the stream object
3671 * will be destroyed after a call to ngx_http_free_request().
3672 */
3673 pool = stream->pool;
3674
3673 ngx_http_free_request(stream->request, rc); 3675 ngx_http_free_request(stream->request, rc);
3676
3677 if (pool != h2c->state.pool) {
3678 ngx_destroy_pool(pool);
3679
3680 } else {
3681 /* pool will be destroyed when the complete header is parsed */
3682 h2c->state.keep_pool = 0;
3683 }
3674 3684
3675 ev = fc->read; 3685 ev = fc->read;
3676 3686
3677 if (ev->active || ev->disabled) { 3687 if (ev->active || ev->disabled) {
3678 ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, 3688 ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,
3823 3833
3824 c = h2c->connection; 3834 c = h2c->connection;
3825 3835
3826 if (h2c->state.stream) { 3836 if (h2c->state.stream) {
3827 h2c->state.stream->out_closed = 1; 3837 h2c->state.stream->out_closed = 1;
3828 h2c->state.pool = NULL;
3829 ngx_http_v2_close_stream(h2c->state.stream, NGX_HTTP_BAD_REQUEST); 3838 ngx_http_v2_close_stream(h2c->state.stream, NGX_HTTP_BAD_REQUEST);
3830 } 3839 }
3831 3840
3832 h2c->blocked = 1; 3841 h2c->blocked = 1;
3833 3842