comparison src/http/ngx_http_request_body.c @ 4930:6f085bfcdb4d

Request body: chunked transfer encoding support.
author Maxim Dounin <mdounin@mdounin.ru>
date Wed, 21 Nov 2012 01:08:11 +0000
parents caca5603bded
children 190fce68b6b5
comparison
equal deleted inserted replaced
4929:5a44d638cd27 4930:6f085bfcdb4d
10 #include <ngx_http.h> 10 #include <ngx_http.h>
11 11
12 12
13 static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r); 13 static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
14 static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r); 14 static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
15 static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r, 15 static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
16 ngx_chain_t *body);
17 static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r); 16 static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
17 static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,
18 ngx_buf_t *b);
18 static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r); 19 static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);
19 20
20 21 static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,
21 /* 22 ngx_chain_t *in);
22 * on completion ngx_http_read_client_request_body() adds to 23 static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,
23 * r->request_body->bufs one or two bufs: 24 ngx_chain_t *in);
24 * *) one memory buf that was preread in r->header_in; 25 static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,
25 * *) one memory or file buf that contains the rest of the body 26 ngx_chain_t *in);
26 */ 27 static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r,
28 ngx_chain_t *in);
29
27 30
28 ngx_int_t 31 ngx_int_t
29 ngx_http_read_client_request_body(ngx_http_request_t *r, 32 ngx_http_read_client_request_body(ngx_http_request_t *r,
30 ngx_http_client_body_handler_pt post_handler) 33 ngx_http_client_body_handler_pt post_handler)
31 { 34 {
32 size_t preread; 35 size_t preread;
33 ssize_t size; 36 ssize_t size;
34 ngx_int_t rc; 37 ngx_int_t rc;
35 ngx_buf_t *b; 38 ngx_chain_t out;
36 ngx_chain_t *cl, **next;
37 ngx_http_request_body_t *rb; 39 ngx_http_request_body_t *rb;
38 ngx_http_core_loc_conf_t *clcf; 40 ngx_http_core_loc_conf_t *clcf;
39 41
40 r->main->count++; 42 r->main->count++;
41 43
52 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); 54 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
53 if (rb == NULL) { 55 if (rb == NULL) {
54 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 56 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
55 goto done; 57 goto done;
56 } 58 }
57
58 r->request_body = rb;
59
60 if (r->headers_in.content_length_n < 0) {
61 post_handler(r);
62 return NGX_OK;
63 }
64
65 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
66
67 if (r->headers_in.content_length_n == 0) {
68
69 if (r->request_body_in_file_only) {
70 if (ngx_http_write_request_body(r, NULL) != NGX_OK) {
71 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
72 goto done;
73 }
74 }
75
76 post_handler(r);
77
78 return NGX_OK;
79 }
80
81 rb->post_handler = post_handler;
82 59
83 /* 60 /*
84 * set by ngx_pcalloc(): 61 * set by ngx_pcalloc():
85 * 62 *
86 * rb->bufs = NULL; 63 * rb->bufs = NULL;
87 * rb->buf = NULL; 64 * rb->buf = NULL;
88 * rb->rest = 0; 65 * rb->free = NULL;
66 * rb->busy = NULL;
67 * rb->chunked = NULL;
89 */ 68 */
69
70 rb->rest = -1;
71 rb->post_handler = post_handler;
72
73 r->request_body = rb;
74
75 if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
76 post_handler(r);
77 return NGX_OK;
78 }
90 79
91 preread = r->header_in->last - r->header_in->pos; 80 preread = r->header_in->last - r->header_in->pos;
92 81
93 if (preread) { 82 if (preread) {
94 83
95 /* there is the pre-read part of the request body */ 84 /* there is the pre-read part of the request body */
96 85
97 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 86 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
98 "http client request body preread %uz", preread); 87 "http client request body preread %uz", preread);
99 88
100 b = ngx_calloc_buf(r->pool); 89 out.buf = r->header_in;
101 if (b == NULL) { 90 out.next = NULL;
91
92 rc = ngx_http_request_body_filter(r, &out);
93
94 if (rc != NGX_OK) {
95 goto done;
96 }
97
98 r->request_length += preread - (r->header_in->last - r->header_in->pos);
99
100 if (!r->headers_in.chunked
101 && rb->rest > 0
102 && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
103 {
104 /* the whole request body may be placed in r->header_in */
105
106 rb->buf = r->header_in;
107 r->read_event_handler = ngx_http_read_client_request_body_handler;
108
109 rc = ngx_http_do_read_client_request_body(r);
110 goto done;
111 }
112
113 } else {
114 /* set rb->rest */
115
116 if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
102 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 117 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
103 goto done; 118 goto done;
104 } 119 }
105 120 }
106 b->temporary = 1; 121
107 b->start = r->header_in->pos; 122 if (rb->rest == 0) {
108 b->pos = r->header_in->pos; 123 /* the whole request body was pre-read */
109 b->last = r->header_in->last; 124
110 b->end = r->header_in->end; 125 if (r->request_body_in_file_only) {
111 126 if (ngx_http_write_request_body(r) != NGX_OK) {
112 rb->bufs = ngx_alloc_chain_link(r->pool); 127 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
113 if (rb->bufs == NULL) { 128 goto done;
114 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 129 }
115 goto done; 130 }
116 } 131
117 132 post_handler(r);
118 rb->bufs->buf = b; 133
119 rb->bufs->next = NULL; 134 return NGX_OK;
120 135 }
121 rb->buf = b; 136
122 137 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
123 if ((off_t) preread >= r->headers_in.content_length_n) {
124
125 /* the whole request body was pre-read */
126
127 r->header_in->pos += (size_t) r->headers_in.content_length_n;
128 r->request_length += r->headers_in.content_length_n;
129 b->last = r->header_in->pos;
130
131 if (r->request_body_in_file_only) {
132 if (ngx_http_write_request_body(r, rb->bufs) != NGX_OK) {
133 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
134 goto done;
135 }
136 }
137
138 post_handler(r);
139
140 return NGX_OK;
141 }
142
143 /*
144 * to not consider the body as pipelined request in
145 * ngx_http_set_keepalive()
146 */
147 r->header_in->pos = r->header_in->last;
148
149 r->request_length += preread;
150
151 rb->rest = r->headers_in.content_length_n - preread;
152
153 if (rb->rest <= (off_t) (b->end - b->last)) {
154
155 /* the whole request body may be placed in r->header_in */
156
157 rb->to_write = rb->bufs;
158
159 r->read_event_handler = ngx_http_read_client_request_body_handler;
160
161 rc = ngx_http_do_read_client_request_body(r);
162 goto done;
163 }
164
165 next = &rb->bufs->next;
166
167 } else {
168 b = NULL;
169 rb->rest = r->headers_in.content_length_n;
170 next = &rb->bufs;
171 }
172 138
173 size = clcf->client_body_buffer_size; 139 size = clcf->client_body_buffer_size;
174 size += size >> 2; 140 size += size >> 2;
175 141
176 if (rb->rest < size) { 142 /* TODO: honor r->request_body_in_single_buf */
143
144 if (!r->headers_in.chunked && rb->rest < size) {
177 size = (ssize_t) rb->rest; 145 size = (ssize_t) rb->rest;
178 146
179 if (r->request_body_in_single_buf) { 147 if (r->request_body_in_single_buf) {
180 size += preread; 148 size += preread;
181 } 149 }
182 150
183 } else { 151 } else {
184 size = clcf->client_body_buffer_size; 152 size = clcf->client_body_buffer_size;
185
186 /* disable copying buffer for r->request_body_in_single_buf */
187 b = NULL;
188 } 153 }
189 154
190 rb->buf = ngx_create_temp_buf(r->pool, size); 155 rb->buf = ngx_create_temp_buf(r->pool, size);
191 if (rb->buf == NULL) { 156 if (rb->buf == NULL) {
192 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 157 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
193 goto done; 158 goto done;
194 } 159 }
195 160
196 cl = ngx_alloc_chain_link(r->pool);
197 if (cl == NULL) {
198 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
199 goto done;
200 }
201
202 cl->buf = rb->buf;
203 cl->next = NULL;
204
205 if (b && r->request_body_in_single_buf) {
206 size = b->last - b->pos;
207 ngx_memcpy(rb->buf->pos, b->pos, size);
208 rb->buf->last += size;
209
210 next = &rb->bufs;
211 }
212
213 *next = cl;
214
215 if (r->request_body_in_file_only || r->request_body_in_single_buf) {
216 rb->to_write = rb->bufs;
217
218 } else {
219 rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
220 }
221
222 r->read_event_handler = ngx_http_read_client_request_body_handler; 161 r->read_event_handler = ngx_http_read_client_request_body_handler;
223 162
224 rc = ngx_http_do_read_client_request_body(r); 163 rc = ngx_http_do_read_client_request_body(r);
225 164
226 done: 165 done:
253 192
254 193
255 static ngx_int_t 194 static ngx_int_t
256 ngx_http_do_read_client_request_body(ngx_http_request_t *r) 195 ngx_http_do_read_client_request_body(ngx_http_request_t *r)
257 { 196 {
197 off_t rest;
258 size_t size; 198 size_t size;
259 ssize_t n; 199 ssize_t n;
200 ngx_int_t rc;
260 ngx_buf_t *b; 201 ngx_buf_t *b;
202 ngx_chain_t *cl, out;
261 ngx_connection_t *c; 203 ngx_connection_t *c;
262 ngx_http_request_body_t *rb; 204 ngx_http_request_body_t *rb;
263 ngx_http_core_loc_conf_t *clcf; 205 ngx_http_core_loc_conf_t *clcf;
264 206
265 c = r->connection; 207 c = r->connection;
270 212
271 for ( ;; ) { 213 for ( ;; ) {
272 for ( ;; ) { 214 for ( ;; ) {
273 if (rb->buf->last == rb->buf->end) { 215 if (rb->buf->last == rb->buf->end) {
274 216
275 if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { 217 /* pass buffer to request body filter chain */
218
219 out.buf = rb->buf;
220 out.next = NULL;
221
222 rc = ngx_http_request_body_filter(r, &out);
223
224 if (rc != NGX_OK) {
225 return rc;
226 }
227
228 /* write to file */
229
230 if (ngx_http_write_request_body(r) != NGX_OK) {
276 return NGX_HTTP_INTERNAL_SERVER_ERROR; 231 return NGX_HTTP_INTERNAL_SERVER_ERROR;
277 } 232 }
278 233
279 rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; 234 /* update chains */
235
236 rc = ngx_http_request_body_filter(r, NULL);
237
238 if (rc != NGX_OK) {
239 return rc;
240 }
241
242 if (rb->busy != NULL) {
243 return NGX_HTTP_INTERNAL_SERVER_ERROR;
244 }
245
246 rb->buf->pos = rb->buf->start;
280 rb->buf->last = rb->buf->start; 247 rb->buf->last = rb->buf->start;
281 } 248 }
282 249
283 size = rb->buf->end - rb->buf->last; 250 size = rb->buf->end - rb->buf->last;
284 251 rest = rb->rest - (rb->buf->last - rb->buf->pos);
285 if ((off_t) size > rb->rest) { 252
286 size = (size_t) rb->rest; 253 if ((off_t) size > rest) {
254 size = (size_t) rest;
287 } 255 }
288 256
289 n = c->recv(c, rb->buf->last, size); 257 n = c->recv(c, rb->buf->last, size);
290 258
291 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, 259 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
304 c->error = 1; 272 c->error = 1;
305 return NGX_HTTP_BAD_REQUEST; 273 return NGX_HTTP_BAD_REQUEST;
306 } 274 }
307 275
308 rb->buf->last += n; 276 rb->buf->last += n;
309 rb->rest -= n;
310 r->request_length += n; 277 r->request_length += n;
278
279 if (n == rest) {
280 /* pass buffer to request body filter chain */
281
282 out.buf = rb->buf;
283 out.next = NULL;
284
285 rc = ngx_http_request_body_filter(r, &out);
286
287 if (rc != NGX_OK) {
288 return rc;
289 }
290 }
311 291
312 if (rb->rest == 0) { 292 if (rb->rest == 0) {
313 break; 293 break;
314 } 294 }
315 295
343 323
344 if (rb->temp_file || r->request_body_in_file_only) { 324 if (rb->temp_file || r->request_body_in_file_only) {
345 325
346 /* save the last part */ 326 /* save the last part */
347 327
348 if (ngx_http_write_request_body(r, rb->to_write) != NGX_OK) { 328 if (ngx_http_write_request_body(r) != NGX_OK) {
349 return NGX_HTTP_INTERNAL_SERVER_ERROR; 329 return NGX_HTTP_INTERNAL_SERVER_ERROR;
350 } 330 }
351 331
352 b = ngx_calloc_buf(r->pool); 332 cl = ngx_chain_get_free_buf(r->pool, &rb->free);
353 if (b == NULL) { 333 if (cl == NULL) {
354 return NGX_HTTP_INTERNAL_SERVER_ERROR; 334 return NGX_HTTP_INTERNAL_SERVER_ERROR;
355 } 335 }
356 336
337 b = cl->buf;
338
339 ngx_memzero(b, sizeof(ngx_buf_t));
340
357 b->in_file = 1; 341 b->in_file = 1;
358 b->file_pos = 0;
359 b->file_last = rb->temp_file->file.offset; 342 b->file_last = rb->temp_file->file.offset;
360 b->file = &rb->temp_file->file; 343 b->file = &rb->temp_file->file;
361 344
362 if (rb->bufs->next) { 345 rb->bufs = cl;
363 rb->bufs->next->buf = b;
364
365 } else {
366 rb->bufs->buf = b;
367 }
368 }
369
370 if (rb->bufs->next
371 && (r->request_body_in_file_only || r->request_body_in_single_buf))
372 {
373 rb->bufs = rb->bufs->next;
374 } 346 }
375 347
376 r->read_event_handler = ngx_http_block_reading; 348 r->read_event_handler = ngx_http_block_reading;
377 349
378 rb->post_handler(r); 350 rb->post_handler(r);
380 return NGX_OK; 352 return NGX_OK;
381 } 353 }
382 354
383 355
384 static ngx_int_t 356 static ngx_int_t
385 ngx_http_write_request_body(ngx_http_request_t *r, ngx_chain_t *body) 357 ngx_http_write_request_body(ngx_http_request_t *r)
386 { 358 {
387 ssize_t n; 359 ssize_t n;
360 ngx_chain_t *cl;
388 ngx_temp_file_t *tf; 361 ngx_temp_file_t *tf;
389 ngx_http_request_body_t *rb; 362 ngx_http_request_body_t *rb;
390 ngx_http_core_loc_conf_t *clcf; 363 ngx_http_core_loc_conf_t *clcf;
391 364
392 rb = r->request_body; 365 rb = r->request_body;
366
367 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
368 "http write client request body, bufs %p", rb->bufs);
393 369
394 if (rb->temp_file == NULL) { 370 if (rb->temp_file == NULL) {
395 tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); 371 tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
396 if (tf == NULL) { 372 if (tf == NULL) {
397 return NGX_ERROR; 373 return NGX_ERROR;
412 tf->access = 0660; 388 tf->access = 0660;
413 } 389 }
414 390
415 rb->temp_file = tf; 391 rb->temp_file = tf;
416 392
417 if (body == NULL) { 393 if (rb->bufs == NULL) {
418 /* empty body with r->request_body_in_file_only */ 394 /* empty body with r->request_body_in_file_only */
419 395
420 if (ngx_create_temp_file(&tf->file, tf->path, tf->pool, 396 if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
421 tf->persistent, tf->clean, tf->access) 397 tf->persistent, tf->clean, tf->access)
422 != NGX_OK) 398 != NGX_OK)
426 402
427 return NGX_OK; 403 return NGX_OK;
428 } 404 }
429 } 405 }
430 406
431 n = ngx_write_chain_to_temp_file(rb->temp_file, body); 407 if (rb->bufs == NULL) {
408 return NGX_OK;
409 }
410
411 n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);
432 412
433 /* TODO: n == 0 or not complete and level event */ 413 /* TODO: n == 0 or not complete and level event */
434 414
435 if (n == NGX_ERROR) { 415 if (n == NGX_ERROR) {
436 return NGX_ERROR; 416 return NGX_ERROR;
437 } 417 }
438 418
439 rb->temp_file->offset += n; 419 rb->temp_file->offset += n;
440 420
421 /* mark all buffers as written */
422
423 for (cl = rb->bufs; cl; cl = cl->next) {
424 cl->buf->pos = cl->buf->last;
425 }
426
427 rb->bufs = NULL;
428
441 return NGX_OK; 429 return NGX_OK;
442 } 430 }
443 431
444 432
445 ngx_int_t 433 ngx_int_t
446 ngx_http_discard_request_body(ngx_http_request_t *r) 434 ngx_http_discard_request_body(ngx_http_request_t *r)
447 { 435 {
448 ssize_t size; 436 ssize_t size;
437 ngx_int_t rc;
449 ngx_event_t *rev; 438 ngx_event_t *rev;
450 439
451 if (r != r->main || r->discard_body) { 440 if (r != r->main || r->discard_body || r->request_body) {
452 return NGX_OK; 441 return NGX_OK;
453 } 442 }
454 443
455 if (ngx_http_test_expect(r) != NGX_OK) { 444 if (ngx_http_test_expect(r) != NGX_OK) {
456 return NGX_HTTP_INTERNAL_SERVER_ERROR; 445 return NGX_HTTP_INTERNAL_SERVER_ERROR;
462 451
463 if (rev->timer_set) { 452 if (rev->timer_set) {
464 ngx_del_timer(rev); 453 ngx_del_timer(rev);
465 } 454 }
466 455
467 if (r->headers_in.content_length_n <= 0 || r->request_body) { 456 if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
468 return NGX_OK; 457 return NGX_OK;
469 } 458 }
470 459
471 size = r->header_in->last - r->header_in->pos; 460 size = r->header_in->last - r->header_in->pos;
472 461
473 if (size) { 462 if (size) {
474 if (r->headers_in.content_length_n > size) { 463 rc = ngx_http_discard_request_body_filter(r, r->header_in);
475 r->header_in->pos += size; 464
476 r->headers_in.content_length_n -= size; 465 if (rc != NGX_OK) {
477 466 return rc;
478 } else { 467 }
479 r->header_in->pos += (size_t) r->headers_in.content_length_n; 468
480 r->headers_in.content_length_n = 0; 469 if (r->headers_in.content_length_n == 0) {
481 return NGX_OK; 470 return NGX_OK;
482 } 471 }
483 } 472 }
484 473
485 if (ngx_http_read_discarded_request_body(r) == NGX_OK) { 474 if (ngx_http_read_discarded_request_body(r) == NGX_OK) {
568 557
569 558
570 static ngx_int_t 559 static ngx_int_t
571 ngx_http_read_discarded_request_body(ngx_http_request_t *r) 560 ngx_http_read_discarded_request_body(ngx_http_request_t *r)
572 { 561 {
573 size_t size; 562 size_t size;
574 ssize_t n; 563 ssize_t n;
575 u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE]; 564 ngx_int_t rc;
565 ngx_buf_t b;
566 u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
576 567
577 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 568 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
578 "http read discarded body"); 569 "http read discarded body");
570
571 ngx_memzero(&b, sizeof(ngx_buf_t));
572
573 b.temporary = 1;
579 574
580 for ( ;; ) { 575 for ( ;; ) {
581 if (r->headers_in.content_length_n == 0) { 576 if (r->headers_in.content_length_n == 0) {
582 r->read_event_handler = ngx_http_block_reading; 577 r->read_event_handler = ngx_http_block_reading;
583 return NGX_OK; 578 return NGX_OK;
585 580
586 if (!r->connection->read->ready) { 581 if (!r->connection->read->ready) {
587 return NGX_AGAIN; 582 return NGX_AGAIN;
588 } 583 }
589 584
590 size = (r->headers_in.content_length_n > NGX_HTTP_DISCARD_BUFFER_SIZE) ? 585 size = (size_t) ngx_min(r->headers_in.content_length_n,
591 NGX_HTTP_DISCARD_BUFFER_SIZE: 586 NGX_HTTP_DISCARD_BUFFER_SIZE);
592 (size_t) r->headers_in.content_length_n;
593 587
594 n = r->connection->recv(r->connection, buffer, size); 588 n = r->connection->recv(r->connection, buffer, size);
595 589
596 if (n == NGX_ERROR) { 590 if (n == NGX_ERROR) {
597 r->connection->error = 1; 591 r->connection->error = 1;
604 598
605 if (n == 0) { 599 if (n == 0) {
606 return NGX_OK; 600 return NGX_OK;
607 } 601 }
608 602
609 r->headers_in.content_length_n -= n; 603 b.pos = buffer;
610 } 604 b.last = buffer + n;
605
606 rc = ngx_http_discard_request_body_filter(r, &b);
607
608 if (rc != NGX_OK) {
609 r->connection->error = 1;
610 return NGX_OK;
611 }
612 }
613 }
614
615
616 static ngx_int_t
617 ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
618 {
619 size_t size;
620 ngx_int_t rc;
621 ngx_http_request_body_t *rb;
622
623 if (r->headers_in.chunked) {
624
625 rb = r->request_body;
626
627 if (rb == NULL) {
628
629 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
630 if (rb == NULL) {
631 return NGX_HTTP_INTERNAL_SERVER_ERROR;
632 }
633
634 rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
635 if (rb == NULL) {
636 return NGX_HTTP_INTERNAL_SERVER_ERROR;
637 }
638
639 r->request_body = rb;
640 }
641
642 for ( ;; ) {
643
644 rc = ngx_http_parse_chunked(r, b, rb->chunked);
645
646 if (rc == NGX_OK) {
647
648 /* a chunk has been parsed successfully */
649
650 size = b->last - b->pos;
651
652 if ((off_t) size > rb->chunked->size) {
653 b->pos += rb->chunked->size;
654 rb->chunked->size = 0;
655
656 } else {
657 rb->chunked->size -= size;
658 b->pos = b->last;
659 }
660
661 continue;
662 }
663
664 if (rc == NGX_DONE) {
665
666 /* a whole response has been parsed successfully */
667
668 r->headers_in.content_length_n = 0;
669 break;
670 }
671
672 if (rc == NGX_AGAIN) {
673
674 /* set amount of data we want to see next time */
675
676 r->headers_in.content_length_n = rb->chunked->length;
677 break;
678 }
679
680 /* invalid */
681
682 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
683 "client sent invalid chunked body");
684
685 return NGX_HTTP_BAD_REQUEST;
686 }
687
688 } else {
689 size = b->last - b->pos;
690
691 if ((off_t) size > r->headers_in.content_length_n) {
692 b->pos += r->headers_in.content_length_n;
693 r->headers_in.content_length_n = 0;
694
695 } else {
696 b->pos = b->last;
697 r->headers_in.content_length_n -= size;
698 }
699 }
700
701 return NGX_OK;
611 } 702 }
612 703
613 704
614 static ngx_int_t 705 static ngx_int_t
615 ngx_http_test_expect(ngx_http_request_t *r) 706 ngx_http_test_expect(ngx_http_request_t *r)
649 740
650 /* we assume that such small packet should be send successfully */ 741 /* we assume that such small packet should be send successfully */
651 742
652 return NGX_ERROR; 743 return NGX_ERROR;
653 } 744 }
745
746
747 static ngx_int_t
748 ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
749 {
750 if (r->headers_in.chunked) {
751 return ngx_http_request_body_chunked_filter(r, in);
752
753 } else {
754 return ngx_http_request_body_length_filter(r, in);
755 }
756 }
757
758
759 static ngx_int_t
760 ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
761 {
762 size_t size;
763 ngx_int_t rc;
764 ngx_buf_t *b;
765 ngx_chain_t *cl, *tl, *out, **ll;
766 ngx_http_request_body_t *rb;
767
768 rb = r->request_body;
769
770 if (rb->rest == -1) {
771 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
772 "http request body content length filter");
773
774 rb->rest = r->headers_in.content_length_n;
775 }
776
777 out = NULL;
778 ll = &out;
779
780 for (cl = in; cl; cl = cl->next) {
781
782 tl = ngx_chain_get_free_buf(r->pool, &rb->free);
783 if (tl == NULL) {
784 return NGX_HTTP_INTERNAL_SERVER_ERROR;
785 }
786
787 b = tl->buf;
788
789 ngx_memzero(b, sizeof(ngx_buf_t));
790
791 b->temporary = 1;
792 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
793 b->start = cl->buf->start;
794 b->pos = cl->buf->pos;
795 b->last = cl->buf->last;
796 b->end = cl->buf->end;
797
798 size = cl->buf->last - cl->buf->pos;
799
800 if ((off_t) size < rb->rest) {
801 cl->buf->pos = cl->buf->last;
802 rb->rest -= size;
803
804 } else {
805 cl->buf->pos += rb->rest;
806 rb->rest = 0;
807 b->last = cl->buf->pos;
808 b->last_buf = 1;
809 }
810
811 *ll = tl;
812 ll = &tl->next;
813 }
814
815 rc = ngx_http_request_body_save_filter(r, out);
816
817 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
818 (ngx_buf_tag_t) &ngx_http_read_client_request_body);
819
820 return rc;
821 }
822
823
824 static ngx_int_t
825 ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
826 {
827 size_t size;
828 ngx_int_t rc;
829 ngx_buf_t *b;
830 ngx_chain_t *cl, *out, *tl, **ll;
831 ngx_http_request_body_t *rb;
832 ngx_http_core_loc_conf_t *clcf;
833
834 rb = r->request_body;
835
836 if (rb->rest == -1) {
837
838 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
839 "http request body chunked filter");
840
841 rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
842 if (rb->chunked == NULL) {
843 return NGX_HTTP_INTERNAL_SERVER_ERROR;
844 }
845
846 r->headers_in.content_length_n = 0;
847 rb->rest = 3;
848 }
849
850 out = NULL;
851 ll = &out;
852
853 for (cl = in; cl; cl = cl->next) {
854
855 for ( ;; ) {
856
857 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
858 "http body chunked buf "
859 "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z",
860 cl->buf->temporary, cl->buf->in_file,
861 cl->buf->start, cl->buf->pos,
862 cl->buf->last - cl->buf->pos,
863 cl->buf->file_pos,
864 cl->buf->file_last - cl->buf->file_pos);
865
866 rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
867
868 if (rc == NGX_OK) {
869
870 /* a chunk has been parsed successfully */
871
872 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
873
874 if (clcf->client_max_body_size
875 && clcf->client_max_body_size
876 < r->headers_in.content_length_n + rb->chunked->size)
877 {
878 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
879 "client intended to send too large chunked "
880 "body: %O bytes",
881 r->headers_in.content_length_n
882 + rb->chunked->size);
883
884 r->lingering_close = 1;
885
886 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
887 }
888
889 tl = ngx_chain_get_free_buf(r->pool, &rb->free);
890 if (tl == NULL) {
891 return NGX_HTTP_INTERNAL_SERVER_ERROR;
892 }
893
894 b = tl->buf;
895
896 ngx_memzero(b, sizeof(ngx_buf_t));
897
898 b->temporary = 1;
899 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
900 b->start = cl->buf->start;
901 b->pos = cl->buf->pos;
902 b->last = cl->buf->last;
903 b->end = cl->buf->end;
904
905 *ll = tl;
906 ll = &tl->next;
907
908 size = cl->buf->last - cl->buf->pos;
909
910 if ((off_t) size > rb->chunked->size) {
911 cl->buf->pos += rb->chunked->size;
912 r->headers_in.content_length_n += rb->chunked->size;
913 rb->chunked->size = 0;
914
915 } else {
916 rb->chunked->size -= size;
917 r->headers_in.content_length_n += size;
918 cl->buf->pos = cl->buf->last;
919 }
920
921 b->last = cl->buf->pos;
922
923 continue;
924 }
925
926 if (rc == NGX_DONE) {
927
928 /* a whole response has been parsed successfully */
929
930 rb->rest = 0;
931
932 tl = ngx_chain_get_free_buf(r->pool, &rb->free);
933 if (tl == NULL) {
934 return NGX_HTTP_INTERNAL_SERVER_ERROR;
935 }
936
937 b = tl->buf;
938
939 ngx_memzero(b, sizeof(ngx_buf_t));
940
941 b->last_buf = 1;
942
943 *ll = tl;
944 ll = &tl->next;
945
946 break;
947 }
948
949 if (rc == NGX_AGAIN) {
950
951 /* set rb->rest, amount of data we want to see next time */
952
953 rb->rest = rb->chunked->length;
954
955 break;
956 }
957
958 /* invalid */
959
960 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
961 "client sent invalid chunked body");
962
963 return NGX_HTTP_BAD_REQUEST;
964 }
965 }
966
967 rc = ngx_http_request_body_save_filter(r, out);
968
969 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
970 (ngx_buf_tag_t) &ngx_http_read_client_request_body);
971
972 return rc;
973 }
974
975
976 static ngx_int_t
977 ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
978 {
979 ngx_chain_t *cl;
980 ngx_http_request_body_t *rb;
981
982 rb = r->request_body;
983
984 #if (NGX_DEBUG)
985
986 for (cl = rb->bufs; cl; cl = cl->next) {
987 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
988 "http body old buf t:%d f:%d %p, pos %p, size: %z "
989 "file: %O, size: %z",
990 cl->buf->temporary, cl->buf->in_file,
991 cl->buf->start, cl->buf->pos,
992 cl->buf->last - cl->buf->pos,
993 cl->buf->file_pos,
994 cl->buf->file_last - cl->buf->file_pos);
995 }
996
997 for (cl = in; cl; cl = cl->next) {
998 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
999 "http body new buf t:%d f:%d %p, pos %p, size: %z "
1000 "file: %O, size: %z",
1001 cl->buf->temporary, cl->buf->in_file,
1002 cl->buf->start, cl->buf->pos,
1003 cl->buf->last - cl->buf->pos,
1004 cl->buf->file_pos,
1005 cl->buf->file_last - cl->buf->file_pos);
1006 }
1007
1008 #endif
1009
1010 /* TODO: coalesce neighbouring buffers */
1011
1012 ngx_chain_add_copy(r->pool, &rb->bufs, in);
1013
1014 return NGX_OK;
1015 }