comparison src/http/v2/ngx_http_v2.c @ 7908:0dcec8e5d50a

HTTP/2: reworked body reading to better match HTTP/1.x code. In particular, now the code always uses a buffer limited by client_body_buffer_size. At the cost of an additional copy it ensures that small DATA frames are not directly mapped to small write() syscalls, but rather buffered in memory before writing. Further, requests without Content-Length are no longer forced to use temporary files.
author Maxim Dounin <mdounin@mdounin.ru>
date Sun, 29 Aug 2021 22:20:36 +0300
parents 51f463301f86
children f302c1096f7b
comparison
equal deleted inserted replaced
7907:51f463301f86 7908:0dcec8e5d50a
4030 h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module); 4030 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); 4031 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
4032 4032
4033 len = r->headers_in.content_length_n; 4033 len = r->headers_in.content_length_n;
4034 4034
4035 if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {
4036 len = clcf->client_body_buffer_size;
4037 }
4038
4035 if (r->request_body_no_buffering && !stream->in_closed) { 4039 if (r->request_body_no_buffering && !stream->in_closed) {
4036
4037 if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {
4038 len = clcf->client_body_buffer_size;
4039 }
4040 4040
4041 /* 4041 /*
4042 * We need a room to store data up to the stream's initial window size, 4042 * We need a room to store data up to the stream's initial window size,
4043 * at least until this window will be exhausted. 4043 * at least until this window will be exhausted.
4044 */ 4044 */
4048 } 4048 }
4049 4049
4050 if (len > NGX_HTTP_V2_MAX_WINDOW) { 4050 if (len > NGX_HTTP_V2_MAX_WINDOW) {
4051 len = NGX_HTTP_V2_MAX_WINDOW; 4051 len = NGX_HTTP_V2_MAX_WINDOW;
4052 } 4052 }
4053 4053 }
4054 rb->buf = ngx_create_temp_buf(r->pool, (size_t) len); 4054
4055 4055 rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
4056 } else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size
4057 && !r->request_body_in_file_only)
4058 {
4059 rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);
4060
4061 } else {
4062 rb->buf = ngx_calloc_buf(r->pool);
4063
4064 if (rb->buf != NULL) {
4065 rb->buf->sync = 1;
4066 }
4067 }
4068 4056
4069 if (rb->buf == NULL) { 4057 if (rb->buf == NULL) {
4070 stream->skip_data = 1; 4058 stream->skip_data = 1;
4071 return NGX_HTTP_INTERNAL_SERVER_ERROR; 4059 return NGX_HTTP_INTERNAL_SERVER_ERROR;
4072 } 4060 }
4142 4130
4143 static ngx_int_t 4131 static ngx_int_t
4144 ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos, 4132 ngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos,
4145 size_t size, ngx_uint_t last) 4133 size_t size, ngx_uint_t last)
4146 { 4134 {
4147 ngx_buf_t *buf; 4135 size_t n;
4148 ngx_int_t rc; 4136 ngx_int_t rc;
4149 ngx_connection_t *fc; 4137 ngx_connection_t *fc;
4150 ngx_http_request_body_t *rb; 4138 ngx_http_request_body_t *rb;
4151 ngx_http_core_loc_conf_t *clcf; 4139 ngx_http_core_loc_conf_t *clcf;
4152 4140
4153 fc = r->connection; 4141 fc = r->connection;
4154 rb = r->request_body; 4142 rb = r->request_body;
4155 buf = rb->buf;
4156 4143
4157 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, 4144 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
4158 "http2 process request body"); 4145 "http2 process request body");
4159 4146
4160 if (size) { 4147 if (size == 0 && !last) {
4161 if (buf->sync) { 4148 return NGX_OK;
4162 buf->pos = buf->start = pos; 4149 }
4163 buf->last = buf->end = pos + size; 4150
4164 4151 for ( ;; ) {
4165 r->request_body_in_file_only = 1; 4152 for ( ;; ) {
4166 4153 if (rb->buf->last == rb->buf->end && size) {
4167 } else { 4154
4168 if (size > (size_t) (buf->end - buf->last)) { 4155 if (r->request_body_no_buffering) {
4169 ngx_log_error(NGX_LOG_INFO, fc->log, 0, 4156
4170 "client intended to send body data " 4157 /* should never happen due to flow control */
4171 "larger than declared"); 4158
4172 4159 ngx_log_error(NGX_LOG_ALERT, fc->log, 0,
4173 return NGX_HTTP_BAD_REQUEST; 4160 "no space in http2 body buffer");
4161
4162 return NGX_HTTP_INTERNAL_SERVER_ERROR;
4163 }
4164
4165 /* update chains */
4166
4167 ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,
4168 "http2 body update chains");
4169
4170 rc = ngx_http_v2_filter_request_body(r);
4171
4172 if (rc != NGX_OK) {
4173 return rc;
4174 }
4175
4176 if (rb->busy != NULL) {
4177 ngx_log_error(NGX_LOG_ALERT, fc->log, 0,
4178 "busy buffers after request body flush");
4179 return NGX_HTTP_INTERNAL_SERVER_ERROR;
4180 }
4181
4182 rb->buf->pos = rb->buf->start;
4183 rb->buf->last = rb->buf->start;
4174 } 4184 }
4175 4185
4176 buf->last = ngx_cpymem(buf->last, pos, size); 4186 /* copy body data to the buffer */
4177 } 4187
4178 } 4188 n = rb->buf->end - rb->buf->last;
4179 4189
4180 if (last) { 4190 if (n > size) {
4181 rb->rest = 0; 4191 n = size;
4182 4192 }
4183 if (fc->read->timer_set) { 4193
4184 ngx_del_timer(fc->read); 4194 rb->buf->last = ngx_cpymem(rb->buf->last, pos, n);
4185 } 4195
4186 4196 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
4187 if (r->request_body_no_buffering) { 4197 "http2 request body recv %uz", n);
4188 ngx_post_event(fc->read, &ngx_posted_events); 4198
4199 pos += n;
4200 size -= n;
4201
4202 if (size == 0 && last) {
4203 rb->rest = 0;
4204 }
4205
4206 if (r->request_body_no_buffering) {
4207 break;
4208 }
4209
4210 /* pass buffer to request body filter chain */
4211
4212 rc = ngx_http_v2_filter_request_body(r);
4213
4214 if (rc != NGX_OK) {
4215 return rc;
4216 }
4217
4218 if (rb->rest == 0) {
4219 break;
4220 }
4221
4222 if (size == 0) {
4223 break;
4224 }
4225 }
4226
4227 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
4228 "http2 request body rest %O", rb->rest);
4229
4230 if (rb->rest == 0) {
4231 break;
4232 }
4233
4234 if (size == 0) {
4235 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
4236 ngx_add_timer(fc->read, clcf->client_body_timeout);
4237
4238 if (r->request_body_no_buffering) {
4239 ngx_post_event(fc->read, &ngx_posted_events);
4240 return NGX_OK;
4241 }
4242
4189 return NGX_OK; 4243 return NGX_OK;
4190 } 4244 }
4191 4245 }
4192 rc = ngx_http_v2_filter_request_body(r); 4246
4193 4247 if (fc->read->timer_set) {
4194 if (rc != NGX_OK) { 4248 ngx_del_timer(fc->read);
4195 return rc; 4249 }
4196 }
4197
4198 if (buf->sync) {
4199 /* prevent reusing this buffer in the upstream module */
4200 rb->buf = NULL;
4201 }
4202
4203 if (r->headers_in.chunked) {
4204 r->headers_in.content_length_n = rb->received;
4205 }
4206
4207 r->read_event_handler = ngx_http_block_reading;
4208 rb->post_handler(r);
4209
4210 return NGX_OK;
4211 }
4212
4213 if (size == 0) {
4214 return NGX_OK;
4215 }
4216
4217 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
4218 ngx_add_timer(fc->read, clcf->client_body_timeout);
4219 4250
4220 if (r->request_body_no_buffering) { 4251 if (r->request_body_no_buffering) {
4221 ngx_post_event(fc->read, &ngx_posted_events); 4252 ngx_post_event(fc->read, &ngx_posted_events);
4222 return NGX_OK; 4253 return NGX_OK;
4223 } 4254 }
4224 4255
4225 if (buf->sync) { 4256 if (r->headers_in.chunked) {
4226 return ngx_http_v2_filter_request_body(r); 4257 r->headers_in.content_length_n = rb->received;
4227 } 4258 }
4259
4260 r->read_event_handler = ngx_http_block_reading;
4261 rb->post_handler(r);
4228 4262
4229 return NGX_OK; 4263 return NGX_OK;
4230 } 4264 }
4231 4265
4232 4266