[PATCH 4 of 4] HTTP/2: support for unbuffered upload of request body

Valentin V. Bartenev vbart at nginx.com
Fri Feb 26 15:45:59 UTC 2016


 src/http/ngx_http_request_body.c |    7 +-
 src/http/v2/ngx_http_v2.c        |  132 +++++++++++++++++++++++++++++++++++++-
 src/http/v2/ngx_http_v2.h        |    1 +
 3 files changed, 134 insertions(+), 6 deletions(-)


# HG changeset patch
# User Valentin Bartenev <vbart at nginx.com>
# Date 1456500171 -10800
#      Fri Feb 26 18:22:51 2016 +0300
# Node ID d0745c528544d1297ba11cc8d3660929a2bce557
# Parent  8933cfd2fb603a65f5ed8b49922abc9b03840375
HTTP/2: support for unbuffered upload of request body.

diff -r 8933cfd2fb60 -r d0745c528544 src/http/ngx_http_request_body.c
--- a/src/http/ngx_http_request_body.c	Fri Feb 26 18:22:26 2016 +0300
+++ b/src/http/ngx_http_request_body.c	Fri Feb 26 18:22:51 2016 +0300
@@ -48,7 +48,6 @@ ngx_http_read_client_request_body(ngx_ht
 
 #if (NGX_HTTP_V2)
     if (r->stream) {
-        r->request_body_no_buffering = 0;
         rc = ngx_http_v2_read_request_body(r, post_handler);
         goto done;
     }
@@ -215,6 +214,12 @@ ngx_http_read_unbuffered_request_body(ng
 {
     ngx_int_t  rc;
 
+#if (NGX_HTTP_V2)
+    if (r->stream) {
+        return ngx_http_v2_read_unbuffered_request_body(r);
+    }
+#endif
+
     if (r->connection->read->timedout) {
         r->connection->timedout = 1;
         return NGX_HTTP_REQUEST_TIME_OUT;
diff -r 8933cfd2fb60 -r d0745c528544 src/http/v2/ngx_http_v2.c
--- a/src/http/v2/ngx_http_v2.c	Fri Feb 26 18:22:26 2016 +0300
+++ b/src/http/v2/ngx_http_v2.c	Fri Feb 26 18:22:51 2016 +0300
@@ -3403,6 +3403,8 @@ ngx_http_v2_run_request(ngx_http_request
         return;
     }
 
+    r->headers_in.chunked = (r->headers_in.content_length_n == -1);
+
     ngx_http_process_request(r);
 }
 
@@ -3449,6 +3451,14 @@ ngx_http_v2_read_request_body(ngx_http_r
         len = 0;
     }
 
+    if (r->request_body_no_buffering) {
+        r->request_body_in_file_only = 0;
+
+        if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {
+            len = clcf->client_body_buffer_size;
+        }
+    }
+
     if (len != 0) {
         if (len < 0
             || len > (off_t) clcf->client_body_buffer_size
@@ -3471,10 +3481,15 @@ ngx_http_v2_read_request_body(ngx_http_r
     }
 
     if (stream->in_closed) {
+        r->request_body_no_buffering = 0;
         return ngx_http_v2_process_request_body(r, NULL, 0, 1);
     }
 
-    stream->recv_window = NGX_HTTP_V2_MAX_WINDOW;
+    rb->rest = 1;
+
+    stream->recv_window = r->request_body_no_buffering
+                          ? clcf->client_body_buffer_size
+                          : NGX_HTTP_V2_MAX_WINDOW;
 
     if (ngx_http_v2_send_window_update(stream->connection, stream->node->id,
                                        stream->recv_window)
@@ -3483,7 +3498,7 @@ ngx_http_v2_read_request_body(ngx_http_r
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    stream->no_flow_control = 1;
+    stream->no_flow_control = !r->request_body_no_buffering;
 
     ngx_add_timer(r->connection->read, clcf->client_body_timeout);
 
@@ -3557,7 +3572,7 @@ ngx_http_v2_process_request_body(ngx_htt
 {
     ngx_buf_t                 *buf;
     ngx_int_t                  rc;
-    ngx_chain_t                out;
+    ngx_chain_t                out, *cl;
     ngx_connection_t          *fc;
     ngx_http_request_body_t   *rb;
     ngx_http_core_loc_conf_t  *clcf;
@@ -3596,6 +3611,32 @@ ngx_http_v2_process_request_body(ngx_htt
 
         } else {
             buf->last = ngx_cpymem(buf->last, pos, size);
+
+            if (r->request_body_no_buffering && !last) {
+                cl = ngx_chain_get_free_buf(r->pool, &rb->free);
+                if (cl == NULL) {
+                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
+                }
+
+                ngx_memcpy(cl->buf, buf, sizeof(ngx_buf_t));
+
+                cl->buf->flush = 1;
+                cl->buf->tag =
+                    (ngx_buf_tag_t) &ngx_http_v2_process_request_body;
+
+                rc = ngx_http_top_request_body_filter(r, cl);
+
+                ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl,
+                            (ngx_buf_tag_t) &ngx_http_v2_process_request_body);
+
+                if (rc != NGX_OK) {
+                    return rc;
+                }
+
+                buf->pos = buf->last;
+
+                ngx_post_event(fc->read, &ngx_posted_events);
+            }
         }
     }
 
@@ -3632,8 +3673,13 @@ ngx_http_v2_process_request_body(ngx_htt
             ngx_del_timer(fc->read);
         }
 
-        r->read_event_handler = ngx_http_block_reading;
-        rb->post_handler(r);
+        if (r->request_body_no_buffering) {
+            r->read_event_handler(r);
+
+        } else {
+            r->read_event_handler = ngx_http_block_reading;
+            rb->post_handler(r);
+        }
 
         return NGX_OK;
     }
@@ -3677,6 +3723,82 @@ ngx_http_v2_read_client_request_body_han
 }
 
 
+ngx_int_t
+ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r)
+{
+    size_t                     available;
+    ngx_buf_t                 *buf;
+    ngx_connection_t          *fc;
+    ngx_http_v2_stream_t      *stream;
+    ngx_http_v2_connection_t  *h2c;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    stream = r->stream;
+    fc = r->connection;
+
+    if (fc->read->timedout) {
+        if (stream->recv_window == 0) {
+            return NGX_AGAIN;
+        }
+
+        stream->skip_data = 1;
+        fc->timedout = 1;
+
+        return NGX_HTTP_REQUEST_TIME_OUT;
+    }
+
+    if (fc->error) {
+        stream->skip_data = 1;
+        return NGX_ERROR;
+    }
+
+    if (!r->request_body->rest) {
+        r->reading_body = 0;
+        return NGX_OK;
+    }
+
+    buf = r->request_body->buf;
+
+    if (buf->pos != buf->last) {
+        return NGX_AGAIN;
+    }
+
+    buf->pos = buf->start;
+    buf->last = buf->start;
+
+    available = buf->end - buf->start;
+
+    h2c = stream->connection;
+
+    if (h2c->state.stream == stream) {
+        available -= h2c->state.length;
+    }
+
+    if (available > stream->recv_window && !stream->in_closed) {
+
+        if (ngx_http_v2_send_window_update(h2c, stream->node->id,
+                                           available - stream->recv_window)
+            == NGX_ERROR)
+        {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        if (stream->recv_window == 0) {
+            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+            ngx_add_timer(fc->read, clcf->client_body_timeout);
+        }
+
+        stream->recv_window = available;
+
+        if (!h2c->blocked && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+    }
+
+    return NGX_AGAIN;
+}
+
+
 static ngx_int_t
 ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,
     ngx_http_v2_stream_t *stream, ngx_uint_t status)
diff -r 8933cfd2fb60 -r d0745c528544 src/http/v2/ngx_http_v2.h
--- a/src/http/v2/ngx_http_v2.h	Fri Feb 26 18:22:26 2016 +0300
+++ b/src/http/v2/ngx_http_v2.h	Fri Feb 26 18:22:51 2016 +0300
@@ -263,6 +263,7 @@ void ngx_http_v2_request_headers_init(vo
 
 ngx_int_t ngx_http_v2_read_request_body(ngx_http_request_t *r,
     ngx_http_client_body_handler_pt post_handler);
+ngx_int_t ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r);
 
 void ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc);
 



More information about the nginx-devel mailing list