0
|
1
|
|
2 /*
|
|
3 * Copyright (C) Igor Sysoev
|
660
|
4 * Copyright (C) Nginx, Inc.
|
0
|
5 */
|
|
6
|
|
7
|
|
8 #include <ngx_config.h>
|
|
9 #include <ngx_core.h>
|
|
10 #include <ngx_http.h>
|
|
11
|
|
12
|
58
|
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);
|
692
|
15 static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
|
326
|
16 static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
|
692
|
17 static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,
|
|
18 ngx_buf_t *b);
|
438
|
19 static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);
|
182
|
20
|
692
|
21 static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,
|
|
22 ngx_chain_t *in);
|
|
23 static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,
|
|
24 ngx_chain_t *in);
|
|
25 static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,
|
|
26 ngx_chain_t *in);
|
|
27 static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r,
|
|
28 ngx_chain_t *in);
|
0
|
29
|
|
30
|
50
|
31 ngx_int_t
|
|
32 ngx_http_read_client_request_body(ngx_http_request_t *r,
|
|
33 ngx_http_client_body_handler_pt post_handler)
|
0
|
34 {
|
212
|
35 size_t preread;
|
|
36 ssize_t size;
|
692
|
37 ngx_int_t rc;
|
|
38 ngx_chain_t out;
|
28
|
39 ngx_http_request_body_t *rb;
|
0
|
40 ngx_http_core_loc_conf_t *clcf;
|
|
41
|
518
|
42 r->main->count++;
|
|
43
|
86
|
44 if (r->request_body || r->discard_body) {
|
58
|
45 post_handler(r);
|
|
46 return NGX_OK;
|
|
47 }
|
|
48
|
438
|
49 if (ngx_http_test_expect(r) != NGX_OK) {
|
692
|
50 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
51 goto done;
|
438
|
52 }
|
|
53
|
50
|
54 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
|
|
55 if (rb == NULL) {
|
692
|
56 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
57 goto done;
|
238
|
58 }
|
|
59
|
28
|
60 /*
|
|
61 * set by ngx_pcalloc():
|
|
62 *
|
|
63 * rb->bufs = NULL;
|
|
64 * rb->buf = NULL;
|
692
|
65 * rb->free = NULL;
|
|
66 * rb->busy = NULL;
|
|
67 * rb->chunked = NULL;
|
28
|
68 */
|
|
69
|
692
|
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 }
|
|
79
|
182
|
80 preread = r->header_in->last - r->header_in->pos;
|
0
|
81
|
182
|
82 if (preread) {
|
0
|
83
|
|
84 /* there is the pre-read part of the request body */
|
|
85
|
182
|
86 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
87 "http client request body preread %uz", preread);
|
|
88
|
692
|
89 out.buf = r->header_in;
|
|
90 out.next = NULL;
|
|
91
|
|
92 rc = ngx_http_request_body_filter(r, &out);
|
|
93
|
|
94 if (rc != NGX_OK) {
|
|
95 goto done;
|
28
|
96 }
|
0
|
97
|
692
|
98 r->request_length += preread - (r->header_in->last - r->header_in->pos);
|
0
|
99
|
692
|
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 r->write_event_handler = ngx_http_request_empty_handler;
|
|
109
|
|
110 rc = ngx_http_do_read_client_request_body(r);
|
|
111 goto done;
|
28
|
112 }
|
|
113
|
692
|
114 } else {
|
|
115 /* set rb->rest */
|
0
|
116
|
692
|
117 if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
|
|
118 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
119 goto done;
|
|
120 }
|
|
121 }
|
0
|
122
|
692
|
123 if (rb->rest == 0) {
|
|
124 /* the whole request body was pre-read */
|
0
|
125
|
692
|
126 if (r->request_body_in_file_only) {
|
|
127 if (ngx_http_write_request_body(r) != NGX_OK) {
|
|
128 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
129 goto done;
|
182
|
130 }
|
0
|
131 }
|
|
132
|
692
|
133 post_handler(r);
|
182
|
134
|
692
|
135 return NGX_OK;
|
|
136 }
|
182
|
137
|
692
|
138 if (rb->rest < 0) {
|
|
139 ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
|
|
140 "negative request body rest");
|
|
141 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
142 goto done;
|
|
143 }
|
182
|
144
|
692
|
145 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
0
|
146
|
182
|
147 size = clcf->client_body_buffer_size;
|
|
148 size += size >> 2;
|
0
|
149
|
692
|
150 /* TODO: honor r->request_body_in_single_buf */
|
|
151
|
|
152 if (!r->headers_in.chunked && rb->rest < size) {
|
212
|
153 size = (ssize_t) rb->rest;
|
0
|
154
|
182
|
155 if (r->request_body_in_single_buf) {
|
|
156 size += preread;
|
|
157 }
|
|
158
|
0
|
159 } else {
|
|
160 size = clcf->client_body_buffer_size;
|
|
161 }
|
|
162
|
50
|
163 rb->buf = ngx_create_temp_buf(r->pool, size);
|
|
164 if (rb->buf == NULL) {
|
692
|
165 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
166 goto done;
|
0
|
167 }
|
|
168
|
58
|
169 r->read_event_handler = ngx_http_read_client_request_body_handler;
|
692
|
170 r->write_event_handler = ngx_http_request_empty_handler;
|
0
|
171
|
692
|
172 rc = ngx_http_do_read_client_request_body(r);
|
|
173
|
|
174 done:
|
|
175
|
|
176 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
|
177 r->main->count--;
|
|
178 }
|
|
179
|
|
180 return rc;
|
0
|
181 }
|
|
182
|
|
183
|
50
|
184 static void
|
58
|
185 ngx_http_read_client_request_body_handler(ngx_http_request_t *r)
|
0
|
186 {
|
58
|
187 ngx_int_t rc;
|
0
|
188
|
58
|
189 if (r->connection->read->timedout) {
|
126
|
190 r->connection->timedout = 1;
|
278
|
191 ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
|
0
|
192 return;
|
|
193 }
|
|
194
|
58
|
195 rc = ngx_http_do_read_client_request_body(r);
|
0
|
196
|
|
197 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
278
|
198 ngx_http_finalize_request(r, rc);
|
0
|
199 }
|
|
200 }
|
|
201
|
|
202
|
50
|
203 static ngx_int_t
|
58
|
204 ngx_http_do_read_client_request_body(ngx_http_request_t *r)
|
0
|
205 {
|
692
|
206 off_t rest;
|
0
|
207 size_t size;
|
|
208 ssize_t n;
|
692
|
209 ngx_int_t rc;
|
0
|
210 ngx_buf_t *b;
|
692
|
211 ngx_chain_t *cl, out;
|
58
|
212 ngx_connection_t *c;
|
28
|
213 ngx_http_request_body_t *rb;
|
0
|
214 ngx_http_core_loc_conf_t *clcf;
|
|
215
|
58
|
216 c = r->connection;
|
28
|
217 rb = r->request_body;
|
0
|
218
|
|
219 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
|
220 "http read client request body");
|
|
221
|
|
222 for ( ;; ) {
|
256
|
223 for ( ;; ) {
|
|
224 if (rb->buf->last == rb->buf->end) {
|
|
225
|
692
|
226 /* pass buffer to request body filter chain */
|
|
227
|
|
228 out.buf = rb->buf;
|
|
229 out.next = NULL;
|
|
230
|
|
231 rc = ngx_http_request_body_filter(r, &out);
|
|
232
|
|
233 if (rc != NGX_OK) {
|
|
234 return rc;
|
|
235 }
|
|
236
|
|
237 /* write to file */
|
|
238
|
|
239 if (ngx_http_write_request_body(r) != NGX_OK) {
|
256
|
240 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
241 }
|
28
|
242
|
692
|
243 /* update chains */
|
|
244
|
|
245 rc = ngx_http_request_body_filter(r, NULL);
|
|
246
|
|
247 if (rc != NGX_OK) {
|
|
248 return rc;
|
|
249 }
|
|
250
|
|
251 if (rb->busy != NULL) {
|
|
252 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
253 }
|
|
254
|
|
255 rb->buf->pos = rb->buf->start;
|
256
|
256 rb->buf->last = rb->buf->start;
|
|
257 }
|
|
258
|
|
259 size = rb->buf->end - rb->buf->last;
|
692
|
260 rest = rb->rest - (rb->buf->last - rb->buf->pos);
|
256
|
261
|
692
|
262 if ((off_t) size > rest) {
|
|
263 size = (size_t) rest;
|
0
|
264 }
|
|
265
|
256
|
266 n = c->recv(c, rb->buf->last, size);
|
|
267
|
|
268 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
|
269 "http client request body recv %z", n);
|
0
|
270
|
256
|
271 if (n == NGX_AGAIN) {
|
|
272 break;
|
|
273 }
|
|
274
|
|
275 if (n == 0) {
|
|
276 ngx_log_error(NGX_LOG_INFO, c->log, 0,
|
672
|
277 "client prematurely closed connection");
|
256
|
278 }
|
0
|
279
|
256
|
280 if (n == 0 || n == NGX_ERROR) {
|
|
281 c->error = 1;
|
|
282 return NGX_HTTP_BAD_REQUEST;
|
|
283 }
|
|
284
|
|
285 rb->buf->last += n;
|
|
286 r->request_length += n;
|
|
287
|
692
|
288 if (n == rest) {
|
|
289 /* pass buffer to request body filter chain */
|
|
290
|
|
291 out.buf = rb->buf;
|
|
292 out.next = NULL;
|
|
293
|
|
294 rc = ngx_http_request_body_filter(r, &out);
|
|
295
|
|
296 if (rc != NGX_OK) {
|
|
297 return rc;
|
|
298 }
|
|
299 }
|
|
300
|
256
|
301 if (rb->rest == 0) {
|
|
302 break;
|
|
303 }
|
|
304
|
|
305 if (rb->buf->last < rb->buf->end) {
|
|
306 break;
|
|
307 }
|
0
|
308 }
|
|
309
|
|
310 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
|
286
|
311 "http client request body rest %O", rb->rest);
|
0
|
312
|
28
|
313 if (rb->rest == 0) {
|
0
|
314 break;
|
|
315 }
|
|
316
|
256
|
317 if (!c->read->ready) {
|
|
318 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
|
319 ngx_add_timer(c->read, clcf->client_body_timeout);
|
0
|
320
|
430
|
321 if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
|
256
|
322 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
323 }
|
122
|
324
|
256
|
325 return NGX_AGAIN;
|
122
|
326 }
|
0
|
327 }
|
|
328
|
122
|
329 if (c->read->timer_set) {
|
|
330 ngx_del_timer(c->read);
|
|
331 }
|
|
332
|
182
|
333 if (rb->temp_file || r->request_body_in_file_only) {
|
0
|
334
|
|
335 /* save the last part */
|
28
|
336
|
692
|
337 if (ngx_http_write_request_body(r) != NGX_OK) {
|
0
|
338 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
339 }
|
|
340
|
692
|
341 cl = ngx_chain_get_free_buf(r->pool, &rb->free);
|
|
342 if (cl == NULL) {
|
0
|
343 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
344 }
|
|
345
|
692
|
346 b = cl->buf;
|
|
347
|
|
348 ngx_memzero(b, sizeof(ngx_buf_t));
|
|
349
|
0
|
350 b->in_file = 1;
|
28
|
351 b->file_last = rb->temp_file->file.offset;
|
|
352 b->file = &rb->temp_file->file;
|
0
|
353
|
692
|
354 rb->bufs = cl;
|
182
|
355 }
|
|
356
|
636
|
357 r->read_event_handler = ngx_http_block_reading;
|
|
358
|
28
|
359 rb->post_handler(r);
|
0
|
360
|
|
361 return NGX_OK;
|
|
362 }
|
182
|
363
|
|
364
|
|
365 static ngx_int_t
|
692
|
366 ngx_http_write_request_body(ngx_http_request_t *r)
|
182
|
367 {
|
|
368 ssize_t n;
|
692
|
369 ngx_chain_t *cl;
|
182
|
370 ngx_temp_file_t *tf;
|
|
371 ngx_http_request_body_t *rb;
|
|
372 ngx_http_core_loc_conf_t *clcf;
|
|
373
|
|
374 rb = r->request_body;
|
|
375
|
692
|
376 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
377 "http write client request body, bufs %p", rb->bufs);
|
|
378
|
182
|
379 if (rb->temp_file == NULL) {
|
|
380 tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
|
|
381 if (tf == NULL) {
|
|
382 return NGX_ERROR;
|
|
383 }
|
|
384
|
|
385 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
|
386
|
|
387 tf->file.fd = NGX_INVALID_FILE;
|
|
388 tf->file.log = r->connection->log;
|
|
389 tf->path = clcf->client_body_temp_path;
|
|
390 tf->pool = r->pool;
|
|
391 tf->warn = "a client request body is buffered to a temporary file";
|
186
|
392 tf->log_level = r->request_body_file_log_level;
|
182
|
393 tf->persistent = r->request_body_in_persistent_file;
|
278
|
394 tf->clean = r->request_body_in_clean_file;
|
182
|
395
|
|
396 if (r->request_body_file_group_access) {
|
276
|
397 tf->access = 0660;
|
182
|
398 }
|
|
399
|
|
400 rb->temp_file = tf;
|
692
|
401
|
|
402 if (rb->bufs == NULL) {
|
|
403 /* empty body with r->request_body_in_file_only */
|
|
404
|
|
405 if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
|
|
406 tf->persistent, tf->clean, tf->access)
|
|
407 != NGX_OK)
|
|
408 {
|
|
409 return NGX_ERROR;
|
|
410 }
|
|
411
|
|
412 return NGX_OK;
|
|
413 }
|
182
|
414 }
|
|
415
|
692
|
416 if (rb->bufs == NULL) {
|
|
417 return NGX_OK;
|
|
418 }
|
|
419
|
|
420 n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);
|
182
|
421
|
|
422 /* TODO: n == 0 or not complete and level event */
|
|
423
|
|
424 if (n == NGX_ERROR) {
|
|
425 return NGX_ERROR;
|
|
426 }
|
|
427
|
|
428 rb->temp_file->offset += n;
|
|
429
|
692
|
430 /* mark all buffers as written */
|
|
431
|
|
432 for (cl = rb->bufs; cl; cl = cl->next) {
|
|
433 cl->buf->pos = cl->buf->last;
|
|
434 }
|
|
435
|
|
436 rb->bufs = NULL;
|
|
437
|
182
|
438 return NGX_OK;
|
|
439 }
|
|
440
|
|
441
|
|
442 ngx_int_t
|
326
|
443 ngx_http_discard_request_body(ngx_http_request_t *r)
|
182
|
444 {
|
|
445 ssize_t size;
|
692
|
446 ngx_int_t rc;
|
182
|
447 ngx_event_t *rev;
|
|
448
|
692
|
449 if (r != r->main || r->discard_body || r->request_body) {
|
182
|
450 return NGX_OK;
|
|
451 }
|
|
452
|
438
|
453 if (ngx_http_test_expect(r) != NGX_OK) {
|
|
454 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
455 }
|
|
456
|
182
|
457 rev = r->connection->read;
|
|
458
|
|
459 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");
|
|
460
|
|
461 if (rev->timer_set) {
|
|
462 ngx_del_timer(rev);
|
|
463 }
|
|
464
|
692
|
465 if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
|
182
|
466 return NGX_OK;
|
|
467 }
|
|
468
|
|
469 size = r->header_in->last - r->header_in->pos;
|
|
470
|
692
|
471 if (size || r->headers_in.chunked) {
|
|
472 rc = ngx_http_discard_request_body_filter(r, r->header_in);
|
182
|
473
|
692
|
474 if (rc != NGX_OK) {
|
|
475 return rc;
|
|
476 }
|
|
477
|
|
478 if (r->headers_in.content_length_n == 0) {
|
182
|
479 return NGX_OK;
|
|
480 }
|
|
481 }
|
|
482
|
692
|
483 rc = ngx_http_read_discarded_request_body(r);
|
|
484
|
|
485 if (rc == NGX_OK) {
|
|
486 r->lingering_close = 0;
|
|
487 return NGX_OK;
|
|
488 }
|
|
489
|
|
490 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
|
491 return rc;
|
|
492 }
|
|
493
|
|
494 /* rc == NGX_AGAIN */
|
|
495
|
538
|
496 r->read_event_handler = ngx_http_discarded_request_body_handler;
|
182
|
497
|
430
|
498 if (ngx_handle_read_event(rev, 0) != NGX_OK) {
|
182
|
499 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
500 }
|
|
501
|
692
|
502 r->count++;
|
|
503 r->discard_body = 1;
|
326
|
504
|
|
505 return NGX_OK;
|
182
|
506 }
|
|
507
|
|
508
|
538
|
509 void
|
|
510 ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
|
182
|
511 {
|
326
|
512 ngx_int_t rc;
|
|
513 ngx_msec_t timer;
|
|
514 ngx_event_t *rev;
|
|
515 ngx_connection_t *c;
|
|
516 ngx_http_core_loc_conf_t *clcf;
|
|
517
|
|
518 c = r->connection;
|
|
519 rev = c->read;
|
182
|
520
|
326
|
521 if (rev->timedout) {
|
|
522 c->timedout = 1;
|
|
523 c->error = 1;
|
538
|
524 ngx_http_finalize_request(r, NGX_ERROR);
|
326
|
525 return;
|
|
526 }
|
182
|
527
|
326
|
528 if (r->lingering_time) {
|
346
|
529 timer = (ngx_msec_t) (r->lingering_time - ngx_time());
|
326
|
530
|
|
531 if (timer <= 0) {
|
|
532 r->discard_body = 0;
|
532
|
533 r->lingering_close = 0;
|
538
|
534 ngx_http_finalize_request(r, NGX_ERROR);
|
182
|
535 return;
|
|
536 }
|
326
|
537
|
|
538 } else {
|
|
539 timer = 0;
|
|
540 }
|
|
541
|
|
542 rc = ngx_http_read_discarded_request_body(r);
|
|
543
|
|
544 if (rc == NGX_OK) {
|
|
545 r->discard_body = 0;
|
532
|
546 r->lingering_close = 0;
|
538
|
547 ngx_http_finalize_request(r, NGX_DONE);
|
326
|
548 return;
|
182
|
549 }
|
|
550
|
692
|
551 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
|
|
552 c->error = 1;
|
|
553 ngx_http_finalize_request(r, NGX_ERROR);
|
|
554 return;
|
|
555 }
|
|
556
|
326
|
557 /* rc == NGX_AGAIN */
|
|
558
|
430
|
559 if (ngx_handle_read_event(rev, 0) != NGX_OK) {
|
332
|
560 c->error = 1;
|
538
|
561 ngx_http_finalize_request(r, NGX_ERROR);
|
326
|
562 return;
|
|
563 }
|
|
564
|
|
565 if (timer) {
|
|
566
|
|
567 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
|
568
|
|
569 timer *= 1000;
|
|
570
|
|
571 if (timer > clcf->lingering_timeout) {
|
|
572 timer = clcf->lingering_timeout;
|
|
573 }
|
|
574
|
|
575 ngx_add_timer(rev, timer);
|
182
|
576 }
|
|
577 }
|
|
578
|
|
579
|
|
580 static ngx_int_t
|
326
|
581 ngx_http_read_discarded_request_body(ngx_http_request_t *r)
|
182
|
582 {
|
692
|
583 size_t size;
|
|
584 ssize_t n;
|
|
585 ngx_int_t rc;
|
|
586 ngx_buf_t b;
|
|
587 u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
|
182
|
588
|
|
589 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
590 "http read discarded body");
|
|
591
|
692
|
592 ngx_memzero(&b, sizeof(ngx_buf_t));
|
|
593
|
|
594 b.temporary = 1;
|
|
595
|
530
|
596 for ( ;; ) {
|
326
|
597 if (r->headers_in.content_length_n == 0) {
|
|
598 r->read_event_handler = ngx_http_block_reading;
|
|
599 return NGX_OK;
|
|
600 }
|
182
|
601
|
530
|
602 if (!r->connection->read->ready) {
|
|
603 return NGX_AGAIN;
|
|
604 }
|
|
605
|
692
|
606 size = (size_t) ngx_min(r->headers_in.content_length_n,
|
|
607 NGX_HTTP_DISCARD_BUFFER_SIZE);
|
182
|
608
|
326
|
609 n = r->connection->recv(r->connection, buffer, size);
|
182
|
610
|
326
|
611 if (n == NGX_ERROR) {
|
|
612 r->connection->error = 1;
|
|
613 return NGX_OK;
|
|
614 }
|
182
|
615
|
326
|
616 if (n == NGX_AGAIN) {
|
|
617 return NGX_AGAIN;
|
|
618 }
|
182
|
619
|
330
|
620 if (n == 0) {
|
|
621 return NGX_OK;
|
|
622 }
|
|
623
|
692
|
624 b.pos = buffer;
|
|
625 b.last = buffer + n;
|
|
626
|
|
627 rc = ngx_http_discard_request_body_filter(r, &b);
|
|
628
|
|
629 if (rc != NGX_OK) {
|
|
630 return rc;
|
|
631 }
|
530
|
632 }
|
182
|
633 }
|
438
|
634
|
|
635
|
|
636 static ngx_int_t
|
692
|
637 ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
|
|
638 {
|
|
639 size_t size;
|
|
640 ngx_int_t rc;
|
|
641 ngx_http_request_body_t *rb;
|
|
642
|
|
643 if (r->headers_in.chunked) {
|
|
644
|
|
645 rb = r->request_body;
|
|
646
|
|
647 if (rb == NULL) {
|
|
648
|
|
649 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
|
|
650 if (rb == NULL) {
|
|
651 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
652 }
|
|
653
|
|
654 rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
|
|
655 if (rb->chunked == NULL) {
|
|
656 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
657 }
|
|
658
|
|
659 r->request_body = rb;
|
|
660 }
|
|
661
|
|
662 for ( ;; ) {
|
|
663
|
|
664 rc = ngx_http_parse_chunked(r, b, rb->chunked);
|
|
665
|
|
666 if (rc == NGX_OK) {
|
|
667
|
|
668 /* a chunk has been parsed successfully */
|
|
669
|
|
670 size = b->last - b->pos;
|
|
671
|
|
672 if ((off_t) size > rb->chunked->size) {
|
|
673 b->pos += rb->chunked->size;
|
|
674 rb->chunked->size = 0;
|
|
675
|
|
676 } else {
|
|
677 rb->chunked->size -= size;
|
|
678 b->pos = b->last;
|
|
679 }
|
|
680
|
|
681 continue;
|
|
682 }
|
|
683
|
|
684 if (rc == NGX_DONE) {
|
|
685
|
|
686 /* a whole response has been parsed successfully */
|
|
687
|
|
688 r->headers_in.content_length_n = 0;
|
|
689 break;
|
|
690 }
|
|
691
|
|
692 if (rc == NGX_AGAIN) {
|
|
693
|
|
694 /* set amount of data we want to see next time */
|
|
695
|
|
696 r->headers_in.content_length_n = rb->chunked->length;
|
|
697 break;
|
|
698 }
|
|
699
|
|
700 /* invalid */
|
|
701
|
|
702 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
703 "client sent invalid chunked body");
|
|
704
|
|
705 return NGX_HTTP_BAD_REQUEST;
|
|
706 }
|
|
707
|
|
708 } else {
|
|
709 size = b->last - b->pos;
|
|
710
|
|
711 if ((off_t) size > r->headers_in.content_length_n) {
|
|
712 b->pos += r->headers_in.content_length_n;
|
|
713 r->headers_in.content_length_n = 0;
|
|
714
|
|
715 } else {
|
|
716 b->pos = b->last;
|
|
717 r->headers_in.content_length_n -= size;
|
|
718 }
|
|
719 }
|
|
720
|
|
721 return NGX_OK;
|
|
722 }
|
|
723
|
|
724
|
|
725 static ngx_int_t
|
438
|
726 ngx_http_test_expect(ngx_http_request_t *r)
|
|
727 {
|
|
728 ngx_int_t n;
|
|
729 ngx_str_t *expect;
|
|
730
|
|
731 if (r->expect_tested
|
|
732 || r->headers_in.expect == NULL
|
|
733 || r->http_version < NGX_HTTP_VERSION_11)
|
|
734 {
|
|
735 return NGX_OK;
|
|
736 }
|
|
737
|
|
738 r->expect_tested = 1;
|
|
739
|
|
740 expect = &r->headers_in.expect->value;
|
|
741
|
|
742 if (expect->len != sizeof("100-continue") - 1
|
|
743 || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
|
|
744 sizeof("100-continue") - 1)
|
|
745 != 0)
|
|
746 {
|
|
747 return NGX_OK;
|
|
748 }
|
|
749
|
|
750 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
751 "send 100 Continue");
|
|
752
|
|
753 n = r->connection->send(r->connection,
|
|
754 (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
|
|
755 sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
|
|
756
|
|
757 if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
|
|
758 return NGX_OK;
|
|
759 }
|
|
760
|
|
761 /* we assume that such small packet should be send successfully */
|
|
762
|
|
763 return NGX_ERROR;
|
|
764 }
|
692
|
765
|
|
766
|
|
767 static ngx_int_t
|
|
768 ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
|
769 {
|
|
770 if (r->headers_in.chunked) {
|
|
771 return ngx_http_request_body_chunked_filter(r, in);
|
|
772
|
|
773 } else {
|
|
774 return ngx_http_request_body_length_filter(r, in);
|
|
775 }
|
|
776 }
|
|
777
|
|
778
|
|
779 static ngx_int_t
|
|
780 ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
|
781 {
|
|
782 size_t size;
|
|
783 ngx_int_t rc;
|
|
784 ngx_buf_t *b;
|
|
785 ngx_chain_t *cl, *tl, *out, **ll;
|
|
786 ngx_http_request_body_t *rb;
|
|
787
|
|
788 rb = r->request_body;
|
|
789
|
|
790 if (rb->rest == -1) {
|
|
791 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
792 "http request body content length filter");
|
|
793
|
|
794 rb->rest = r->headers_in.content_length_n;
|
|
795 }
|
|
796
|
|
797 out = NULL;
|
|
798 ll = &out;
|
|
799
|
|
800 for (cl = in; cl; cl = cl->next) {
|
|
801
|
|
802 tl = ngx_chain_get_free_buf(r->pool, &rb->free);
|
|
803 if (tl == NULL) {
|
|
804 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
805 }
|
|
806
|
|
807 b = tl->buf;
|
|
808
|
|
809 ngx_memzero(b, sizeof(ngx_buf_t));
|
|
810
|
|
811 b->temporary = 1;
|
|
812 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
|
|
813 b->start = cl->buf->start;
|
|
814 b->pos = cl->buf->pos;
|
|
815 b->last = cl->buf->last;
|
|
816 b->end = cl->buf->end;
|
|
817
|
|
818 size = cl->buf->last - cl->buf->pos;
|
|
819
|
|
820 if ((off_t) size < rb->rest) {
|
|
821 cl->buf->pos = cl->buf->last;
|
|
822 rb->rest -= size;
|
|
823
|
|
824 } else {
|
|
825 cl->buf->pos += rb->rest;
|
|
826 rb->rest = 0;
|
|
827 b->last = cl->buf->pos;
|
|
828 b->last_buf = 1;
|
|
829 }
|
|
830
|
|
831 *ll = tl;
|
|
832 ll = &tl->next;
|
|
833 }
|
|
834
|
|
835 rc = ngx_http_request_body_save_filter(r, out);
|
|
836
|
|
837 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
|
|
838 (ngx_buf_tag_t) &ngx_http_read_client_request_body);
|
|
839
|
|
840 return rc;
|
|
841 }
|
|
842
|
|
843
|
|
844 static ngx_int_t
|
|
845 ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
|
846 {
|
|
847 size_t size;
|
|
848 ngx_int_t rc;
|
|
849 ngx_buf_t *b;
|
|
850 ngx_chain_t *cl, *out, *tl, **ll;
|
|
851 ngx_http_request_body_t *rb;
|
|
852 ngx_http_core_loc_conf_t *clcf;
|
|
853
|
|
854 rb = r->request_body;
|
|
855
|
|
856 if (rb->rest == -1) {
|
|
857
|
|
858 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
|
|
859 "http request body chunked filter");
|
|
860
|
|
861 rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
|
|
862 if (rb->chunked == NULL) {
|
|
863 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
864 }
|
|
865
|
|
866 r->headers_in.content_length_n = 0;
|
|
867 rb->rest = 3;
|
|
868 }
|
|
869
|
|
870 out = NULL;
|
|
871 ll = &out;
|
|
872
|
|
873 for (cl = in; cl; cl = cl->next) {
|
|
874
|
|
875 for ( ;; ) {
|
|
876
|
|
877 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
|
|
878 "http body chunked buf "
|
|
879 "t:%d f:%d %p, pos %p, size: %z file: %O, size: %z",
|
|
880 cl->buf->temporary, cl->buf->in_file,
|
|
881 cl->buf->start, cl->buf->pos,
|
|
882 cl->buf->last - cl->buf->pos,
|
|
883 cl->buf->file_pos,
|
|
884 cl->buf->file_last - cl->buf->file_pos);
|
|
885
|
|
886 rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
|
|
887
|
|
888 if (rc == NGX_OK) {
|
|
889
|
|
890 /* a chunk has been parsed successfully */
|
|
891
|
|
892 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
|
|
893
|
|
894 if (clcf->client_max_body_size
|
|
895 && clcf->client_max_body_size
|
|
896 < r->headers_in.content_length_n + rb->chunked->size)
|
|
897 {
|
|
898 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
899 "client intended to send too large chunked "
|
|
900 "body: %O bytes",
|
|
901 r->headers_in.content_length_n
|
|
902 + rb->chunked->size);
|
|
903
|
|
904 r->lingering_close = 1;
|
|
905
|
|
906 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
|
|
907 }
|
|
908
|
|
909 tl = ngx_chain_get_free_buf(r->pool, &rb->free);
|
|
910 if (tl == NULL) {
|
|
911 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
912 }
|
|
913
|
|
914 b = tl->buf;
|
|
915
|
|
916 ngx_memzero(b, sizeof(ngx_buf_t));
|
|
917
|
|
918 b->temporary = 1;
|
|
919 b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
|
|
920 b->start = cl->buf->start;
|
|
921 b->pos = cl->buf->pos;
|
|
922 b->last = cl->buf->last;
|
|
923 b->end = cl->buf->end;
|
|
924
|
|
925 *ll = tl;
|
|
926 ll = &tl->next;
|
|
927
|
|
928 size = cl->buf->last - cl->buf->pos;
|
|
929
|
|
930 if ((off_t) size > rb->chunked->size) {
|
|
931 cl->buf->pos += rb->chunked->size;
|
|
932 r->headers_in.content_length_n += rb->chunked->size;
|
|
933 rb->chunked->size = 0;
|
|
934
|
|
935 } else {
|
|
936 rb->chunked->size -= size;
|
|
937 r->headers_in.content_length_n += size;
|
|
938 cl->buf->pos = cl->buf->last;
|
|
939 }
|
|
940
|
|
941 b->last = cl->buf->pos;
|
|
942
|
|
943 continue;
|
|
944 }
|
|
945
|
|
946 if (rc == NGX_DONE) {
|
|
947
|
|
948 /* a whole response has been parsed successfully */
|
|
949
|
|
950 rb->rest = 0;
|
|
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->last_buf = 1;
|
|
962
|
|
963 *ll = tl;
|
|
964 ll = &tl->next;
|
|
965
|
|
966 break;
|
|
967 }
|
|
968
|
|
969 if (rc == NGX_AGAIN) {
|
|
970
|
|
971 /* set rb->rest, amount of data we want to see next time */
|
|
972
|
|
973 rb->rest = rb->chunked->length;
|
|
974
|
|
975 break;
|
|
976 }
|
|
977
|
|
978 /* invalid */
|
|
979
|
|
980 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
|
|
981 "client sent invalid chunked body");
|
|
982
|
|
983 return NGX_HTTP_BAD_REQUEST;
|
|
984 }
|
|
985 }
|
|
986
|
|
987 rc = ngx_http_request_body_save_filter(r, out);
|
|
988
|
|
989 ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
|
|
990 (ngx_buf_tag_t) &ngx_http_read_client_request_body);
|
|
991
|
|
992 return rc;
|
|
993 }
|
|
994
|
|
995
|
|
996 static ngx_int_t
|
|
997 ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
|
|
998 {
|
|
999 #if (NGX_DEBUG)
|
|
1000 ngx_chain_t *cl;
|
|
1001 #endif
|
|
1002 ngx_http_request_body_t *rb;
|
|
1003
|
|
1004 rb = r->request_body;
|
|
1005
|
|
1006 #if (NGX_DEBUG)
|
|
1007
|
|
1008 for (cl = rb->bufs; cl; cl = cl->next) {
|
|
1009 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
|
|
1010 "http body old buf t:%d f:%d %p, pos %p, size: %z "
|
|
1011 "file: %O, size: %z",
|
|
1012 cl->buf->temporary, cl->buf->in_file,
|
|
1013 cl->buf->start, cl->buf->pos,
|
|
1014 cl->buf->last - cl->buf->pos,
|
|
1015 cl->buf->file_pos,
|
|
1016 cl->buf->file_last - cl->buf->file_pos);
|
|
1017 }
|
|
1018
|
|
1019 for (cl = in; cl; cl = cl->next) {
|
|
1020 ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
|
|
1021 "http body new buf t:%d f:%d %p, pos %p, size: %z "
|
|
1022 "file: %O, size: %z",
|
|
1023 cl->buf->temporary, cl->buf->in_file,
|
|
1024 cl->buf->start, cl->buf->pos,
|
|
1025 cl->buf->last - cl->buf->pos,
|
|
1026 cl->buf->file_pos,
|
|
1027 cl->buf->file_last - cl->buf->file_pos);
|
|
1028 }
|
|
1029
|
|
1030 #endif
|
|
1031
|
|
1032 /* TODO: coalesce neighbouring buffers */
|
|
1033
|
|
1034 if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
|
|
1035 return NGX_HTTP_INTERNAL_SERVER_ERROR;
|
|
1036 }
|
|
1037
|
|
1038 return NGX_OK;
|
|
1039 }
|