mirror of
https://github.com/nginx/nginx.git
synced 2025-06-28 10:10:39 +08:00
Proxy: proxy_request_buffering chunked support.
This commit is contained in:
parent
2743bb68ee
commit
756997b696
@ -110,7 +110,12 @@ typedef struct {
|
|||||||
ngx_http_proxy_vars_t vars;
|
ngx_http_proxy_vars_t vars;
|
||||||
off_t internal_body_length;
|
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;
|
} ngx_http_proxy_ctx_t;
|
||||||
|
|
||||||
|
|
||||||
@ -121,6 +126,7 @@ static ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r);
|
|||||||
#endif
|
#endif
|
||||||
static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r);
|
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_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_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_process_header(ngx_http_request_t *r);
|
||||||
static ngx_int_t ngx_http_proxy_input_filter_init(void *data);
|
static ngx_int_t ngx_http_proxy_input_filter_init(void *data);
|
||||||
@ -146,6 +152,8 @@ static ngx_int_t
|
|||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
|
ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
|
||||||
ngx_http_variable_value_t *v, uintptr_t data);
|
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,
|
static ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,
|
||||||
ngx_table_elt_t *h, size_t prefix);
|
ngx_table_elt_t *h, size_t prefix);
|
||||||
static ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,
|
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_headers[] = {
|
|||||||
{ ngx_string("Host"), ngx_string("$proxy_host") },
|
{ ngx_string("Host"), ngx_string("$proxy_host") },
|
||||||
{ ngx_string("Connection"), ngx_string("close") },
|
{ ngx_string("Connection"), ngx_string("close") },
|
||||||
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
|
{ 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("TE"), ngx_string("") },
|
||||||
{ ngx_string("Transfer-Encoding"), ngx_string("") },
|
|
||||||
{ ngx_string("Keep-Alive"), ngx_string("") },
|
{ ngx_string("Keep-Alive"), ngx_string("") },
|
||||||
{ ngx_string("Expect"), ngx_string("") },
|
{ ngx_string("Expect"), ngx_string("") },
|
||||||
{ ngx_string("Upgrade"), ngx_string("") },
|
{ ngx_string("Upgrade"), ngx_string("") },
|
||||||
@ -756,8 +764,8 @@ static ngx_keyval_t ngx_http_proxy_cache_headers[] = {
|
|||||||
{ ngx_string("Host"), ngx_string("$proxy_host") },
|
{ ngx_string("Host"), ngx_string("$proxy_host") },
|
||||||
{ ngx_string("Connection"), ngx_string("close") },
|
{ ngx_string("Connection"), ngx_string("close") },
|
||||||
{ ngx_string("Content-Length"), ngx_string("$proxy_internal_body_length") },
|
{ 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("TE"), ngx_string("") },
|
||||||
{ ngx_string("Transfer-Encoding"), ngx_string("") },
|
|
||||||
{ ngx_string("Keep-Alive"), ngx_string("") },
|
{ ngx_string("Keep-Alive"), ngx_string("") },
|
||||||
{ ngx_string("Expect"), ngx_string("") },
|
{ ngx_string("Expect"), ngx_string("") },
|
||||||
{ ngx_string("Upgrade"), ngx_string("") },
|
{ ngx_string("Upgrade"), ngx_string("") },
|
||||||
@ -793,6 +801,10 @@ static ngx_http_variable_t ngx_http_proxy_vars[] = {
|
|||||||
ngx_http_proxy_internal_body_length_variable, 0,
|
ngx_http_proxy_internal_body_length_variable, 0,
|
||||||
NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 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 }
|
{ ngx_null_string, NULL, NULL, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -885,10 +897,9 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
|
|||||||
|
|
||||||
if (!plcf->upstream.request_buffering
|
if (!plcf->upstream.request_buffering
|
||||||
&& plcf->body_values == NULL && plcf->upstream.pass_request_body
|
&& 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;
|
r->request_body_no_buffering = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1210,6 +1221,10 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
|
|||||||
ctx->internal_body_length = body_len;
|
ctx->internal_body_length = body_len;
|
||||||
len += body_len;
|
len += body_len;
|
||||||
|
|
||||||
|
} else if (r->headers_in.chunked && r->reading_body) {
|
||||||
|
ctx->internal_body_length = -1;
|
||||||
|
ctx->internal_chunked = 1;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ctx->internal_body_length = r->headers_in.content_length_n;
|
ctx->internal_body_length = r->headers_in.content_length_n;
|
||||||
}
|
}
|
||||||
@ -1413,6 +1428,11 @@ ngx_http_proxy_create_request(ngx_http_request_t *r)
|
|||||||
|
|
||||||
u->request_bufs = cl;
|
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) {
|
} else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) {
|
||||||
|
|
||||||
body = u->request_bufs;
|
body = u->request_bufs;
|
||||||
@ -1474,6 +1494,172 @@ 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)
|
||||||
|
{
|
||||||
|
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
|
static ngx_int_t
|
||||||
ngx_http_proxy_process_status_line(ngx_http_request_t *r)
|
ngx_http_proxy_process_status_line(ngx_http_request_t *r)
|
||||||
{
|
{
|
||||||
@ -2267,6 +2453,30 @@ ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
|
ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,
|
||||||
size_t prefix)
|
size_t prefix)
|
||||||
|
Loading…
Reference in New Issue
Block a user