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