Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

swh:1:snp:158a3f36b0bd3da461fb7458de44cfa2c94e4270
  • Code
  • Branches (0)
  • Releases (20)
    • Branches
    • Releases
      • 1.18.0-6ubuntu14.1
      • 1.18.0-6ubuntu14
      • 1.18.0-6ubuntu11.1
      • 1.18.0-6ubuntu11
      • 1.18.0-6ubuntu8.2
      • 1.18.0-6ubuntu8
      • 1.18.0-0ubuntu1.3
      • 1.17.10-0ubuntu1
      • 1.14.0-0ubuntu1.10
      • 1.10.3-0ubuntu0.16.04.5
      • 1.9.15-0ubuntu1
      • 1.4.6-1ubuntu3.9
      • 1.4.6-1ubuntu3
    • 3f4fece
    • /
    • src
    • /
    • http
    • /
    • ngx_http_request_body.c
    Raw File Download

    To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
    Select below a type of object currently browsed in order to display its associated SWHID and permalink.

    • content
    • directory
    • snapshot
    • release
    content badge
    swh:1:cnt:0641329c532e063b72f015186af54de5f9f2078c
    directory badge
    swh:1:dir:ac40a315942202dc971527da0ce196b30aaa57b6
    snapshot badge
    swh:1:snp:158a3f36b0bd3da461fb7458de44cfa2c94e4270
    release badge
    swh:1:rel:768dfd8ff182940cf6903371cdbfcde764231226

    This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
    Select below a type of object currently browsed in order to generate citations for them.

    • content
    • directory
    • snapshot
    • release
    Generate software citation in BibTex format (requires biblatex-software package)
    Generating citation ...
    Generate software citation in BibTex format (requires biblatex-software package)
    Generating citation ...
    Generate software citation in BibTex format (requires biblatex-software package)
    Generating citation ...
    Generate software citation in BibTex format (requires biblatex-software package)
    Generating citation ...
    ngx_http_request_body.c
    
    /*
     * Copyright (C) Igor Sysoev
     * Copyright (C) Nginx, Inc.
     */
    
    
    #include <ngx_config.h>
    #include <ngx_core.h>
    #include <ngx_http.h>
    
    
    static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
    static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
    static ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);
    static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);
    static ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,
        ngx_buf_t *b);
    static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);
    
    static ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,
        ngx_chain_t *in);
    static ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,
        ngx_chain_t *in);
    static ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,
        ngx_chain_t *in);
    
    
    ngx_int_t
    ngx_http_read_client_request_body(ngx_http_request_t *r,
        ngx_http_client_body_handler_pt post_handler)
    {
        size_t                     preread;
        ssize_t                    size;
        ngx_int_t                  rc;
        ngx_buf_t                 *b;
        ngx_chain_t                out;
        ngx_http_request_body_t   *rb;
        ngx_http_core_loc_conf_t  *clcf;
    
        r->main->count++;
    
        if (r != r->main || r->request_body || r->discard_body) {
            r->request_body_no_buffering = 0;
            post_handler(r);
            return NGX_OK;
        }
    
    #if (NGX_HTTP_V2)
        if (r->stream) {
            rc = ngx_http_v2_read_request_body(r, post_handler);
            goto done;
        }
    #endif
    
        if (ngx_http_test_expect(r) != NGX_OK) {
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
            goto done;
        }
    
        rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
        if (rb == NULL) {
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
            goto done;
        }
    
        /*
         * set by ngx_pcalloc():
         *
         *     rb->bufs = NULL;
         *     rb->buf = NULL;
         *     rb->free = NULL;
         *     rb->busy = NULL;
         *     rb->chunked = NULL;
         */
    
        rb->rest = -1;
        rb->post_handler = post_handler;
    
        r->request_body = rb;
    
        if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {
            r->request_body_no_buffering = 0;
            post_handler(r);
            return NGX_OK;
        }
    
        preread = r->header_in->last - r->header_in->pos;
    
        if (preread) {
    
            /* there is the pre-read part of the request body */
    
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http client request body preread %uz", preread);
    
            out.buf = r->header_in;
            out.next = NULL;
    
            rc = ngx_http_request_body_filter(r, &out);
    
            if (rc != NGX_OK) {
                goto done;
            }
    
            r->request_length += preread - (r->header_in->last - r->header_in->pos);
    
            if (!r->headers_in.chunked
                && rb->rest > 0
                && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))
            {
                /* the whole request body may be placed in r->header_in */
    
                b = ngx_calloc_buf(r->pool);
                if (b == NULL) {
                    rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
                    goto done;
                }
    
                b->temporary = 1;
                b->start = r->header_in->pos;
                b->pos = r->header_in->pos;
                b->last = r->header_in->last;
                b->end = r->header_in->end;
    
                rb->buf = b;
    
                r->read_event_handler = ngx_http_read_client_request_body_handler;
                r->write_event_handler = ngx_http_request_empty_handler;
    
                rc = ngx_http_do_read_client_request_body(r);
                goto done;
            }
    
        } else {
            /* set rb->rest */
    
            if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
                goto done;
            }
        }
    
        if (rb->rest == 0) {
            /* the whole request body was pre-read */
            r->request_body_no_buffering = 0;
            post_handler(r);
            return NGX_OK;
        }
    
        if (rb->rest < 0) {
            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                          "negative request body rest");
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
            goto done;
        }
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        size = clcf->client_body_buffer_size;
        size += size >> 2;
    
        /* TODO: honor r->request_body_in_single_buf */
    
        if (!r->headers_in.chunked && rb->rest < size) {
            size = (ssize_t) rb->rest;
    
            if (r->request_body_in_single_buf) {
                size += preread;
            }
    
        } else {
            size = clcf->client_body_buffer_size;
        }
    
        rb->buf = ngx_create_temp_buf(r->pool, size);
        if (rb->buf == NULL) {
            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
            goto done;
        }
    
        r->read_event_handler = ngx_http_read_client_request_body_handler;
        r->write_event_handler = ngx_http_request_empty_handler;
    
        rc = ngx_http_do_read_client_request_body(r);
    
    done:
    
        if (r->request_body_no_buffering
            && (rc == NGX_OK || rc == NGX_AGAIN))
        {
            if (rc == NGX_OK) {
                r->request_body_no_buffering = 0;
    
            } else {
                /* rc == NGX_AGAIN */
                r->reading_body = 1;
            }
    
            r->read_event_handler = ngx_http_block_reading;
            post_handler(r);
        }
    
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            r->main->count--;
        }
    
        return rc;
    }
    
    
    ngx_int_t
    ngx_http_read_unbuffered_request_body(ngx_http_request_t *r)
    {
        ngx_int_t  rc;
    
    #if (NGX_HTTP_V2)
        if (r->stream) {
            rc = ngx_http_v2_read_unbuffered_request_body(r);
    
            if (rc == NGX_OK) {
                r->reading_body = 0;
            }
    
            return rc;
        }
    #endif
    
        if (r->connection->read->timedout) {
            r->connection->timedout = 1;
            return NGX_HTTP_REQUEST_TIME_OUT;
        }
    
        rc = ngx_http_do_read_client_request_body(r);
    
        if (rc == NGX_OK) {
            r->reading_body = 0;
        }
    
        return rc;
    }
    
    
    static void
    ngx_http_read_client_request_body_handler(ngx_http_request_t *r)
    {
        ngx_int_t  rc;
    
        if (r->connection->read->timedout) {
            r->connection->timedout = 1;
            ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
            return;
        }
    
        rc = ngx_http_do_read_client_request_body(r);
    
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            ngx_http_finalize_request(r, rc);
        }
    }
    
    
    static ngx_int_t
    ngx_http_do_read_client_request_body(ngx_http_request_t *r)
    {
        off_t                      rest;
        size_t                     size;
        ssize_t                    n;
        ngx_int_t                  rc;
        ngx_chain_t                out;
        ngx_connection_t          *c;
        ngx_http_request_body_t   *rb;
        ngx_http_core_loc_conf_t  *clcf;
    
        c = r->connection;
        rb = r->request_body;
    
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http read client request body");
    
        for ( ;; ) {
            for ( ;; ) {
                if (rb->buf->last == rb->buf->end) {
    
                    if (rb->buf->pos != rb->buf->last) {
    
                        /* pass buffer to request body filter chain */
    
                        out.buf = rb->buf;
                        out.next = NULL;
    
                        rc = ngx_http_request_body_filter(r, &out);
    
                        if (rc != NGX_OK) {
                            return rc;
                        }
    
                    } else {
    
                        /* update chains */
    
                        rc = ngx_http_request_body_filter(r, NULL);
    
                        if (rc != NGX_OK) {
                            return rc;
                        }
                    }
    
                    if (rb->busy != NULL) {
                        if (r->request_body_no_buffering) {
                            if (c->read->timer_set) {
                                ngx_del_timer(c->read);
                            }
    
                            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
                                return NGX_HTTP_INTERNAL_SERVER_ERROR;
                            }
    
                            return NGX_AGAIN;
                        }
    
                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
                    }
    
                    rb->buf->pos = rb->buf->start;
                    rb->buf->last = rb->buf->start;
                }
    
                size = rb->buf->end - rb->buf->last;
                rest = rb->rest - (rb->buf->last - rb->buf->pos);
    
                if ((off_t) size > rest) {
                    size = (size_t) rest;
                }
    
                n = c->recv(c, rb->buf->last, size);
    
                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                               "http client request body recv %z", n);
    
                if (n == NGX_AGAIN) {
                    break;
                }
    
                if (n == 0) {
                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                  "client prematurely closed connection");
                }
    
                if (n == 0 || n == NGX_ERROR) {
                    c->error = 1;
                    return NGX_HTTP_BAD_REQUEST;
                }
    
                rb->buf->last += n;
                r->request_length += n;
    
                if (n == rest) {
                    /* pass buffer to request body filter chain */
    
                    out.buf = rb->buf;
                    out.next = NULL;
    
                    rc = ngx_http_request_body_filter(r, &out);
    
                    if (rc != NGX_OK) {
                        return rc;
                    }
                }
    
                if (rb->rest == 0) {
                    break;
                }
    
                if (rb->buf->last < rb->buf->end) {
                    break;
                }
            }
    
            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                           "http client request body rest %O", rb->rest);
    
            if (rb->rest == 0) {
                break;
            }
    
            if (!c->read->ready) {
    
                if (r->request_body_no_buffering
                    && rb->buf->pos != rb->buf->last)
                {
                    /* pass buffer to request body filter chain */
    
                    out.buf = rb->buf;
                    out.next = NULL;
    
                    rc = ngx_http_request_body_filter(r, &out);
    
                    if (rc != NGX_OK) {
                        return rc;
                    }
                }
    
                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
                ngx_add_timer(c->read, clcf->client_body_timeout);
    
                if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }
    
                return NGX_AGAIN;
            }
        }
    
        if (c->read->timer_set) {
            ngx_del_timer(c->read);
        }
    
        if (!r->request_body_no_buffering) {
            r->read_event_handler = ngx_http_block_reading;
            rb->post_handler(r);
        }
    
        return NGX_OK;
    }
    
    
    static ngx_int_t
    ngx_http_write_request_body(ngx_http_request_t *r)
    {
        ssize_t                    n;
        ngx_chain_t               *cl, *ln;
        ngx_temp_file_t           *tf;
        ngx_http_request_body_t   *rb;
        ngx_http_core_loc_conf_t  *clcf;
    
        rb = r->request_body;
    
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http write client request body, bufs %p", rb->bufs);
    
        if (rb->temp_file == NULL) {
            tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));
            if (tf == NULL) {
                return NGX_ERROR;
            }
    
            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
            tf->file.fd = NGX_INVALID_FILE;
            tf->file.log = r->connection->log;
            tf->path = clcf->client_body_temp_path;
            tf->pool = r->pool;
            tf->warn = "a client request body is buffered to a temporary file";
            tf->log_level = r->request_body_file_log_level;
            tf->persistent = r->request_body_in_persistent_file;
            tf->clean = r->request_body_in_clean_file;
    
            if (r->request_body_file_group_access) {
                tf->access = 0660;
            }
    
            rb->temp_file = tf;
    
            if (rb->bufs == NULL) {
                /* empty body with r->request_body_in_file_only */
    
                if (ngx_create_temp_file(&tf->file, tf->path, tf->pool,
                                         tf->persistent, tf->clean, tf->access)
                    != NGX_OK)
                {
                    return NGX_ERROR;
                }
    
                return NGX_OK;
            }
        }
    
        if (rb->bufs == NULL) {
            return NGX_OK;
        }
    
        n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);
    
        /* TODO: n == 0 or not complete and level event */
    
        if (n == NGX_ERROR) {
            return NGX_ERROR;
        }
    
        rb->temp_file->offset += n;
    
        /* mark all buffers as written */
    
        for (cl = rb->bufs; cl; /* void */) {
    
            cl->buf->pos = cl->buf->last;
    
            ln = cl;
            cl = cl->next;
            ngx_free_chain(r->pool, ln);
        }
    
        rb->bufs = NULL;
    
        return NGX_OK;
    }
    
    
    ngx_int_t
    ngx_http_discard_request_body(ngx_http_request_t *r)
    {
        ssize_t       size;
        ngx_int_t     rc;
        ngx_event_t  *rev;
    
        if (r != r->main || r->discard_body || r->request_body) {
            return NGX_OK;
        }
    
    #if (NGX_HTTP_V2)
        if (r->stream) {
            r->stream->skip_data = 1;
            return NGX_OK;
        }
    #endif
    
        if (ngx_http_test_expect(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        rev = r->connection->read;
    
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");
    
        if (rev->timer_set) {
            ngx_del_timer(rev);
        }
    
        if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
            return NGX_OK;
        }
    
        size = r->header_in->last - r->header_in->pos;
    
        if (size || r->headers_in.chunked) {
            rc = ngx_http_discard_request_body_filter(r, r->header_in);
    
            if (rc != NGX_OK) {
                return rc;
            }
    
            if (r->headers_in.content_length_n == 0) {
                return NGX_OK;
            }
        }
    
        rc = ngx_http_read_discarded_request_body(r);
    
        if (rc == NGX_OK) {
            r->lingering_close = 0;
            return NGX_OK;
        }
    
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            return rc;
        }
    
        /* rc == NGX_AGAIN */
    
        r->read_event_handler = ngx_http_discarded_request_body_handler;
    
        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        r->count++;
        r->discard_body = 1;
    
        return NGX_OK;
    }
    
    
    void
    ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
    {
        ngx_int_t                  rc;
        ngx_msec_t                 timer;
        ngx_event_t               *rev;
        ngx_connection_t          *c;
        ngx_http_core_loc_conf_t  *clcf;
    
        c = r->connection;
        rev = c->read;
    
        if (rev->timedout) {
            c->timedout = 1;
            c->error = 1;
            ngx_http_finalize_request(r, NGX_ERROR);
            return;
        }
    
        if (r->lingering_time) {
            timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time();
    
            if ((ngx_msec_int_t) timer <= 0) {
                r->discard_body = 0;
                r->lingering_close = 0;
                ngx_http_finalize_request(r, NGX_ERROR);
                return;
            }
    
        } else {
            timer = 0;
        }
    
        rc = ngx_http_read_discarded_request_body(r);
    
        if (rc == NGX_OK) {
            r->discard_body = 0;
            r->lingering_close = 0;
            ngx_http_finalize_request(r, NGX_DONE);
            return;
        }
    
        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
            c->error = 1;
            ngx_http_finalize_request(r, NGX_ERROR);
            return;
        }
    
        /* rc == NGX_AGAIN */
    
        if (ngx_handle_read_event(rev, 0) != NGX_OK) {
            c->error = 1;
            ngx_http_finalize_request(r, NGX_ERROR);
            return;
        }
    
        if (timer) {
    
            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
            timer *= 1000;
    
            if (timer > clcf->lingering_timeout) {
                timer = clcf->lingering_timeout;
            }
    
            ngx_add_timer(rev, timer);
        }
    }
    
    
    static ngx_int_t
    ngx_http_read_discarded_request_body(ngx_http_request_t *r)
    {
        size_t     size;
        ssize_t    n;
        ngx_int_t  rc;
        ngx_buf_t  b;
        u_char     buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];
    
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "http read discarded body");
    
        ngx_memzero(&b, sizeof(ngx_buf_t));
    
        b.temporary = 1;
    
        for ( ;; ) {
            if (r->headers_in.content_length_n == 0) {
                r->read_event_handler = ngx_http_block_reading;
                return NGX_OK;
            }
    
            if (!r->connection->read->ready) {
                return NGX_AGAIN;
            }
    
            size = (size_t) ngx_min(r->headers_in.content_length_n,
                                    NGX_HTTP_DISCARD_BUFFER_SIZE);
    
            n = r->connection->recv(r->connection, buffer, size);
    
            if (n == NGX_ERROR) {
                r->connection->error = 1;
                return NGX_OK;
            }
    
            if (n == NGX_AGAIN) {
                return NGX_AGAIN;
            }
    
            if (n == 0) {
                return NGX_OK;
            }
    
            b.pos = buffer;
            b.last = buffer + n;
    
            rc = ngx_http_discard_request_body_filter(r, &b);
    
            if (rc != NGX_OK) {
                return rc;
            }
        }
    }
    
    
    static ngx_int_t
    ngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)
    {
        size_t                    size;
        ngx_int_t                 rc;
        ngx_http_request_body_t  *rb;
    
        if (r->headers_in.chunked) {
    
            rb = r->request_body;
    
            if (rb == NULL) {
    
                rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
                if (rb == NULL) {
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }
    
                rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
                if (rb->chunked == NULL) {
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }
    
                r->request_body = rb;
            }
    
            for ( ;; ) {
    
                rc = ngx_http_parse_chunked(r, b, rb->chunked);
    
                if (rc == NGX_OK) {
    
                    /* a chunk has been parsed successfully */
    
                    size = b->last - b->pos;
    
                    if ((off_t) size > rb->chunked->size) {
                        b->pos += (size_t) rb->chunked->size;
                        rb->chunked->size = 0;
    
                    } else {
                        rb->chunked->size -= size;
                        b->pos = b->last;
                    }
    
                    continue;
                }
    
                if (rc == NGX_DONE) {
    
                    /* a whole response has been parsed successfully */
    
                    r->headers_in.content_length_n = 0;
                    break;
                }
    
                if (rc == NGX_AGAIN) {
    
                    /* set amount of data we want to see next time */
    
                    r->headers_in.content_length_n = rb->chunked->length;
                    break;
                }
    
                /* invalid */
    
                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                              "client sent invalid chunked body");
    
                return NGX_HTTP_BAD_REQUEST;
            }
    
        } else {
            size = b->last - b->pos;
    
            if ((off_t) size > r->headers_in.content_length_n) {
                b->pos += (size_t) r->headers_in.content_length_n;
                r->headers_in.content_length_n = 0;
    
            } else {
                b->pos = b->last;
                r->headers_in.content_length_n -= size;
            }
        }
    
        return NGX_OK;
    }
    
    
    static ngx_int_t
    ngx_http_test_expect(ngx_http_request_t *r)
    {
        ngx_int_t   n;
        ngx_str_t  *expect;
    
        if (r->expect_tested
            || r->headers_in.expect == NULL
            || r->http_version < NGX_HTTP_VERSION_11)
        {
            return NGX_OK;
        }
    
        r->expect_tested = 1;
    
        expect = &r->headers_in.expect->value;
    
        if (expect->len != sizeof("100-continue") - 1
            || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
                               sizeof("100-continue") - 1)
               != 0)
        {
            return NGX_OK;
        }
    
        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "send 100 Continue");
    
        n = r->connection->send(r->connection,
                                (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
                                sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
    
        if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
            return NGX_OK;
        }
    
        /* we assume that such small packet should be send successfully */
    
        return NGX_ERROR;
    }
    
    
    static ngx_int_t
    ngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        if (r->headers_in.chunked) {
            return ngx_http_request_body_chunked_filter(r, in);
    
        } else {
            return ngx_http_request_body_length_filter(r, in);
        }
    }
    
    
    static ngx_int_t
    ngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        size_t                     size;
        ngx_int_t                  rc;
        ngx_buf_t                 *b;
        ngx_chain_t               *cl, *tl, *out, **ll;
        ngx_http_request_body_t   *rb;
    
        rb = r->request_body;
    
        if (rb->rest == -1) {
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http request body content length filter");
    
            rb->rest = r->headers_in.content_length_n;
        }
    
        out = NULL;
        ll = &out;
    
        for (cl = in; cl; cl = cl->next) {
    
            if (rb->rest == 0) {
                break;
            }
    
            tl = ngx_chain_get_free_buf(r->pool, &rb->free);
            if (tl == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
    
            b = tl->buf;
    
            ngx_memzero(b, sizeof(ngx_buf_t));
    
            b->temporary = 1;
            b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
            b->start = cl->buf->pos;
            b->pos = cl->buf->pos;
            b->last = cl->buf->last;
            b->end = cl->buf->end;
            b->flush = r->request_body_no_buffering;
    
            size = cl->buf->last - cl->buf->pos;
    
            if ((off_t) size < rb->rest) {
                cl->buf->pos = cl->buf->last;
                rb->rest -= size;
    
            } else {
                cl->buf->pos += (size_t) rb->rest;
                rb->rest = 0;
                b->last = cl->buf->pos;
                b->last_buf = 1;
            }
    
            *ll = tl;
            ll = &tl->next;
        }
    
        rc = ngx_http_top_request_body_filter(r, out);
    
        ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
                                (ngx_buf_tag_t) &ngx_http_read_client_request_body);
    
        return rc;
    }
    
    
    static ngx_int_t
    ngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        size_t                     size;
        ngx_int_t                  rc;
        ngx_buf_t                 *b;
        ngx_chain_t               *cl, *out, *tl, **ll;
        ngx_http_request_body_t   *rb;
        ngx_http_core_loc_conf_t  *clcf;
    
        rb = r->request_body;
    
        if (rb->rest == -1) {
    
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http request body chunked filter");
    
            rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));
            if (rb->chunked == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
    
            r->headers_in.content_length_n = 0;
            rb->rest = 3;
        }
    
        out = NULL;
        ll = &out;
    
        for (cl = in; cl; cl = cl->next) {
    
            for ( ;; ) {
    
                ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                               "http body chunked buf "
                               "t:%d f:%d %p, pos %p, size: %z file: %O, size: %O",
                               cl->buf->temporary, cl->buf->in_file,
                               cl->buf->start, cl->buf->pos,
                               cl->buf->last - cl->buf->pos,
                               cl->buf->file_pos,
                               cl->buf->file_last - cl->buf->file_pos);
    
                rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);
    
                if (rc == NGX_OK) {
    
                    /* a chunk has been parsed successfully */
    
                    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
                    if (clcf->client_max_body_size
                        && clcf->client_max_body_size
                           - r->headers_in.content_length_n < rb->chunked->size)
                    {
                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                                      "client intended to send too large chunked "
                                      "body: %O+%O bytes",
                                      r->headers_in.content_length_n,
                                      rb->chunked->size);
    
                        r->lingering_close = 1;
    
                        return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
                    }
    
                    tl = ngx_chain_get_free_buf(r->pool, &rb->free);
                    if (tl == NULL) {
                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
                    }
    
                    b = tl->buf;
    
                    ngx_memzero(b, sizeof(ngx_buf_t));
    
                    b->temporary = 1;
                    b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;
                    b->start = cl->buf->pos;
                    b->pos = cl->buf->pos;
                    b->last = cl->buf->last;
                    b->end = cl->buf->end;
                    b->flush = r->request_body_no_buffering;
    
                    *ll = tl;
                    ll = &tl->next;
    
                    size = cl->buf->last - cl->buf->pos;
    
                    if ((off_t) size > rb->chunked->size) {
                        cl->buf->pos += (size_t) rb->chunked->size;
                        r->headers_in.content_length_n += rb->chunked->size;
                        rb->chunked->size = 0;
    
                    } else {
                        rb->chunked->size -= size;
                        r->headers_in.content_length_n += size;
                        cl->buf->pos = cl->buf->last;
                    }
    
                    b->last = cl->buf->pos;
    
                    continue;
                }
    
                if (rc == NGX_DONE) {
    
                    /* a whole response has been parsed successfully */
    
                    rb->rest = 0;
    
                    tl = ngx_chain_get_free_buf(r->pool, &rb->free);
                    if (tl == NULL) {
                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
                    }
    
                    b = tl->buf;
    
                    ngx_memzero(b, sizeof(ngx_buf_t));
    
                    b->last_buf = 1;
    
                    *ll = tl;
                    ll = &tl->next;
    
                    break;
                }
    
                if (rc == NGX_AGAIN) {
    
                    /* set rb->rest, amount of data we want to see next time */
    
                    rb->rest = rb->chunked->length;
    
                    break;
                }
    
                /* invalid */
    
                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                              "client sent invalid chunked body");
    
                return NGX_HTTP_BAD_REQUEST;
            }
        }
    
        rc = ngx_http_top_request_body_filter(r, out);
    
        ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,
                                (ngx_buf_tag_t) &ngx_http_read_client_request_body);
    
        return rc;
    }
    
    
    ngx_int_t
    ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        ngx_buf_t                 *b;
        ngx_chain_t               *cl;
        ngx_http_request_body_t   *rb;
    
        rb = r->request_body;
    
    #if (NGX_DEBUG)
    
        for (cl = rb->bufs; cl; cl = cl->next) {
            ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                           "http body old buf t:%d f:%d %p, pos %p, size: %z "
                           "file: %O, size: %O",
                           cl->buf->temporary, cl->buf->in_file,
                           cl->buf->start, cl->buf->pos,
                           cl->buf->last - cl->buf->pos,
                           cl->buf->file_pos,
                           cl->buf->file_last - cl->buf->file_pos);
        }
    
        for (cl = in; cl; cl = cl->next) {
            ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
                           "http body new buf t:%d f:%d %p, pos %p, size: %z "
                           "file: %O, size: %O",
                           cl->buf->temporary, cl->buf->in_file,
                           cl->buf->start, cl->buf->pos,
                           cl->buf->last - cl->buf->pos,
                           cl->buf->file_pos,
                           cl->buf->file_last - cl->buf->file_pos);
        }
    
    #endif
    
        /* TODO: coalesce neighbouring buffers */
    
        if (ngx_chain_add_copy(r->pool, &rb->bufs, in) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        if (r->request_body_no_buffering) {
            return NGX_OK;
        }
    
        if (rb->rest > 0) {
    
            if (rb->buf && rb->buf->last == rb->buf->end
                && ngx_http_write_request_body(r) != NGX_OK)
            {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
    
            return NGX_OK;
        }
    
        /* rb->rest == 0 */
    
        if (rb->temp_file || r->request_body_in_file_only) {
    
            if (ngx_http_write_request_body(r) != NGX_OK) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
    
            if (rb->temp_file->file.offset != 0) {
    
                cl = ngx_chain_get_free_buf(r->pool, &rb->free);
                if (cl == NULL) {
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }
    
                b = cl->buf;
    
                ngx_memzero(b, sizeof(ngx_buf_t));
    
                b->in_file = 1;
                b->file_last = rb->temp_file->file.offset;
                b->file = &rb->temp_file->file;
    
                rb->bufs = cl;
            }
        }
    
        return NGX_OK;
    }
    

    back to top

    Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
    The source code of Software Heritage itself is available on our development forge.
    The source code files archived by Software Heritage are available under their own copyright and licenses.
    Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API