blob: 2c612311d0a65b6e3fab06d2c21822edf98b6247 [file] [log] [blame] [raw]
Igor Sysoev7d66b1f2004-10-05 15:39:18 +00001
2/*
3 * Copyright (C) Igor Sysoev
NGINX team01f0bd52012-01-30 14:53:52 +00004 * Copyright (C) Nginx, Inc.
Igor Sysoev7d66b1f2004-10-05 15:39:18 +00005 */
6
7
8#include <ngx_config.h>
9#include <ngx_core.h>
Igor Sysoev7d66b1f2004-10-05 15:39:18 +000010#include <ngx_http.h>
11
12
Igor Sysoev4b4c1f62005-05-12 14:59:46 +000013static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
14static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
nginxa79aeb72012-11-27 14:26:09 +000015static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
Igor Sysoev55c04d12007-08-15 12:44:43 +000016static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
nginxa79aeb72012-11-27 14:26:09 +000017static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,
18 ngx_buf_t *b);
Igor Sysoeva1ff67e2009-01-19 14:03:40 +000019static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);
Igor Sysoev9fdefb12006-04-14 09:34:54 +000020
nginxa79aeb72012-11-27 14:26:09 +000021static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,
22 ngx_chain_t *in);
23static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,
24 ngx_chain_t *in);
25static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,
26 ngx_chain_t *in);
27static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r,
28 ngx_chain_t *in);
Igor Sysoev7d66b1f2004-10-05 15:39:18 +000029
30
Igor Sysoevc70429e2005-03-19 12:35:58 +000031ngx_int_t
32ngx_http_read_client_request_body(ngx_http_request_t *r,
33 ngx_http_client_body_handler_pt post_handler)
Igor Sysoev7d66b1f2004-10-05 15:39:18 +000034{
Igor Sysoevfdd76482006-07-07 16:31:14 +000035 size_t preread;
36 ssize_t size;
nginxa79aeb72012-11-27 14:26:09 +000037 ngx_int_t rc;
nginxbf317882013-02-05 14:21:59 +000038 ngx_buf_t *b;
39 ngx_chain_t out, *cl;
Igor Sysoevea4ca082005-01-18 13:04:59 +000040 ngx_http_request_body_t *rb;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +000041 ngx_http_core_loc_conf_t *clcf;
Igor Sysoev4b4c1f62005-05-12 14:59:46 +000042
Igor Sysoev802fd202009-08-28 13:21:12 +000043 r->main->count++;
44
nginx7f609092013-03-26 13:27:30 +000045#if (NGX_HTTP_SPDY)
46 if (r->spdy_stream) {
47 rc = ngx_http_spdy_read_request_body(r, post_handler);
48 goto done;
49 }
50#endif
51
nginx73a0d762013-04-16 14:10:50 +000052 if (r != r->main || r->request_body || r->discard_body) {
Igor Sysoev4b4c1f62005-05-12 14:59:46 +000053 post_handler(r);
54 return NGX_OK;
Igor Sysoeva1ff67e2009-01-19 14:03:40 +000055 }
56
57 if (ngx_http_test_expect(r) != NGX_OK) {
nginxa79aeb72012-11-27 14:26:09 +000058 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
59 goto done;
Igor Sysoev4b4c1f62005-05-12 14:59:46 +000060 }
Igor Sysoevea4ca082005-01-18 13:04:59 +000061
Igor Sysoevc70429e2005-03-19 12:35:58 +000062 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
63 if (rb == NULL) {
nginxa79aeb72012-11-27 14:26:09 +000064 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
65 goto done;
Igor Sysoevea4ca082005-01-18 13:04:59 +000066 }
Igor Sysoevea4ca082005-01-18 13:04:59 +000067
68 /*
69 * set by ngx_pcalloc():
70 *
71 * rb->bufs = NULL;
72 * rb->buf = NULL;
nginxa79aeb72012-11-27 14:26:09 +000073 * rb->free = NULL;
74 * rb->busy = NULL;
75 * rb->chunked = NULL;
Igor Sysoevea4ca082005-01-18 13:04:59 +000076 */
nginxa79aeb72012-11-27 14:26:09 +000077
78 rb->rest = -1;
79 rb->post_handler = post_handler;
80
81 r->request_body = rb;
82
83 if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
84 post_handler(r);
85 return NGX_OK;
86 }
Igor Sysoev7d66b1f2004-10-05 15:39:18 +000087
Igor Sysoev9fdefb12006-04-14 09:34:54 +000088 preread = r->header_in->last - r->header_in->pos;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +000089
Igor Sysoev9fdefb12006-04-14 09:34:54 +000090 if (preread) {
Igor Sysoev7d66b1f2004-10-05 15:39:18 +000091
92 /* there is the pre-read part of the request body */
Igor Sysoev9fdefb12006-04-14 09:34:54 +000093
94 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
95 "http client request body preread %uz", preread);
Igor Sysoev7d66b1f2004-10-05 15:39:18 +000096
nginxa79aeb72012-11-27 14:26:09 +000097 out.buf = r->header_in;
98 out.next = NULL;
99
100 rc = ngx_http_request_body_filter(r, &out);
101
102 if (rc != NGX_OK) {
103 goto done;
Igor Sysoevea4ca082005-01-18 13:04:59 +0000104 }
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000105
nginxa79aeb72012-11-27 14:26:09 +0000106 r->request_length += preread - (r->header_in->last - r->header_in->pos);
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000107
nginxa79aeb72012-11-27 14:26:09 +0000108 if (!r->headers_in.chunked
109 && rb->rest > 0
110 && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
111 {
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000112 /* the whole request body may be placed in r->header_in */
Igor Sysoev6c8b6b32007-01-19 16:14:25 +0000113
nginx7f609092013-03-26 13:27:30 +0000114 b = ngx_calloc_buf(r->pool);
115 if (b == NULL) {
116 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
117 goto done;
118 }
119
120 b->temporary = 1;
121 b->start = r->header_in->pos;
122 b->pos = r->header_in->pos;
123 b->last = r->header_in->last;
124 b->end = r->header_in->end;
125
126 rb->buf = b;
127
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000128 r->read_event_handler = ngx_http_read_client_request_body_handler;
nginxa79aeb72012-11-27 14:26:09 +0000129 r->write_event_handler = ngx_http_request_empty_handler;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000130
nginxa79aeb72012-11-27 14:26:09 +0000131 rc = ngx_http_do_read_client_request_body(r);
132 goto done;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000133 }
134
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000135 } else {
nginxa79aeb72012-11-27 14:26:09 +0000136 /* set rb->rest */
137
138 if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
139 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
140 goto done;
141 }
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000142 }
nginxa79aeb72012-11-27 14:26:09 +0000143
144 if (rb->rest == 0) {
145 /* the whole request body was pre-read */
146
147 if (r->request_body_in_file_only) {
148 if (ngx_http_write_request_body(r) != NGX_OK) {
149 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
150 goto done;
151 }
nginxbf317882013-02-05 14:21:59 +0000152
153 cl = ngx_chain_get_free_buf(r->pool, &rb->free);
154 if (cl == NULL) {
155 return NGX_HTTP_INTERNAL_SERVER_ERROR;
156 }
157
158 b = cl->buf;
159
160 ngx_memzero(b, sizeof(ngx_buf_t));
161
162 b->in_file = 1;
163 b->file_last = rb->temp_file->file.offset;
164 b->file = &rb->temp_file->file;
165
166 rb->bufs = cl;
nginxa79aeb72012-11-27 14:26:09 +0000167 }
168
169 post_handler(r);
170
171 return NGX_OK;
172 }
173
174 if (rb->rest < 0) {
175 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
176 "negative request body rest");
177 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
178 goto done;
179 }
180
181 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000182
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000183 size = clcf->client_body_buffer_size;
184 size += size >> 2;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000185
nginxa79aeb72012-11-27 14:26:09 +0000186 /* TODO: honor r->request_body_in_single_buf */
187
188 if (!r->headers_in.chunked && rb->rest < size) {
Igor Sysoevfdd76482006-07-07 16:31:14 +0000189 size = (ssize_t) rb->rest;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000190
191 if (r->request_body_in_single_buf) {
192 size += preread;
193 }
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000194
195 } else {
196 size = clcf->client_body_buffer_size;
197 }
198
Igor Sysoevc70429e2005-03-19 12:35:58 +0000199 rb->buf = ngx_create_temp_buf(r->pool, size);
200 if (rb->buf == NULL) {
nginxa79aeb72012-11-27 14:26:09 +0000201 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
202 goto done;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000203 }
204
Igor Sysoev4b4c1f62005-05-12 14:59:46 +0000205 r->read_event_handler = ngx_http_read_client_request_body_handler;
nginxa79aeb72012-11-27 14:26:09 +0000206 r->write_event_handler = ngx_http_request_empty_handler;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000207
nginxa79aeb72012-11-27 14:26:09 +0000208 rc = ngx_http_do_read_client_request_body(r);
209
210done:
211
212 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
213 r->main->count--;
214 }
215
216 return rc;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000217}
218
219
Igor Sysoevc70429e2005-03-19 12:35:58 +0000220static void
Igor Sysoev4b4c1f62005-05-12 14:59:46 +0000221ngx_http_read_client_request_body_handler(ngx_http_request_t *r)
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000222{
Igor Sysoev4b4c1f62005-05-12 14:59:46 +0000223 ngx_int_t rc;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000224
Igor Sysoev4b4c1f62005-05-12 14:59:46 +0000225 if (r->connection->read->timedout) {
Igor Sysoevfb0f94f2005-11-15 12:26:51 +0000226 r->connection->timedout = 1;
Igor Sysoev50138442007-01-25 16:35:14 +0000227 ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000228 return;
229 }
230
Igor Sysoev4b4c1f62005-05-12 14:59:46 +0000231 rc = ngx_http_do_read_client_request_body(r);
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000232
233 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
Igor Sysoev50138442007-01-25 16:35:14 +0000234 ngx_http_finalize_request(r, rc);
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000235 }
236}
237
238
Igor Sysoevc70429e2005-03-19 12:35:58 +0000239static ngx_int_t
Igor Sysoev4b4c1f62005-05-12 14:59:46 +0000240ngx_http_do_read_client_request_body(ngx_http_request_t *r)
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000241{
nginxa79aeb72012-11-27 14:26:09 +0000242 off_t rest;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000243 size_t size;
244 ssize_t n;
nginxa79aeb72012-11-27 14:26:09 +0000245 ngx_int_t rc;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000246 ngx_buf_t *b;
nginxa79aeb72012-11-27 14:26:09 +0000247 ngx_chain_t *cl, out;
Igor Sysoev4b4c1f62005-05-12 14:59:46 +0000248 ngx_connection_t *c;
Igor Sysoevea4ca082005-01-18 13:04:59 +0000249 ngx_http_request_body_t *rb;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000250 ngx_http_core_loc_conf_t *clcf;
251
Igor Sysoev4b4c1f62005-05-12 14:59:46 +0000252 c = r->connection;
Igor Sysoevea4ca082005-01-18 13:04:59 +0000253 rb = r->request_body;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000254
255 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
256 "http read client request body");
257
258 for ( ;; ) {
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000259 for ( ;; ) {
260 if (rb->buf->last == rb->buf->end) {
Igor Sysoevea4ca082005-01-18 13:04:59 +0000261
nginxa79aeb72012-11-27 14:26:09 +0000262 /* pass buffer to request body filter chain */
263
264 out.buf = rb->buf;
265 out.next = NULL;
266
267 rc = ngx_http_request_body_filter(r, &out);
268
269 if (rc != NGX_OK) {
270 return rc;
271 }
272
273 /* write to file */
274
275 if (ngx_http_write_request_body(r) != NGX_OK) {
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000276 return NGX_HTTP_INTERNAL_SERVER_ERROR;
277 }
278
nginxa79aeb72012-11-27 14:26:09 +0000279 /* update chains */
280
281 rc = ngx_http_request_body_filter(r, NULL);
282
283 if (rc != NGX_OK) {
284 return rc;
285 }
286
287 if (rb->busy != NULL) {
288 return NGX_HTTP_INTERNAL_SERVER_ERROR;
289 }
290
291 rb->buf->pos = rb->buf->start;
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000292 rb->buf->last = rb->buf->start;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000293 }
294
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000295 size = rb->buf->end - rb->buf->last;
nginxa79aeb72012-11-27 14:26:09 +0000296 rest = rb->rest - (rb->buf->last - rb->buf->pos);
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000297
nginxa79aeb72012-11-27 14:26:09 +0000298 if ((off_t) size > rest) {
299 size = (size_t) rest;
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000300 }
301
302 n = c->recv(c, rb->buf->last, size);
303
304 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
305 "http client request body recv %z", n);
306
307 if (n == NGX_AGAIN) {
308 break;
309 }
310
311 if (n == 0) {
312 ngx_log_error(NGX_LOG_INFO, c->log, 0,
NGINX teamceea69f2012-04-23 13:32:31 +0000313 "client prematurely closed connection");
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000314 }
315
316 if (n == 0 || n == NGX_ERROR) {
317 c->error = 1;
318 return NGX_HTTP_BAD_REQUEST;
319 }
320
321 rb->buf->last += n;
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000322 r->request_length += n;
nginxa79aeb72012-11-27 14:26:09 +0000323
324 if (n == rest) {
325 /* pass buffer to request body filter chain */
326
327 out.buf = rb->buf;
328 out.next = NULL;
329
330 rc = ngx_http_request_body_filter(r, &out);
331
332 if (rc != NGX_OK) {
333 return rc;
334 }
335 }
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000336
337 if (rb->rest == 0) {
338 break;
339 }
340
341 if (rb->buf->last < rb->buf->end) {
342 break;
343 }
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000344 }
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000345
346 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
Igor Sysoev0125d542007-02-19 13:26:03 +0000347 "http client request body rest %O", rb->rest);
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000348
Igor Sysoevea4ca082005-01-18 13:04:59 +0000349 if (rb->rest == 0) {
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000350 break;
351 }
352
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000353 if (!c->read->ready) {
354 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
355 ngx_add_timer(c->read, clcf->client_body_timeout);
356
Igor Sysoevaccf3742008-12-15 11:30:58 +0000357 if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
Igor Sysoeve0cae6b2006-11-15 20:02:20 +0000358 return NGX_HTTP_INTERNAL_SERVER_ERROR;
359 }
360
361 return NGX_AGAIN;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000362 }
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000363 }
364
Igor Sysoev9f37f032005-11-09 17:27:29 +0000365 if (c->read->timer_set) {
366 ngx_del_timer(c->read);
367 }
368
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000369 if (rb->temp_file || r->request_body_in_file_only) {
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000370
371 /* save the last part */
Igor Sysoevea4ca082005-01-18 13:04:59 +0000372
nginxa79aeb72012-11-27 14:26:09 +0000373 if (ngx_http_write_request_body(r) != NGX_OK) {
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000374 return NGX_HTTP_INTERNAL_SERVER_ERROR;
375 }
376
nginxa79aeb72012-11-27 14:26:09 +0000377 cl = ngx_chain_get_free_buf(r->pool, &rb->free);
378 if (cl == NULL) {
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000379 return NGX_HTTP_INTERNAL_SERVER_ERROR;
380 }
nginxa79aeb72012-11-27 14:26:09 +0000381
382 b = cl->buf;
383
384 ngx_memzero(b, sizeof(ngx_buf_t));
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000385
386 b->in_file = 1;
Igor Sysoevea4ca082005-01-18 13:04:59 +0000387 b->file_last = rb->temp_file->file.offset;
388 b->file = &rb->temp_file->file;
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000389
nginxa79aeb72012-11-27 14:26:09 +0000390 rb->bufs = cl;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000391 }
392
Igor Sysoev3eed28f2011-09-05 13:18:16 +0000393 r->read_event_handler = ngx_http_block_reading;
394
Igor Sysoevea4ca082005-01-18 13:04:59 +0000395 rb->post_handler(r);
Igor Sysoev7d66b1f2004-10-05 15:39:18 +0000396
397 return NGX_OK;
398}
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000399
400
401static ngx_int_t
nginxa79aeb72012-11-27 14:26:09 +0000402ngx_http_write_request_body(ngx_http_request_t *r)
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000403{
404 ssize_t n;
nginxa79aeb72012-11-27 14:26:09 +0000405 ngx_chain_t *cl;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000406 ngx_temp_file_t *tf;
407 ngx_http_request_body_t *rb;
408 ngx_http_core_loc_conf_t *clcf;
409
410 rb = r->request_body;
nginxa79aeb72012-11-27 14:26:09 +0000411
412 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
413 "http write client request body, bufs %p", rb->bufs);
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000414
415 if (rb->temp_file == NULL) {
416 tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
417 if (tf == NULL) {
418 return NGX_ERROR;
419 }
420
421 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
422
423 tf->file.fd = NGX_INVALID_FILE;
424 tf->file.log = r->connection->log;
425 tf->path = clcf->client_body_temp_path;
426 tf->pool = r->pool;
427 tf->warn = "a client request body is buffered to a temporary file";
Igor Sysoev89fe1622006-04-19 15:24:22 +0000428 tf->log_level = r->request_body_file_log_level;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000429 tf->persistent = r->request_body_in_persistent_file;
Igor Sysoev50138442007-01-25 16:35:14 +0000430 tf->clean = r->request_body_in_clean_file;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000431
432 if (r->request_body_file_group_access) {
Igor Sysoev6c8b6b32007-01-19 16:14:25 +0000433 tf->access = 0660;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000434 }
435
436 rb->temp_file = tf;
nginxa79aeb72012-11-27 14:26:09 +0000437
438 if (rb->bufs == NULL) {
439 /* empty body with r->request_body_in_file_only */
440
441 if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
442 tf->persistent, tf->clean, tf->access)
443 != NGX_OK)
444 {
445 return NGX_ERROR;
446 }
447
448 return NGX_OK;
449 }
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000450 }
451
nginxa79aeb72012-11-27 14:26:09 +0000452 if (rb->bufs == NULL) {
453 return NGX_OK;
454 }
455
456 n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000457
458 /* TODO: n == 0 or not complete and level event */
459
460 if (n == NGX_ERROR) {
461 return NGX_ERROR;
462 }
463
464 rb->temp_file->offset += n;
465
nginxa79aeb72012-11-27 14:26:09 +0000466 /* mark all buffers as written */
467
468 for (cl = rb->bufs; cl; cl = cl->next) {
469 cl->buf->pos = cl->buf->last;
470 }
471
472 rb->bufs = NULL;
473
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000474 return NGX_OK;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000475}
476
477
478ngx_int_t
Igor Sysoev55c04d12007-08-15 12:44:43 +0000479ngx_http_discard_request_body(ngx_http_request_t *r)
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000480{
481 ssize_t size;
nginxa79aeb72012-11-27 14:26:09 +0000482 ngx_int_t rc;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000483 ngx_event_t *rev;
nginx7f609092013-03-26 13:27:30 +0000484
485#if (NGX_HTTP_SPDY)
486 if (r->spdy_stream && r == r->main) {
487 r->spdy_stream->skip_data = NGX_SPDY_DATA_DISCARD;
488 return NGX_OK;
489 }
490#endif
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000491
nginxa79aeb72012-11-27 14:26:09 +0000492 if (r != r->main || r->discard_body || r->request_body) {
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000493 return NGX_OK;
494 }
495
Igor Sysoeva1ff67e2009-01-19 14:03:40 +0000496 if (ngx_http_test_expect(r) != NGX_OK) {
497 return NGX_HTTP_INTERNAL_SERVER_ERROR;
498 }
499
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000500 rev = r->connection->read;
501
502 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");
503
504 if (rev->timer_set) {
505 ngx_del_timer(rev);
506 }
507
nginxa79aeb72012-11-27 14:26:09 +0000508 if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000509 return NGX_OK;
510 }
Igor Sysoev55c04d12007-08-15 12:44:43 +0000511
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000512 size = r->header_in->last - r->header_in->pos;
513
nginxa79aeb72012-11-27 14:26:09 +0000514 if (size || r->headers_in.chunked) {
515 rc = ngx_http_discard_request_body_filter(r, r->header_in);
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000516
nginxa79aeb72012-11-27 14:26:09 +0000517 if (rc != NGX_OK) {
518 return rc;
519 }
520
521 if (r->headers_in.content_length_n == 0) {
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000522 return NGX_OK;
523 }
524 }
nginxa79aeb72012-11-27 14:26:09 +0000525
526 rc = ngx_http_read_discarded_request_body(r);
527
528 if (rc == NGX_OK) {
529 r->lingering_close = 0;
530 return NGX_OK;
531 }
532
533 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
534 return rc;
535 }
536
537 /* rc == NGX_AGAIN */
Igor Sysoev43335b32007-09-03 10:30:19 +0000538
Igor Sysoevef4b4d72009-10-26 14:09:32 +0000539 r->read_event_handler = ngx_http_discarded_request_body_handler;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000540
Igor Sysoevaccf3742008-12-15 11:30:58 +0000541 if (ngx_handle_read_event(rev, 0) != NGX_OK) {
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000542 return NGX_HTTP_INTERNAL_SERVER_ERROR;
543 }
544
nginxa79aeb72012-11-27 14:26:09 +0000545 r->count++;
546 r->discard_body = 1;
Igor Sysoev55c04d12007-08-15 12:44:43 +0000547
548 return NGX_OK;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000549}
550
551
Igor Sysoevef4b4d72009-10-26 14:09:32 +0000552void
553ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000554{
Igor Sysoev55c04d12007-08-15 12:44:43 +0000555 ngx_int_t rc;
556 ngx_msec_t timer;
557 ngx_event_t *rev;
558 ngx_connection_t *c;
559 ngx_http_core_loc_conf_t *clcf;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000560
Igor Sysoev55c04d12007-08-15 12:44:43 +0000561 c = r->connection;
562 rev = c->read;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000563
Igor Sysoev55c04d12007-08-15 12:44:43 +0000564 if (rev->timedout) {
565 c->timedout = 1;
566 c->error = 1;
Igor Sysoevef4b4d72009-10-26 14:09:32 +0000567 ngx_http_finalize_request(r, NGX_ERROR);
Igor Sysoev55c04d12007-08-15 12:44:43 +0000568 return;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000569 }
570
Igor Sysoev55c04d12007-08-15 12:44:43 +0000571 if (r->lingering_time) {
Igor Sysoevaa639b22007-11-15 15:05:24 +0000572 timer = (ngx_msec_t) (r->lingering_time - ngx_time());
Igor Sysoev55c04d12007-08-15 12:44:43 +0000573
574 if (timer <= 0) {
575 r->discard_body = 0;
Igor Sysoev0183c2f2009-10-06 12:44:57 +0000576 r->lingering_close = 0;
Igor Sysoevef4b4d72009-10-26 14:09:32 +0000577 ngx_http_finalize_request(r, NGX_ERROR);
Igor Sysoev55c04d12007-08-15 12:44:43 +0000578 return;
579 }
580
581 } else {
582 timer = 0;
583 }
584
585 rc = ngx_http_read_discarded_request_body(r);
586
587 if (rc == NGX_OK) {
Igor Sysoev55c04d12007-08-15 12:44:43 +0000588 r->discard_body = 0;
Igor Sysoev0183c2f2009-10-06 12:44:57 +0000589 r->lingering_close = 0;
Igor Sysoevef4b4d72009-10-26 14:09:32 +0000590 ngx_http_finalize_request(r, NGX_DONE);
Igor Sysoev55c04d12007-08-15 12:44:43 +0000591 return;
592 }
593
nginxa79aeb72012-11-27 14:26:09 +0000594 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
595 c->error = 1;
596 ngx_http_finalize_request(r, NGX_ERROR);
597 return;
598 }
599
Igor Sysoev55c04d12007-08-15 12:44:43 +0000600 /* rc == NGX_AGAIN */
601
Igor Sysoevaccf3742008-12-15 11:30:58 +0000602 if (ngx_handle_read_event(rev, 0) != NGX_OK) {
Igor Sysoev43335b32007-09-03 10:30:19 +0000603 c->error = 1;
Igor Sysoevef4b4d72009-10-26 14:09:32 +0000604 ngx_http_finalize_request(r, NGX_ERROR);
Igor Sysoev55c04d12007-08-15 12:44:43 +0000605 return;
606 }
607
608 if (timer) {
609
610 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
611
612 timer *= 1000;
613
614 if (timer > clcf->lingering_timeout) {
615 timer = clcf->lingering_timeout;
616 }
617
618 ngx_add_timer(rev, timer);
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000619 }
620}
621
622
623static ngx_int_t
Igor Sysoev55c04d12007-08-15 12:44:43 +0000624ngx_http_read_discarded_request_body(ngx_http_request_t *r)
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000625{
nginxa79aeb72012-11-27 14:26:09 +0000626 size_t size;
627 ssize_t n;
628 ngx_int_t rc;
629 ngx_buf_t b;
630 u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000631
632 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
633 "http read discarded body");
nginxa79aeb72012-11-27 14:26:09 +0000634
635 ngx_memzero(&b, sizeof(ngx_buf_t));
636
637 b.temporary = 1;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000638
Igor Sysoev49590f72009-09-28 13:08:15 +0000639 for ( ;; ) {
Igor Sysoev55c04d12007-08-15 12:44:43 +0000640 if (r->headers_in.content_length_n == 0) {
641 r->read_event_handler = ngx_http_block_reading;
642 return NGX_OK;
Igor Sysoev49590f72009-09-28 13:08:15 +0000643 }
644
645 if (!r->connection->read->ready) {
646 return NGX_AGAIN;
Igor Sysoev55c04d12007-08-15 12:44:43 +0000647 }
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000648
nginxa79aeb72012-11-27 14:26:09 +0000649 size = (size_t) ngx_min(r->headers_in.content_length_n,
650 NGX_HTTP_DISCARD_BUFFER_SIZE);
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000651
Igor Sysoev55c04d12007-08-15 12:44:43 +0000652 n = r->connection->recv(r->connection, buffer, size);
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000653
Igor Sysoev55c04d12007-08-15 12:44:43 +0000654 if (n == NGX_ERROR) {
655 r->connection->error = 1;
656 return NGX_OK;
657 }
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000658
Igor Sysoev55c04d12007-08-15 12:44:43 +0000659 if (n == NGX_AGAIN) {
660 return NGX_AGAIN;
661 }
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000662
Igor Sysoev650585b2007-08-28 16:23:30 +0000663 if (n == 0) {
664 return NGX_OK;
665 }
666
nginxa79aeb72012-11-27 14:26:09 +0000667 b.pos = buffer;
668 b.last = buffer + n;
669
670 rc = ngx_http_discard_request_body_filter(r, &b);
671
672 if (rc != NGX_OK) {
673 return rc;
674 }
Igor Sysoev49590f72009-09-28 13:08:15 +0000675 }
nginxa79aeb72012-11-27 14:26:09 +0000676}
677
678
679static ngx_int_t
680ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
681{
682 size_t size;
683 ngx_int_t rc;
684 ngx_http_request_body_t *rb;
685
686 if (r->headers_in.chunked) {
687
688 rb = r->request_body;
689
690 if (rb == NULL) {
691
692 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
693 if (rb == NULL) {
694 return NGX_HTTP_INTERNAL_SERVER_ERROR;
695 }
696
697 rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
698 if (rb->chunked == NULL) {
699 return NGX_HTTP_INTERNAL_SERVER_ERROR;
700 }
701
702 r->request_body = rb;
703 }
704
705 for ( ;; ) {
706
707 rc = ngx_http_parse_chunked(r, b, rb->chunked);
708
709 if (rc == NGX_OK) {
710
711 /* a chunk has been parsed successfully */
712
713 size = b->last - b->pos;
714
715 if ((off_t) size > rb->chunked->size) {
716 b->pos += rb->chunked->size;
717 rb->chunked->size = 0;
718
719 } else {
720 rb->chunked->size -= size;
721 b->pos = b->last;
722 }
723
724 continue;
725 }
726
727 if (rc == NGX_DONE) {
728
729 /* a whole response has been parsed successfully */
730
731 r->headers_in.content_length_n = 0;
732 break;
733 }
734
735 if (rc == NGX_AGAIN) {
736
737 /* set amount of data we want to see next time */
738
739 r->headers_in.content_length_n = rb->chunked->length;
740 break;
741 }
742
743 /* invalid */
744
745 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
746 "client sent invalid chunked body");
747
748 return NGX_HTTP_BAD_REQUEST;
749 }
750
751 } else {
752 size = b->last - b->pos;
753
754 if ((off_t) size > r->headers_in.content_length_n) {
755 b->pos += r->headers_in.content_length_n;
756 r->headers_in.content_length_n = 0;
757
758 } else {
759 b->pos = b->last;
760 r->headers_in.content_length_n -= size;
761 }
762 }
763
764 return NGX_OK;
Igor Sysoev9fdefb12006-04-14 09:34:54 +0000765}
Igor Sysoeva1ff67e2009-01-19 14:03:40 +0000766
767
768static ngx_int_t
769ngx_http_test_expect(ngx_http_request_t *r)
770{
771 ngx_int_t n;
772 ngx_str_t *expect;
773
774 if (r->expect_tested
775 || r->headers_in.expect == NULL
776 || r->http_version < NGX_HTTP_VERSION_11)
777 {
778 return NGX_OK;
779 }
780
781 r->expect_tested = 1;
782
783 expect = &r->headers_in.expect->value;
784
785 if (expect->len != sizeof("100-continue") - 1
786 || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
787 sizeof("100-continue") - 1)
788 != 0)
789 {
790 return NGX_OK;
791 }
792
793 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
794 "send 100 Continue");
795
796 n = r->connection->send(r->connection,
797 (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
798 sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
799
800 if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
801 return NGX_OK;
802 }
803
804 /* we assume that such small packet should be send successfully */
805
806 return NGX_ERROR;
807}
nginxa79aeb72012-11-27 14:26:09 +0000808
809
810static ngx_int_t
811ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
812{
813 if (r->headers_in.chunked) {
814 return ngx_http_request_body_chunked_filter(r, in);
815
816 } else {
817 return ngx_http_request_body_length_filter(r, in);
818 }
819}
820
821
822static ngx_int_t
823ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
824{
825 size_t size;
826 ngx_int_t rc;
827 ngx_buf_t *b;
828 ngx_chain_t *cl, *tl, *out, **ll;
829 ngx_http_request_body_t *rb;
830
831 rb = r->request_body;
832
833 if (rb->rest == -1) {
834 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
835 "http request body content length filter");
836
837 rb->rest = r->headers_in.content_length_n;
838 }
839
840 out = NULL;
841 ll = &out;
842
843 for (cl = in; cl; cl = cl->next) {
844
845 tl = ngx_chain_get_free_buf(r->pool, &rb->free);
846 if (tl == NULL) {
847 return NGX_HTTP_INTERNAL_SERVER_ERROR;
848 }
849
850 b = tl->buf;
851
852 ngx_memzero(b, sizeof(ngx_buf_t));
853
854 b->temporary = 1;
855 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
nginx7f609092013-03-26 13:27:30 +0000856 b->start = cl->buf->pos;
nginxa79aeb72012-11-27 14:26:09 +0000857 b->pos = cl->buf->pos;
858 b->last = cl->buf->last;
859 b->end = cl->buf->end;
860
861 size = cl->buf->last - cl->buf->pos;
862
863 if ((off_t) size < rb->rest) {
864 cl->buf->pos = cl->buf->last;
865 rb->rest -= size;
866
867 } else {
868 cl->buf->pos += rb->rest;
869 rb->rest = 0;
870 b->last = cl->buf->pos;
871 b->last_buf = 1;
872 }
873
874 *ll = tl;
875 ll = &tl->next;
876 }
877
878 rc = ngx_http_request_body_save_filter(r, out);
879
880 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
881 (ngx_buf_tag_t) &ngx_http_read_client_request_body);
882
883 return rc;
884}
885
886
887static ngx_int_t
888ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
889{
890 size_t size;
891 ngx_int_t rc;
892 ngx_buf_t *b;
893 ngx_chain_t *cl, *out, *tl, **ll;
894 ngx_http_request_body_t *rb;
895 ngx_http_core_loc_conf_t *clcf;
896
897 rb = r->request_body;
898
899 if (rb->rest == -1) {
900
901 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
902 "http request body chunked filter");
903
904 rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
905 if (rb->chunked == NULL) {
906 return NGX_HTTP_INTERNAL_SERVER_ERROR;
907 }
908
909 r->headers_in.content_length_n = 0;
910 rb->rest = 3;
911 }
912
913 out = NULL;
914 ll = &out;
915
916 for (cl = in; cl; cl = cl->next) {
917
918 for ( ;; ) {
919
920 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
921 "http body chunked buf "
922 "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z",
923 cl->buf->temporary, cl->buf->in_file,
924 cl->buf->start, cl->buf->pos,
925 cl->buf->last - cl->buf->pos,
926 cl->buf->file_pos,
927 cl->buf->file_last - cl->buf->file_pos);
928
929 rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
930
931 if (rc == NGX_OK) {
932
933 /* a chunk has been parsed successfully */
934
935 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
936
937 if (clcf->client_max_body_size
938 && clcf->client_max_body_size
939 < r->headers_in.content_length_n + rb->chunked->size)
940 {
941 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
942 "client intended to send too large chunked "
943 "body: %O bytes",
944 r->headers_in.content_length_n
945 + rb->chunked->size);
946
947 r->lingering_close = 1;
948
949 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
950 }
951
952 tl = ngx_chain_get_free_buf(r->pool, &rb->free);
953 if (tl == NULL) {
954 return NGX_HTTP_INTERNAL_SERVER_ERROR;
955 }
956
957 b = tl->buf;
958
959 ngx_memzero(b, sizeof(ngx_buf_t));
960
961 b->temporary = 1;
962 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
nginx7f609092013-03-26 13:27:30 +0000963 b->start = cl->buf->pos;
nginxa79aeb72012-11-27 14:26:09 +0000964 b->pos = cl->buf->pos;
965 b->last = cl->buf->last;
966 b->end = cl->buf->end;
967
968 *ll = tl;
969 ll = &tl->next;
970
971 size = cl->buf->last - cl->buf->pos;
972
973 if ((off_t) size > rb->chunked->size) {
974 cl->buf->pos += rb->chunked->size;
975 r->headers_in.content_length_n += rb->chunked->size;
976 rb->chunked->size = 0;
977
978 } else {
979 rb->chunked->size -= size;
980 r->headers_in.content_length_n += size;
981 cl->buf->pos = cl->buf->last;
982 }
983
984 b->last = cl->buf->pos;
985
986 continue;
987 }
988
989 if (rc == NGX_DONE) {
990
991 /* a whole response has been parsed successfully */
992
993 rb->rest = 0;
994
995 tl = ngx_chain_get_free_buf(r->pool, &rb->free);
996 if (tl == NULL) {
997 return NGX_HTTP_INTERNAL_SERVER_ERROR;
998 }
999
1000 b = tl->buf;
1001
1002 ngx_memzero(b, sizeof(ngx_buf_t));
1003
1004 b->last_buf = 1;
1005
1006 *ll = tl;
1007 ll = &tl->next;
1008
1009 break;
1010 }
1011
1012 if (rc == NGX_AGAIN) {
1013
1014 /* set rb->rest, amount of data we want to see next time */
1015
1016 rb->rest = rb->chunked->length;
1017
1018 break;
1019 }
1020
1021 /* invalid */
1022
1023 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1024 "client sent invalid chunked body");
1025
1026 return NGX_HTTP_BAD_REQUEST;
1027 }
1028 }
1029
1030 rc = ngx_http_request_body_save_filter(r, out);
1031
1032 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
1033 (ngx_buf_tag_t) &ngx_http_read_client_request_body);
1034
1035 return rc;
1036}
1037
1038
1039static ngx_int_t
1040ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
1041{
1042#if (NGX_DEBUG)
1043 ngx_chain_t *cl;
1044#endif
1045 ngx_http_request_body_t *rb;
1046
1047 rb = r->request_body;
1048
1049#if (NGX_DEBUG)
1050
1051 for (cl = rb->bufs; cl; cl = cl->next) {
1052 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
1053 "http body old buf t:%d f:%d %p, pos %p, size: %z "
1054 "file: %O, size: %z",
1055 cl->buf->temporary, cl->buf->in_file,
1056 cl->buf->start, cl->buf->pos,
1057 cl->buf->last - cl->buf->pos,
1058 cl->buf->file_pos,
1059 cl->buf->file_last - cl->buf->file_pos);
1060 }
1061
1062 for (cl = in; cl; cl = cl->next) {
1063 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
1064 "http body new buf t:%d f:%d %p, pos %p, size: %z "
1065 "file: %O, size: %z",
1066 cl->buf->temporary, cl->buf->in_file,
1067 cl->buf->start, cl->buf->pos,
1068 cl->buf->last - cl->buf->pos,
1069 cl->buf->file_pos,
1070 cl->buf->file_last - cl->buf->file_pos);
1071 }
1072
1073#endif
1074
1075 /* TODO: coalesce neighbouring buffers */
1076
1077 if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
1078 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1079 }
1080
1081 return NGX_OK;
1082}