[nginx] Request body: unbuffered reading.
Maxim Dounin
mdounin at mdounin.ru
Mon Mar 23 18:11:26 UTC 2015
details: http://hg.nginx.org/nginx/rev/a08fad30aeac
branches:
changeset: 6050:a08fad30aeac
user: Maxim Dounin <mdounin at mdounin.ru>
date: Mon Mar 23 21:09:19 2015 +0300
description:
Request body: unbuffered reading.
The r->request_body_no_buffering flag was introduced. It instructs
client request body reading code to avoid reading the whole body, and
to call post_handler early instead. The caller should use the
ngx_http_read_unbuffered_request_body() function to read remaining
parts of the body.
Upstream module is now able to use this mode, if configured with
the proxy_request_buffering directive.
diffstat:
src/http/modules/ngx_http_proxy_module.c | 26 ++++-
src/http/ngx_http.h | 1 +
src/http/ngx_http_request.c | 5 +
src/http/ngx_http_request.h | 2 +
src/http/ngx_http_request_body.c | 107 ++++++++++++++++-
src/http/ngx_http_upstream.c | 185 ++++++++++++++++++++++++++++--
src/http/ngx_http_upstream.h | 1 +
src/http/ngx_http_variables.c | 4 +
8 files changed, 306 insertions(+), 25 deletions(-)
diffs (truncated from 602 to 300 lines):
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -292,6 +292,13 @@ static ngx_command_t ngx_http_proxy_com
offsetof(ngx_http_proxy_loc_conf_t, upstream.buffering),
NULL },
+ { ngx_string("proxy_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_proxy_loc_conf_t, upstream.request_buffering),
+ NULL },
+
{ ngx_string("proxy_ignore_client_abort"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
@@ -876,6 +883,15 @@ ngx_http_proxy_handler(ngx_http_request_
u->accel = 1;
+ if (!plcf->upstream.request_buffering
+ && plcf->body_values == NULL && plcf->upstream.pass_request_body
+ && !r->headers_in.chunked)
+ {
+ /* TODO: support chunked when using HTTP/1.1 */
+
+ r->request_body_no_buffering = 1;
+ }
+
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
@@ -1393,7 +1409,11 @@ ngx_http_proxy_create_request(ngx_http_r
"http proxy header:%N\"%*s\"",
(size_t) (b->last - b->pos), b->pos);
- if (plcf->body_values == NULL && plcf->upstream.pass_request_body) {
+ if (r->request_body_no_buffering) {
+
+ u->request_bufs = cl;
+
+ } else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) {
body = u->request_bufs;
u->request_bufs = cl;
@@ -2582,6 +2602,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
conf->upstream.store_access = NGX_CONF_UNSET_UINT;
conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;
conf->upstream.buffering = NGX_CONF_UNSET;
+ conf->upstream.request_buffering = NGX_CONF_UNSET;
conf->upstream.ignore_client_abort = NGX_CONF_UNSET;
conf->upstream.force_ranges = NGX_CONF_UNSET;
@@ -2691,6 +2712,9 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
ngx_conf_merge_value(conf->upstream.buffering,
prev->upstream.buffering, 1);
+ ngx_conf_merge_value(conf->upstream.request_buffering,
+ prev->upstream.request_buffering, 1);
+
ngx_conf_merge_value(conf->upstream.ignore_client_abort,
prev->upstream.ignore_client_abort, 0);
diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -138,6 +138,7 @@ ngx_int_t ngx_http_send_special(ngx_http
ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,
ngx_http_client_body_handler_pt post_handler);
+ngx_int_t ngx_http_read_unbuffered_request_body(ngx_http_request_t *r);
ngx_int_t ngx_http_send_header(ngx_http_request_t *r);
ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -2525,6 +2525,11 @@ ngx_http_finalize_connection(ngx_http_re
return;
}
+ if (r->reading_body) {
+ r->keepalive = 0;
+ r->lingering_close = 1;
+ }
+
if (!ngx_terminate
&& !ngx_exiting
&& r->keepalive
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -473,6 +473,7 @@ struct ngx_http_request_s {
unsigned request_body_in_clean_file:1;
unsigned request_body_file_group_access:1;
unsigned request_body_file_log_level:3;
+ unsigned request_body_no_buffering:1;
unsigned subrequest_in_memory:1;
unsigned waited:1;
@@ -509,6 +510,7 @@ struct ngx_http_request_s {
unsigned keepalive:1;
unsigned lingering_close:1;
unsigned discard_body:1;
+ unsigned reading_body:1;
unsigned internal:1;
unsigned error_page:1;
unsigned filter_finalize:1;
diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -42,12 +42,14 @@ ngx_http_read_client_request_body(ngx_ht
#if (NGX_HTTP_SPDY)
if (r->spdy_stream && r == r->main) {
+ r->request_body_no_buffering = 0;
rc = ngx_http_spdy_read_request_body(r, post_handler);
goto done;
}
#endif
if (r != r->main || r->request_body || r->discard_body) {
+ r->request_body_no_buffering = 0;
post_handler(r);
return NGX_OK;
}
@@ -57,6 +59,10 @@ ngx_http_read_client_request_body(ngx_ht
goto done;
}
+ if (r->request_body_no_buffering) {
+ r->request_body_in_file_only = 0;
+ }
+
rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
if (rb == NULL) {
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -79,6 +85,7 @@ ngx_http_read_client_request_body(ngx_ht
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;
}
@@ -171,6 +178,8 @@ ngx_http_read_client_request_body(ngx_ht
}
}
+ r->request_body_no_buffering = 0;
+
post_handler(r);
return NGX_OK;
@@ -214,6 +223,21 @@ ngx_http_read_client_request_body(ngx_ht
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--;
}
@@ -222,6 +246,26 @@ done:
}
+ngx_int_t
+ngx_http_read_unbuffered_request_body(ngx_http_request_t *r)
+{
+ ngx_int_t rc;
+
+ 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)
{
@@ -264,18 +308,43 @@ ngx_http_do_read_client_request_body(ngx
for ( ;; ) {
if (rb->buf->last == rb->buf->end) {
- /* pass buffer to request body filter chain */
+ if (rb->buf->pos != rb->buf->last) {
- out.buf = rb->buf;
- out.next = NULL;
+ /* pass buffer to request body filter chain */
- rc = ngx_http_request_body_filter(r, &out);
+ out.buf = rb->buf;
+ out.next = NULL;
- if (rc != NGX_OK) {
- return rc;
+ 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;
}
@@ -342,6 +411,22 @@ ngx_http_do_read_client_request_body(ngx
}
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);
@@ -387,9 +472,10 @@ ngx_http_do_read_client_request_body(ngx
}
}
- r->read_event_handler = ngx_http_block_reading;
-
- rb->post_handler(r);
+ if (!r->request_body_no_buffering) {
+ r->read_event_handler = ngx_http_block_reading;
+ rb->post_handler(r);
+ }
return NGX_OK;
}
@@ -1085,7 +1171,8 @@ ngx_http_request_body_save_filter(ngx_ht
}
if (rb->rest > 0
- && rb->buf && rb->buf->last == rb->buf->end)
+ && rb->buf && rb->buf->last == rb->buf->end
+ && !r->request_body_no_buffering)
{
if (ngx_http_write_request_body(r) != NGX_OK) {
More information about the nginx-devel
mailing list