Mercurial > hg > nginx
comparison src/http/v2/ngx_http_v2.c @ 7914:9cf043a5d9ca
Request body: reading body buffering in filters.
If a filter wants to buffer the request body during reading (for
example, to check an external scanner), it can now do so. To make
it possible, the code now checks rb->last_saved (introduced in the
previous change) along with rb->rest == 0.
Since in HTTP/2 this requires flow control to avoid overflowing the
request body buffer, so filters which need buffering have to set
the rb->filter_need_buffering flag on the first filter call. (Note
that each filter is expected to call the next filter, so all filters
will be able set the flag if needed.)
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Sun, 29 Aug 2021 22:22:02 +0300 |
parents | 1d78437dbc3f |
children | 29795b697e14 |
comparison
equal
deleted
inserted
replaced
7913:185c86b830ef | 7914:9cf043a5d9ca |
---|---|
171 ngx_http_v2_header_t *header); | 171 ngx_http_v2_header_t *header); |
172 static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r); | 172 static ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r); |
173 static void ngx_http_v2_run_request(ngx_http_request_t *r); | 173 static void ngx_http_v2_run_request(ngx_http_request_t *r); |
174 static void ngx_http_v2_run_request_handler(ngx_event_t *ev); | 174 static void ngx_http_v2_run_request_handler(ngx_event_t *ev); |
175 static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r, | 175 static ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r, |
176 u_char *pos, size_t size, ngx_uint_t last); | 176 u_char *pos, size_t size, ngx_uint_t last, ngx_uint_t flush); |
177 static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r); | 177 static ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r); |
178 static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r); | 178 static void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r); |
179 | 179 |
180 static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c, | 180 static ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c, |
181 ngx_http_v2_stream_t *stream, ngx_uint_t status); | 181 ngx_http_v2_stream_t *stream, ngx_uint_t status); |
1090 | 1090 |
1091 static u_char * | 1091 static u_char * |
1092 ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, | 1092 ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos, |
1093 u_char *end) | 1093 u_char *end) |
1094 { | 1094 { |
1095 size_t size; | 1095 size_t size, window; |
1096 ngx_buf_t *buf; | 1096 ngx_buf_t *buf; |
1097 ngx_int_t rc; | 1097 ngx_int_t rc; |
1098 ngx_connection_t *fc; | 1098 ngx_connection_t *fc; |
1099 ngx_http_request_t *r; | 1099 ngx_http_request_t *r; |
1100 ngx_http_v2_stream_t *stream; | 1100 ngx_http_v2_stream_t *stream; |
1138 } | 1138 } |
1139 | 1139 |
1140 h2c->payload_bytes += size; | 1140 h2c->payload_bytes += size; |
1141 | 1141 |
1142 if (r->request_body) { | 1142 if (r->request_body) { |
1143 rc = ngx_http_v2_process_request_body(r, pos, size, stream->in_closed); | 1143 rc = ngx_http_v2_process_request_body(r, pos, size, |
1144 | 1144 stream->in_closed, 0); |
1145 if (rc != NGX_OK) { | 1145 |
1146 if (rc != NGX_OK && rc != NGX_AGAIN) { | |
1146 stream->skip_data = 1; | 1147 stream->skip_data = 1; |
1147 ngx_http_finalize_request(r, rc); | 1148 ngx_http_finalize_request(r, rc); |
1149 } | |
1150 | |
1151 if (rc == NGX_AGAIN && !stream->no_flow_control) { | |
1152 buf = r->request_body->buf; | |
1153 window = buf->end - buf->last; | |
1154 | |
1155 window -= h2c->state.length - size; | |
1156 | |
1157 if (window < stream->recv_window) { | |
1158 ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0, | |
1159 "http2 negative window update"); | |
1160 return ngx_http_v2_connection_error(h2c, | |
1161 NGX_HTTP_V2_INTERNAL_ERROR); | |
1162 } | |
1163 | |
1164 if (window > stream->recv_window) { | |
1165 if (ngx_http_v2_send_window_update(h2c, stream->node->id, | |
1166 window - stream->recv_window) | |
1167 == NGX_ERROR) | |
1168 { | |
1169 return ngx_http_v2_connection_error(h2c, | |
1170 NGX_HTTP_V2_INTERNAL_ERROR); | |
1171 } | |
1172 | |
1173 stream->recv_window = window; | |
1174 } | |
1148 } | 1175 } |
1149 | 1176 |
1150 ngx_http_run_posted_requests(fc); | 1177 ngx_http_run_posted_requests(fc); |
1151 | 1178 |
1152 } else if (size) { | 1179 } else if (size) { |
4025 r->request_body_no_buffering = 0; | 4052 r->request_body_no_buffering = 0; |
4026 rb->post_handler(r); | 4053 rb->post_handler(r); |
4027 return NGX_OK; | 4054 return NGX_OK; |
4028 } | 4055 } |
4029 | 4056 |
4057 rb->rest = 1; | |
4058 | |
4059 /* set rb->filter_need_buffering */ | |
4060 | |
4061 rc = ngx_http_top_request_body_filter(r, NULL); | |
4062 | |
4063 if (rc != NGX_OK) { | |
4064 stream->skip_data = 1; | |
4065 return rc; | |
4066 } | |
4067 | |
4030 h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); | 4068 h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); |
4031 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | 4069 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
4032 | 4070 |
4033 len = r->headers_in.content_length_n; | 4071 len = r->headers_in.content_length_n; |
4034 | 4072 |
4037 | 4075 |
4038 } else { | 4076 } else { |
4039 len++; | 4077 len++; |
4040 } | 4078 } |
4041 | 4079 |
4042 if (r->request_body_no_buffering) { | 4080 if (r->request_body_no_buffering || rb->filter_need_buffering) { |
4043 | 4081 |
4044 /* | 4082 /* |
4045 * We need a room to store data up to the stream's initial window size, | 4083 * We need a room to store data up to the stream's initial window size, |
4046 * at least until this window will be exhausted. | 4084 * at least until this window will be exhausted. |
4047 */ | 4085 */ |
4060 if (rb->buf == NULL) { | 4098 if (rb->buf == NULL) { |
4061 stream->skip_data = 1; | 4099 stream->skip_data = 1; |
4062 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 4100 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
4063 } | 4101 } |
4064 | 4102 |
4065 rb->rest = 1; | |
4066 | |
4067 buf = stream->preread; | 4103 buf = stream->preread; |
4068 | 4104 |
4069 if (stream->in_closed) { | 4105 if (stream->in_closed) { |
4070 r->request_body_no_buffering = 0; | 4106 if (!rb->filter_need_buffering) { |
4107 r->request_body_no_buffering = 0; | |
4108 } | |
4071 | 4109 |
4072 if (buf) { | 4110 if (buf) { |
4073 rc = ngx_http_v2_process_request_body(r, buf->pos, | 4111 rc = ngx_http_v2_process_request_body(r, buf->pos, |
4074 buf->last - buf->pos, 1); | 4112 buf->last - buf->pos, 1, 0); |
4075 ngx_pfree(r->pool, buf->start); | 4113 ngx_pfree(r->pool, buf->start); |
4114 | |
4115 } else { | |
4116 rc = ngx_http_v2_process_request_body(r, NULL, 0, 1, 0); | |
4117 } | |
4118 | |
4119 if (rc != NGX_AGAIN) { | |
4076 return rc; | 4120 return rc; |
4077 } | 4121 } |
4078 | 4122 |
4079 return ngx_http_v2_process_request_body(r, NULL, 0, 1); | 4123 r->read_event_handler = ngx_http_v2_read_client_request_body_handler; |
4124 r->write_event_handler = ngx_http_request_empty_handler; | |
4125 | |
4126 return NGX_AGAIN; | |
4080 } | 4127 } |
4081 | 4128 |
4082 if (buf) { | 4129 if (buf) { |
4083 rc = ngx_http_v2_process_request_body(r, buf->pos, | 4130 rc = ngx_http_v2_process_request_body(r, buf->pos, |
4084 buf->last - buf->pos, 0); | 4131 buf->last - buf->pos, 0, 0); |
4085 | 4132 |
4086 ngx_pfree(r->pool, buf->start); | 4133 ngx_pfree(r->pool, buf->start); |
4087 | 4134 |
4088 if (rc != NGX_OK) { | 4135 if (rc != NGX_OK && rc != NGX_AGAIN) { |
4089 stream->skip_data = 1; | 4136 stream->skip_data = 1; |
4090 return rc; | 4137 return rc; |
4091 } | 4138 } |
4092 } | 4139 } |
4093 | 4140 |
4094 if (r->request_body_no_buffering) { | 4141 if (r->request_body_no_buffering || rb->filter_need_buffering) { |
4095 size = (size_t) len - h2scf->preread_size; | 4142 size = (size_t) len - h2scf->preread_size; |
4096 | 4143 |
4097 } else { | 4144 } else { |
4098 stream->no_flow_control = 1; | 4145 stream->no_flow_control = 1; |
4099 size = NGX_HTTP_V2_MAX_WINDOW - stream->recv_window; | 4146 size = NGX_HTTP_V2_MAX_WINDOW - stream->recv_window; |
4131 } | 4178 } |
4132 | 4179 |
4133 | 4180 |
4134 static ngx_int_t | 4181 static ngx_int_t |
4135 ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, | 4182 ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, |
4136 size_t size, ngx_uint_t last) | 4183 size_t size, ngx_uint_t last, ngx_uint_t flush) |
4137 { | 4184 { |
4138 size_t n; | 4185 size_t n; |
4139 ngx_int_t rc; | 4186 ngx_int_t rc; |
4140 ngx_connection_t *fc; | 4187 ngx_connection_t *fc; |
4141 ngx_http_request_body_t *rb; | 4188 ngx_http_request_body_t *rb; |
4145 rb = r->request_body; | 4192 rb = r->request_body; |
4146 | 4193 |
4147 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, | 4194 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, |
4148 "http2 process request body"); | 4195 "http2 process request body"); |
4149 | 4196 |
4150 if (size == 0 && !last) { | 4197 if (size == 0 && !last && !flush) { |
4151 return NGX_OK; | 4198 return NGX_AGAIN; |
4152 } | 4199 } |
4153 | 4200 |
4154 for ( ;; ) { | 4201 for ( ;; ) { |
4155 for ( ;; ) { | 4202 for ( ;; ) { |
4156 if (rb->buf->last == rb->buf->end && size) { | 4203 if (rb->buf->last == rb->buf->end && size) { |
4228 } | 4275 } |
4229 | 4276 |
4230 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, | 4277 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0, |
4231 "http2 request body rest %O", rb->rest); | 4278 "http2 request body rest %O", rb->rest); |
4232 | 4279 |
4233 if (rb->rest == 0) { | 4280 if (rb->rest == 0 && rb->last_saved) { |
4234 break; | 4281 break; |
4235 } | 4282 } |
4236 | 4283 |
4237 if (size == 0) { | 4284 if (size == 0) { |
4238 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); | 4285 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); |
4239 ngx_add_timer(fc->read, clcf->client_body_timeout); | 4286 ngx_add_timer(fc->read, clcf->client_body_timeout); |
4240 | 4287 |
4241 if (r->request_body_no_buffering) { | 4288 if (r->request_body_no_buffering) { |
4242 ngx_post_event(fc->read, &ngx_posted_events); | 4289 ngx_post_event(fc->read, &ngx_posted_events); |
4243 return NGX_OK; | 4290 return NGX_AGAIN; |
4244 } | 4291 } |
4245 | 4292 |
4246 return NGX_OK; | 4293 return NGX_AGAIN; |
4247 } | 4294 } |
4248 } | 4295 } |
4249 | 4296 |
4250 if (fc->read->timer_set) { | 4297 if (fc->read->timer_set) { |
4251 ngx_del_timer(fc->read); | 4298 ngx_del_timer(fc->read); |
4277 ngx_http_core_loc_conf_t *clcf; | 4324 ngx_http_core_loc_conf_t *clcf; |
4278 | 4325 |
4279 rb = r->request_body; | 4326 rb = r->request_body; |
4280 buf = rb->buf; | 4327 buf = rb->buf; |
4281 | 4328 |
4282 if (buf->pos == buf->last && rb->rest) { | 4329 if (buf->pos == buf->last && (rb->rest || rb->last_sent)) { |
4283 cl = NULL; | 4330 cl = NULL; |
4284 goto update; | 4331 goto update; |
4285 } | 4332 } |
4286 | 4333 |
4287 cl = ngx_chain_get_free_buf(r->pool, &rb->free); | 4334 cl = ngx_chain_get_free_buf(r->pool, &rb->free); |
4340 | 4387 |
4341 return NGX_HTTP_BAD_REQUEST; | 4388 return NGX_HTTP_BAD_REQUEST; |
4342 } | 4389 } |
4343 | 4390 |
4344 b->last_buf = 1; | 4391 b->last_buf = 1; |
4392 rb->last_sent = 1; | |
4345 } | 4393 } |
4346 | 4394 |
4347 b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body; | 4395 b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body; |
4348 b->flush = r->request_body_no_buffering; | 4396 b->flush = r->request_body_no_buffering; |
4349 | 4397 |
4359 | 4407 |
4360 | 4408 |
4361 static void | 4409 static void |
4362 ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r) | 4410 ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r) |
4363 { | 4411 { |
4364 ngx_connection_t *fc; | 4412 size_t window; |
4413 ngx_buf_t *buf; | |
4414 ngx_int_t rc; | |
4415 ngx_connection_t *fc; | |
4416 ngx_http_v2_stream_t *stream; | |
4417 ngx_http_v2_connection_t *h2c; | |
4365 | 4418 |
4366 fc = r->connection; | 4419 fc = r->connection; |
4367 | 4420 |
4368 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, | 4421 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, |
4369 "http2 read client request body handler"); | 4422 "http2 read client request body handler"); |
4383 "client prematurely closed stream"); | 4436 "client prematurely closed stream"); |
4384 | 4437 |
4385 r->stream->skip_data = 1; | 4438 r->stream->skip_data = 1; |
4386 | 4439 |
4387 ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST); | 4440 ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST); |
4441 return; | |
4442 } | |
4443 | |
4444 rc = ngx_http_v2_process_request_body(r, NULL, 0, r->stream->in_closed, 1); | |
4445 | |
4446 if (rc != NGX_OK && rc != NGX_AGAIN) { | |
4447 r->stream->skip_data = 1; | |
4448 ngx_http_finalize_request(r, rc); | |
4449 return; | |
4450 } | |
4451 | |
4452 if (rc == NGX_OK) { | |
4453 return; | |
4454 } | |
4455 | |
4456 if (r->request_body->rest == 0) { | |
4457 return; | |
4458 } | |
4459 | |
4460 stream = r->stream; | |
4461 h2c = stream->connection; | |
4462 | |
4463 buf = r->request_body->buf; | |
4464 window = buf->end - buf->start; | |
4465 | |
4466 if (h2c->state.stream == stream) { | |
4467 window -= h2c->state.length; | |
4468 } | |
4469 | |
4470 if (window <= stream->recv_window) { | |
4471 if (window < stream->recv_window) { | |
4472 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, | |
4473 "http2 negative window update"); | |
4474 | |
4475 stream->skip_data = 1; | |
4476 | |
4477 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
4478 return; | |
4479 } | |
4480 | |
4481 return; | |
4482 } | |
4483 | |
4484 if (ngx_http_v2_send_window_update(h2c, stream->node->id, | |
4485 window - stream->recv_window) | |
4486 == NGX_ERROR) | |
4487 { | |
4488 stream->skip_data = 1; | |
4489 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
4490 return; | |
4491 } | |
4492 | |
4493 stream->recv_window = window; | |
4494 | |
4495 if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) { | |
4496 stream->skip_data = 1; | |
4497 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); | |
4388 return; | 4498 return; |
4389 } | 4499 } |
4390 } | 4500 } |
4391 | 4501 |
4392 | 4502 |
4428 if (rc != NGX_OK) { | 4538 if (rc != NGX_OK) { |
4429 stream->skip_data = 1; | 4539 stream->skip_data = 1; |
4430 return rc; | 4540 return rc; |
4431 } | 4541 } |
4432 | 4542 |
4433 if (!r->request_body->rest) { | 4543 if (r->request_body->rest == 0 && r->request_body->last_saved) { |
4434 return NGX_OK; | 4544 return NGX_OK; |
4545 } | |
4546 | |
4547 if (r->request_body->rest == 0) { | |
4548 return NGX_AGAIN; | |
4435 } | 4549 } |
4436 | 4550 |
4437 if (r->request_body->busy != NULL) { | 4551 if (r->request_body->busy != NULL) { |
4438 return NGX_AGAIN; | 4552 return NGX_AGAIN; |
4439 } | 4553 } |