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 }