Coverage Report

Created: 2026-08-14 06:54

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