[nginx] FastCGI: fastcgi_request_buffering.

Maxim Dounin mdounin at mdounin.ru
Mon Mar 23 18:11:36 UTC 2015


details:   http://hg.nginx.org/nginx/rev/8ad78808a612
branches:  
changeset: 6052:8ad78808a612
user:      Maxim Dounin <mdounin at mdounin.ru>
date:      Mon Mar 23 21:09:19 2015 +0300
description:
FastCGI: fastcgi_request_buffering.

diffstat:

 src/http/modules/ngx_http_fastcgi_module.c |  362 +++++++++++++++++++++++++++-
 1 files changed, 343 insertions(+), 19 deletions(-)

diffs (truncated from 463 to 300 lines):

diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -81,8 +81,12 @@ typedef struct {
     size_t                         length;
     size_t                         padding;
 
+    ngx_chain_t                   *free;
+    ngx_chain_t                   *busy;
+
     unsigned                       fastcgi_stdout:1;
     unsigned                       large_stderr:1;
+    unsigned                       header_sent:1;
 
     ngx_array_t                   *split_parts;
 
@@ -147,6 +151,8 @@ static ngx_int_t ngx_http_fastcgi_create
 #endif
 static ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);
 static ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_fastcgi_body_output_filter(void *data,
+    ngx_chain_t *in);
 static ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);
 static ngx_int_t ngx_http_fastcgi_input_filter_init(void *data);
 static ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p,
@@ -257,6 +263,13 @@ static ngx_command_t  ngx_http_fastcgi_c
       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffering),
       NULL },
 
+    { ngx_string("fastcgi_request_buffering"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.request_buffering),
+      NULL },
+
     { ngx_string("fastcgi_ignore_client_abort"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
@@ -703,6 +716,12 @@ ngx_http_fastcgi_handler(ngx_http_reques
     u->input_filter = ngx_http_fastcgi_non_buffered_filter;
     u->input_filter_ctx = r;
 
+    if (!flcf->upstream.request_buffering
+        && flcf->upstream.pass_request_body)
+    {
+        r->request_body_no_buffering = 1;
+    }
+
     rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
 
     if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
@@ -799,6 +818,7 @@ ngx_http_fastcgi_create_request(ngx_http
     ngx_chain_t                  *cl, *body;
     ngx_list_part_t              *part;
     ngx_table_elt_t              *header, **ignored;
+    ngx_http_upstream_t          *u;
     ngx_http_script_code_pt       code;
     ngx_http_script_engine_t      e, le;
     ngx_http_fastcgi_header_t    *h;
@@ -810,10 +830,12 @@ ngx_http_fastcgi_create_request(ngx_http
     header_params = 0;
     ignored = NULL;
 
+    u = r->upstream;
+
     flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
 
 #if (NGX_HTTP_CACHE)
-    params = r->upstream->cacheable ? &flcf->params_cache : &flcf->params;
+    params = u->cacheable ? &flcf->params_cache : &flcf->params;
 #else
     params = &flcf->params;
 #endif
@@ -1134,12 +1156,17 @@ ngx_http_fastcgi_create_request(ngx_http
     h->padding_length = 0;
     h->reserved = 0;
 
-    h = (ngx_http_fastcgi_header_t *) b->last;
-    b->last += sizeof(ngx_http_fastcgi_header_t);
-
-    if (flcf->upstream.pass_request_body) {
-        body = r->upstream->request_bufs;
-        r->upstream->request_bufs = cl;
+    if (r->request_body_no_buffering) {
+
+        u->request_bufs = cl;
+
+        u->output.output_filter = ngx_http_fastcgi_body_output_filter;
+        u->output.filter_ctx = r;
+
+    } else if (flcf->upstream.pass_request_body) {
+
+        body = u->request_bufs;
+        u->request_bufs = cl;
 
 #if (NGX_SUPPRESS_WARN)
         file_pos = 0;
@@ -1194,6 +1221,9 @@ ngx_http_fastcgi_create_request(ngx_http
                 padding = 8 - len % 8;
                 padding = (padding == 8) ? 0 : padding;
 
+                h = (ngx_http_fastcgi_header_t *) cl->buf->last;
+                cl->buf->last += sizeof(ngx_http_fastcgi_header_t);
+
                 h->version = 1;
                 h->type = NGX_HTTP_FASTCGI_STDIN;
                 h->request_id_hi = 0;
@@ -1223,9 +1253,6 @@ ngx_http_fastcgi_create_request(ngx_http
                     b->last += padding;
                 }
 
-                h = (ngx_http_fastcgi_header_t *) b->last;
-                b->last += sizeof(ngx_http_fastcgi_header_t);
-
                 cl->next = ngx_alloc_chain_link(r->pool);
                 if (cl->next == NULL) {
                     return NGX_ERROR;
@@ -1240,17 +1267,22 @@ ngx_http_fastcgi_create_request(ngx_http
         }
 
     } else {
-        r->upstream->request_bufs = cl;
+        u->request_bufs = cl;
     }
 
-    h->version = 1;
-    h->type = NGX_HTTP_FASTCGI_STDIN;
-    h->request_id_hi = 0;
-    h->request_id_lo = 1;
-    h->content_length_hi = 0;
-    h->content_length_lo = 0;
-    h->padding_length = 0;
-    h->reserved = 0;
+    if (!r->request_body_no_buffering) {
+        h = (ngx_http_fastcgi_header_t *) cl->buf->last;
+        cl->buf->last += sizeof(ngx_http_fastcgi_header_t);
+
+        h->version = 1;
+        h->type = NGX_HTTP_FASTCGI_STDIN;
+        h->request_id_hi = 0;
+        h->request_id_lo = 1;
+        h->content_length_hi = 0;
+        h->content_length_lo = 0;
+        h->padding_length = 0;
+        h->reserved = 0;
+    }
 
     cl->next = NULL;
 
@@ -1284,6 +1316,294 @@ ngx_http_fastcgi_reinit_request(ngx_http
 
 
 static ngx_int_t
+ngx_http_fastcgi_body_output_filter(void *data, ngx_chain_t *in)
+{
+    ngx_http_request_t  *r = data;
+
+    off_t                       file_pos;
+    u_char                     *pos, *start;
+    size_t                      len, padding;
+    ngx_buf_t                  *b;
+    ngx_int_t                   rc;
+    ngx_uint_t                  next, last;
+    ngx_chain_t                *cl, *tl, *out, **ll;
+    ngx_http_fastcgi_ctx_t     *f;
+    ngx_http_fastcgi_header_t  *h;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "fastcgi output filter");
+
+    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);
+
+    if (in == NULL) {
+        out = in;
+        goto out;
+    }
+
+    out = NULL;
+    ll = &out;
+
+    if (!f->header_sent) {
+        /* first buffer contains headers, pass it unmodified */
+
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "fastcgi output header");
+
+        f->header_sent = 1;
+
+        tl = ngx_alloc_chain_link(r->pool);
+        if (tl == NULL) {
+            return NGX_ERROR;
+        }
+
+        tl->buf = in->buf;
+        *ll = tl;
+        ll = &tl->next;
+
+        in = in->next;
+
+        if (in == NULL) {
+            tl->next = NULL;
+            goto out;
+        }
+    }
+
+    cl = ngx_chain_get_free_buf(r->pool, &f->free);
+    if (cl == NULL) {
+        return NGX_ERROR;
+    }
+
+    b = cl->buf;
+
+    b->tag = (ngx_buf_tag_t) &ngx_http_fastcgi_body_output_filter;
+    b->temporary = 1;
+
+    if (b->start == NULL) {
+        /* reserve space for maximum possible padding, 7 bytes */
+
+        b->start = ngx_palloc(r->pool,
+                              sizeof(ngx_http_fastcgi_header_t) + 7);
+        if (b->start == NULL) {
+            return NGX_ERROR;
+        }
+
+        b->pos = b->start;
+        b->last = b->start;
+
+        b->end = b->start + sizeof(ngx_http_fastcgi_header_t) + 7;
+    }
+
+    *ll = cl;
+
+    last = 0;
+    padding = 0;
+
+#if (NGX_SUPPRESS_WARN)
+    file_pos = 0;
+    pos = NULL;
+#endif
+
+    while (in) {
+
+        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,
+                       "fastcgi output in  l:%d f:%d %p, pos %p, size: %z "
+                       "file: %O, size: %O",
+                       in->buf->last_buf,
+                       in->buf->in_file,
+                       in->buf->start, in->buf->pos,
+                       in->buf->last - in->buf->pos,
+                       in->buf->file_pos,
+                       in->buf->file_last - in->buf->file_pos);
+
+        if (in->buf->last_buf) {
+            last = 1;
+        }
+
+        if (ngx_buf_special(in->buf)) {
+            in = in->next;
+            continue;
+        }
+
+        if (in->buf->in_file) {
+            file_pos = in->buf->file_pos;
+
+        } else {
+            pos = in->buf->pos;
+        }
+
+        next = 0;
+
+        do {
+            tl = ngx_chain_get_free_buf(r->pool, &f->free);
+            if (tl == NULL) {
+                return NGX_ERROR;
+            }
+
+            b = tl->buf;
+            start = b->start;
+
+            ngx_memcpy(b, in->buf, sizeof(ngx_buf_t));
+
+            /*
+             * restore b->start to preserve memory allocated in the buffer,
+             * to reuse it later for headers and padding
+             */
+
+            b->start = start;
+
+            if (in->buf->in_file) {
+                b->file_pos = file_pos;
+                file_pos += 32 * 1024;
+
+                if (file_pos >= in->buf->file_last) {
+                    file_pos = in->buf->file_last;
+                    next = 1;
+                }
+
+                b->file_last = file_pos;
+                len = (ngx_uint_t) (file_pos - b->file_pos);



More information about the nginx-devel mailing list