[nginx] Proxy: proxy_request_buffering chunked support.
Maxim Dounin
mdounin at mdounin.ru
Mon Mar 23 18:11:33 UTC 2015
details: http://hg.nginx.org/nginx/rev/d97e6be2d292
branches:
changeset: 6051:d97e6be2d292
user: Maxim Dounin <mdounin at mdounin.ru>
date: Mon Mar 23 21:09:19 2015 +0300
description:
Proxy: proxy_request_buffering chunked support.
diffstat:
src/http/modules/ngx_http_proxy_module.c | 222 ++++++++++++++++++++++++++++++-
1 files changed, 216 insertions(+), 6 deletions(-)
diffs (truncated from 305 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
@@ -110,7 +110,12 @@ typedef struct {
ngx_http_proxy_vars_t vars;
off_t internal_body_length;
- ngx_uint_t head; /* unsigned head:1 */
+ ngx_chain_t *free;
+ ngx_chain_t *busy;
+
+ unsigned head:1;
+ unsigned internal_chunked:1;
+ unsigned header_sent:1;
} ngx_http_proxy_ctx_t;
@@ -121,6 +126,7 @@ static ngx_int_t ngx_http_proxy_create_k
#endif
static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r);
+static ngx_int_t ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in);
static ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_proxy_input_filter_init(void *data);
@@ -146,6 +152,8 @@ static ngx_int_t
static ngx_int_t
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix);
static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,
@@ -728,8 +736,8 @@ static ngx_keyval_t ngx_http_proxy_head
{ ngx_string("Host"), ngx_string("$proxy_host") },
{ ngx_string("Connection"), ngx_string("close") },
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
+ { ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") },
{ ngx_string("TE"), ngx_string("") },
- { ngx_string("Transfer-Encoding"), ngx_string("") },
{ ngx_string("Keep-Alive"), ngx_string("") },
{ ngx_string("Expect"), ngx_string("") },
{ ngx_string("Upgrade"), ngx_string("") },
@@ -756,8 +764,8 @@ static ngx_keyval_t ngx_http_proxy_cach
{ ngx_string("Host"), ngx_string("$proxy_host") },
{ ngx_string("Connection"), ngx_string("close") },
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
+ { ngx_string("Transfer-Encoding"), ngx_string("$proxy_internal_chunked") },
{ ngx_string("TE"), ngx_string("") },
- { ngx_string("Transfer-Encoding"), ngx_string("") },
{ ngx_string("Keep-Alive"), ngx_string("") },
{ ngx_string("Expect"), ngx_string("") },
{ ngx_string("Upgrade"), ngx_string("") },
@@ -793,6 +801,10 @@ static ngx_http_variable_t ngx_http_pro
ngx_http_proxy_internal_body_length_variable, 0,
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+ { ngx_string("proxy_internal_chunked"), NULL,
+ ngx_http_proxy_internal_chunked_variable, 0,
+ NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
};
@@ -885,10 +897,9 @@ ngx_http_proxy_handler(ngx_http_request_
if (!plcf->upstream.request_buffering
&& plcf->body_values == NULL && plcf->upstream.pass_request_body
- && !r->headers_in.chunked)
+ && (!r->headers_in.chunked
+ || plcf->http_version == NGX_HTTP_VERSION_11))
{
- /* TODO: support chunked when using HTTP/1.1 */
-
r->request_body_no_buffering = 1;
}
@@ -1210,6 +1221,10 @@ ngx_http_proxy_create_request(ngx_http_r
ctx->internal_body_length = body_len;
len += body_len;
+ } else if (r->headers_in.chunked && r->reading_body) {
+ ctx->internal_body_length = -1;
+ ctx->internal_chunked = 1;
+
} else {
ctx->internal_body_length = r->headers_in.content_length_n;
}
@@ -1413,6 +1428,11 @@ ngx_http_proxy_create_request(ngx_http_r
u->request_bufs = cl;
+ if (ctx->internal_chunked) {
+ u->output.output_filter = ngx_http_proxy_body_output_filter;
+ u->output.filter_ctx = r;
+ }
+
} else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) {
body = u->request_bufs;
@@ -1475,6 +1495,172 @@ ngx_http_proxy_reinit_request(ngx_http_r
static ngx_int_t
+ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in)
+{
+ ngx_http_request_t *r = data;
+
+ off_t size;
+ u_char *chunk;
+ ngx_int_t rc;
+ ngx_buf_t *b;
+ ngx_chain_t *out, *cl, *tl, **ll;
+ ngx_http_proxy_ctx_t *ctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "proxy output filter");
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (in == NULL) {
+ out = in;
+ goto out;
+ }
+
+ out = NULL;
+ ll = &out;
+
+ if (!ctx->header_sent) {
+ /* first buffer contains headers, pass it unmodified */
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "proxy output header");
+
+ ctx->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;
+ }
+ }
+
+ size = 0;
+ cl = in;
+
+ for ( ;; ) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "proxy output chunk: %d", ngx_buf_size(cl->buf));
+
+ size += ngx_buf_size(cl->buf);
+
+ if (cl->buf->flush
+ || cl->buf->sync
+ || ngx_buf_in_memory(cl->buf)
+ || cl->buf->in_file)
+ {
+ tl = ngx_alloc_chain_link(r->pool);
+ if (tl == NULL) {
+ return NGX_ERROR;
+ }
+
+ tl->buf = cl->buf;
+ *ll = tl;
+ ll = &tl->next;
+ }
+
+ if (cl->next == NULL) {
+ break;
+ }
+
+ cl = cl->next;
+ }
+
+ if (size) {
+ tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (tl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = tl->buf;
+ chunk = b->start;
+
+ if (chunk == NULL) {
+ /* the "0000000000000000" is 64-bit hexadecimal string */
+
+ chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1);
+ if (chunk == NULL) {
+ return NGX_ERROR;
+ }
+
+ b->start = chunk;
+ b->end = chunk + sizeof("0000000000000000" CRLF) - 1;
+ }
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter;
+ b->memory = 0;
+ b->temporary = 1;
+ b->pos = chunk;
+ b->last = ngx_sprintf(chunk, "%xO" CRLF, size);
+
+ tl->next = out;
+ out = tl;
+ }
+
+ if (cl->buf->last_buf) {
+ tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (tl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = tl->buf;
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter;
+ b->temporary = 0;
+ b->memory = 1;
+ b->last_buf = 1;
+ b->pos = (u_char *) CRLF "0" CRLF CRLF;
+ b->last = b->pos + 7;
+
+ cl->buf->last_buf = 0;
+
+ *ll = tl;
+
+ if (size == 0) {
+ b->pos += 2;
+ }
+
+ } else if (size > 0) {
+ tl = ngx_chain_get_free_buf(r->pool, &ctx->free);
+ if (tl == NULL) {
+ return NGX_ERROR;
+ }
+
+ b = tl->buf;
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter;
+ b->temporary = 0;
+ b->memory = 1;
+ b->pos = (u_char *) CRLF;
+ b->last = b->pos + 2;
+
+ *ll = tl;
+
+ } else {
+ *ll = NULL;
+ }
+
+out:
+
+ rc = ngx_chain_writer(&r->upstream->writer, out);
+
+ ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,
+ (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter);
+
+ return rc;
+}
+
+
+static ngx_int_t
ngx_http_proxy_process_status_line(ngx_http_request_t *r)
{
size_t len;
@@ -2268,6 +2454,30 @@ ngx_http_proxy_internal_body_length_vari
static ngx_int_t
+ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data)
+{
+ ngx_http_proxy_ctx_t *ctx;
+
+ ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);
+
+ if (ctx == NULL || !ctx->internal_chunked) {
+ v->not_found = 1;
+ return NGX_OK;
+ }
+
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+
+ v->data = (u_char *) "chunked";
+ v->len = sizeof("chunked") - 1;
+
+ return NGX_OK;
+}
+
More information about the nginx-devel
mailing list