summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornginx <nginx@nginx.org>2013-02-19 15:28:29 +0000
committerJon Kolb <jon@b0g.us>2013-02-19 15:28:29 +0000
commita36b88c56061d08f8fbe96a51489bf3381792aca (patch)
treefe157c41a9e920b0b05a343f2dc85312f080b6f4
parentbf3178867712d61cd5bb7692e6f837c4b0c51d8c (diff)
downloadnginx-a36b88c56061d08f8fbe96a51489bf3381792aca.tar.gz
Changes with nginx 1.3.13 19 Feb 2013v1.3.13
*) Change: a compiler with name "cc" is now used by default. *) Feature: support for proxying of WebSocket connections. Thanks to Apcera and CloudBees for sponsoring this work. *) Feature: the "auth_basic_user_file" directive supports "{SHA}" password encryption method. Thanks to Louis Opter.
-rw-r--r--CHANGES12
-rw-r--r--CHANGES.ru13
-rw-r--r--auto/lib/perl/make3
-rw-r--r--auto/options2
-rw-r--r--src/core/nginx.h4
-rw-r--r--src/core/ngx_crypt.c37
-rw-r--r--src/http/modules/ngx_http_autoindex_module.c7
-rw-r--r--src/http/modules/ngx_http_chunked_filter_module.c1
-rw-r--r--src/http/modules/ngx_http_proxy_module.c8
-rw-r--r--src/http/modules/perl/nginx.pm2
-rw-r--r--src/http/ngx_http_header_filter_module.c11
-rw-r--r--src/http/ngx_http_request.c4
-rw-r--r--src/http/ngx_http_request.h5
-rw-r--r--src/http/ngx_http_upstream.c287
-rw-r--r--src/http/ngx_http_upstream.h3
-rw-r--r--src/http/ngx_http_variables.c6
16 files changed, 395 insertions, 10 deletions
diff --git a/CHANGES b/CHANGES
index 37e62cad0..eb5377e74 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,16 @@
+Changes with nginx 1.3.13 19 Feb 2013
+
+ *) Change: a compiler with name "cc" is now used by default.
+
+ *) Feature: support for proxying of WebSocket connections.
+ Thanks to Apcera and CloudBees for sponsoring this work.
+
+ *) Feature: the "auth_basic_user_file" directive supports "{SHA}"
+ password encryption method.
+ Thanks to Louis Opter.
+
+
Changes with nginx 1.3.12 05 Feb 2013
*) Feature: variables support in the "proxy_bind", "fastcgi_bind",
diff --git a/CHANGES.ru b/CHANGES.ru
index 881866e14..1d1a3a025 100644
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,17 @@
+Изменения в nginx 1.3.13 19.02.2013
+
+ *) Изменение: теперь для сборки по умолчанию используется компилятор с
+ именем "cc".
+
+ *) Добавление: поддержка проксирования WebSocket-соединений.
+ Спасибо Apcera и CloudBees за спонсирование разработки.
+
+ *) Добавление: директива auth_basic_user_file поддерживает шифрование
+ паролей методом "{SHA}".
+ Спасибо Louis Opter.
+
+
Изменения в nginx 1.3.12 05.02.2013
*) Добавление: директивы proxy_bind, fastcgi_bind, memcached_bind,
diff --git a/auto/lib/perl/make b/auto/lib/perl/make
index 6fe95af0e..b40352abf 100644
--- a/auto/lib/perl/make
+++ b/auto/lib/perl/make
@@ -6,11 +6,12 @@
cat << END >> $NGX_MAKEFILE
$NGX_OBJS/src/http/modules/perl/blib/arch/auto/nginx/nginx.so: \
+ \$(CORE_DEPS) \$(HTTP_DEPS) \
src/http/modules/perl/nginx.pm \
src/http/modules/perl/nginx.xs \
src/http/modules/perl/ngx_http_perl_module.h \
$NGX_OBJS/src/http/modules/perl/Makefile
- cp -p src/http/modules/perl/nginx.* $NGX_OBJS/src/http/modules/perl/
+ cp src/http/modules/perl/nginx.* $NGX_OBJS/src/http/modules/perl/
cd $NGX_OBJS/src/http/modules/perl && \$(MAKE)
diff --git a/auto/options b/auto/options
index a75bead54..150286d7b 100644
--- a/auto/options
+++ b/auto/options
@@ -15,7 +15,7 @@ NGX_LOCK_PATH=
NGX_USER=
NGX_GROUP=
-CC=${CC:-gcc}
+CC=${CC:-cc}
CPP=
NGX_OBJS=objs
diff --git a/src/core/nginx.h b/src/core/nginx.h
index 9c9df6356..f522433e2 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
#define _NGINX_H_INCLUDED_
-#define nginx_version 1003012
-#define NGINX_VERSION "1.3.12"
+#define nginx_version 1003013
+#define NGINX_VERSION "1.3.13"
#define NGINX_VER "nginx/" NGINX_VERSION
#define NGINX_VAR "NGINX"
diff --git a/src/core/ngx_crypt.c b/src/core/ngx_crypt.c
index b2e25b901..629d160e8 100644
--- a/src/core/ngx_crypt.c
+++ b/src/core/ngx_crypt.c
@@ -24,6 +24,8 @@ static ngx_int_t ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt,
static ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt,
u_char **encrypted);
+static ngx_int_t ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
#endif
@@ -43,6 +45,9 @@ ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
#if (NGX_HAVE_SHA1)
} else if (ngx_strncmp(salt, "{SSHA}", sizeof("{SSHA}") - 1) == 0) {
return ngx_crypt_ssha(pool, key, salt, encrypted);
+
+ } else if (ngx_strncmp(salt, "{SHA}", sizeof("{SHA}") - 1) == 0) {
+ return ngx_crypt_sha(pool, key, salt, encrypted);
#endif
}
@@ -241,6 +246,38 @@ ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
return NGX_OK;
}
+
+static ngx_int_t
+ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ size_t len;
+ ngx_str_t encoded, decoded;
+ ngx_sha1_t sha1;
+ u_char digest[20];
+
+ /* "{SHA}" base64(SHA1(key)) */
+
+ decoded.len = sizeof(digest);
+ decoded.data = digest;
+
+ ngx_sha1_init(&sha1);
+ ngx_sha1_update(&sha1, key, ngx_strlen(key));
+ ngx_sha1_final(digest, &sha1);
+
+ len = sizeof("{SHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1;
+
+ *encrypted = ngx_pnalloc(pool, len);
+ if (*encrypted == NULL) {
+ return NGX_ERROR;
+ }
+
+ encoded.data = ngx_cpymem(*encrypted, "{SHA}", sizeof("{SHA}") - 1);
+ ngx_encode_base64(&encoded, &decoded);
+ encoded.data[encoded.len] = '\0';
+
+ return NGX_OK;
+}
+
#endif /* NGX_HAVE_SHA1 */
#endif /* NGX_CRYPT */
diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c
index 450a48e50..fb46d65d1 100644
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -489,8 +489,11 @@ ngx_http_autoindex_handler(ngx_http_request_t *r)
}
b->last = ngx_cpymem(b->last, "</a>", sizeof("</a>") - 1);
- ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len);
- b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len;
+
+ if (NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
+ ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len);
+ b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len;
+ }
}
*b->last++ = ' ';
diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c
index 94313a8f6..a7dc5bf4d 100644
--- a/src/http/modules/ngx_http_chunked_filter_module.c
+++ b/src/http/modules/ngx_http_chunked_filter_module.c
@@ -62,6 +62,7 @@ ngx_http_chunked_header_filter(ngx_http_request_t *r)
if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED
|| r->headers_out.status == NGX_HTTP_NO_CONTENT
+ || r->headers_out.status < NGX_HTTP_OK
|| r != r->main
|| (r->method & NGX_HTTP_HEAD))
{
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index ce47a9e34..a623adc34 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -1474,6 +1474,14 @@ ngx_http_proxy_process_header(ngx_http_request_t *r)
u->keepalive = !u->headers_in.connection_close;
}
+ if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ u->keepalive = 0;
+
+ if (r->headers_in.upgrade) {
+ u->upgrade = 1;
+ }
+ }
+
return NGX_OK;
}
diff --git a/src/http/modules/perl/nginx.pm b/src/http/modules/perl/nginx.pm
index 87ce2c115..353cadb9d 100644
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -50,7 +50,7 @@ our @EXPORT = qw(
HTTP_INSUFFICIENT_STORAGE
);
-our $VERSION = '1.3.12';
+our $VERSION = '1.3.13';
require XSLoader;
XSLoader::load('nginx', $VERSION);
diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c
index e3efbba5b..707a81313 100644
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -379,7 +379,10 @@ ngx_http_header_filter(ngx_http_request_t *r)
len += sizeof("Transfer-Encoding: chunked" CRLF) - 1;
}
- if (r->keepalive) {
+ if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ len += sizeof("Connection: upgrade" CRLF) - 1;
+
+ } else if (r->keepalive) {
len += sizeof("Connection: keep-alive" CRLF) - 1;
/*
@@ -548,7 +551,11 @@ ngx_http_header_filter(ngx_http_request_t *r)
sizeof("Transfer-Encoding: chunked" CRLF) - 1);
}
- if (r->keepalive) {
+ if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ b->last = ngx_cpymem(b->last, "Connection: upgrade" CRLF,
+ sizeof("Connection: upgrade" CRLF) - 1);
+
+ } else if (r->keepalive) {
b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF,
sizeof("Connection: keep-alive" CRLF) - 1);
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index e94e7fcce..763e7bf11 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -130,6 +130,10 @@ ngx_http_header_t ngx_http_headers_in[] = {
offsetof(ngx_http_headers_in_t, expect),
ngx_http_process_unique_header_line },
+ { ngx_string("Upgrade"),
+ offsetof(ngx_http_headers_in_t, upgrade),
+ ngx_http_process_header_line },
+
#if (NGX_HTTP_GZIP)
{ ngx_string("Accept-Encoding"),
offsetof(ngx_http_headers_in_t, accept_encoding),
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index f234840c1..f0c39adaf 100644
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -64,6 +64,10 @@
#define NGX_HTTP_LOG_UNSAFE 8
+#define NGX_HTTP_CONTINUE 100
+#define NGX_HTTP_SWITCHING_PROTOCOLS 101
+#define NGX_HTTP_PROCESSING 102
+
#define NGX_HTTP_OK 200
#define NGX_HTTP_CREATED 201
#define NGX_HTTP_ACCEPTED 202
@@ -184,6 +188,7 @@ typedef struct {
ngx_table_elt_t *transfer_encoding;
ngx_table_elt_t *expect;
+ ngx_table_elt_t *upgrade;
#if (NGX_HTTP_GZIP)
ngx_table_elt_t *accept_encoding;
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 4f167a2b1..6a77f7f03 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -46,6 +46,16 @@ static void ngx_http_upstream_process_body_in_memory(ngx_http_request_t *r,
ngx_http_upstream_t *u);
static void ngx_http_upstream_send_response(ngx_http_request_t *r,
ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgrade(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r);
+static void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u);
+static void ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
+ ngx_uint_t from_upstream, ngx_uint_t do_write);
static void
ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);
static void
@@ -1327,6 +1337,7 @@ ngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)
}
u->keepalive = 0;
+ u->upgrade = 0;
ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));
u->headers_in.content_length_n = -1;
@@ -2078,6 +2089,11 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
return;
}
+ if (u->upgrade) {
+ ngx_http_upstream_upgrade(r, u);
+ return;
+ }
+
c = r->connection;
if (r->header_only) {
@@ -2361,6 +2377,277 @@ ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
static void
+ngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u)
+{
+ int tcp_nodelay;
+ ngx_connection_t *c;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ /* TODO: prevent upgrade if not requested or not possible */
+
+ r->keepalive = 0;
+ c->log->action = "proxying upgraded connection";
+
+ u->read_event_handler = ngx_http_upstream_upgraded_read_upstream;
+ u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;
+ r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;
+ r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;
+
+ if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "tcp_nodelay");
+
+ tcp_nodelay = 1;
+
+ if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int)) == -1)
+ {
+ ngx_connection_error(c, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) failed");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ c->tcp_nodelay = NGX_TCP_NODELAY_SET;
+
+ if (setsockopt(u->peer.connection->fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int)) == -1)
+ {
+ ngx_connection_error(u->peer.connection, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) failed");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ u->peer.connection->tcp_nodelay = NGX_TCP_NODELAY_SET;
+ }
+
+ if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (u->peer.connection->read->ready
+ || u->buffer.pos != u->buffer.last)
+ {
+ ngx_http_upstream_process_upgraded(r, 1, 1);
+ }
+
+ if (c->read->ready
+ || r->header_in->pos != r->header_in->last)
+ {
+ ngx_http_upstream_process_upgraded(r, 0, 1);
+ }
+}
+
+
+static void
+ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r)
+{
+ ngx_http_upstream_process_upgraded(r, 0, 0);
+}
+
+
+static void
+ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r)
+{
+ ngx_http_upstream_process_upgraded(r, 1, 1);
+}
+
+
+static void
+ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_upstream_process_upgraded(r, 1, 0);
+}
+
+
+static void
+ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,
+ ngx_http_upstream_t *u)
+{
+ ngx_http_upstream_process_upgraded(r, 0, 1);
+}
+
+
+static void
+ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
+ ngx_uint_t from_upstream, ngx_uint_t do_write)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_connection_t *c, *downstream, *upstream, *dst, *src;
+ ngx_http_upstream_t *u;
+ ngx_http_core_loc_conf_t *clcf;
+
+ c = r->connection;
+ u = r->upstream;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream process upgraded, fu:%ui", from_upstream);
+
+ downstream = c;
+ upstream = u->peer.connection;
+
+ if (downstream->write->timedout) {
+ c->timedout = 1;
+ ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
+ ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
+ return;
+ }
+
+ if (upstream->read->timedout || upstream->write->timedout) {
+ ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (from_upstream) {
+ src = upstream;
+ dst = downstream;
+ b = &u->buffer;
+
+ } else {
+ src = downstream;
+ dst = upstream;
+ b = &u->from_client;
+
+ if (r->header_in->last > r->header_in->pos) {
+ b = r->header_in;
+ b->end = b->last;
+ do_write = 1;
+ }
+
+ if (b->start == NULL) {
+ b->start = ngx_palloc(r->pool, u->conf->buffer_size);
+ if (b->start == NULL) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ b->pos = b->start;
+ b->last = b->start;
+ b->end = b->start + u->conf->buffer_size;
+ b->temporary = 1;
+ b->tag = u->output.tag;
+ }
+ }
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ size = b->last - b->pos;
+
+ if (size && dst->write->ready) {
+
+ n = dst->send(dst, b->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (n > 0) {
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && src->read->ready) {
+
+ n = src->recv(src, b->last, size);
+
+ if (n == NGX_AGAIN || n == 0) {
+ break;
+ }
+
+ if (n > 0) {
+ do_write = 1;
+ b->last += n;
+
+ continue;
+ }
+
+ if (n == NGX_ERROR) {
+ src->read->eof = 1;
+ }
+ }
+
+ break;
+ }
+
+ if ((upstream->read->eof && u->buffer.pos == u->buffer.last)
+ || (downstream->read->eof && u->from_client.pos == u->from_client.last)
+ || (downstream->read->eof && upstream->read->eof))
+ {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http upstream upgraded done");
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+ if (ngx_handle_write_event(upstream->write, u->conf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (upstream->write->active && !upstream->write->ready) {
+ ngx_add_timer(upstream->write, u->conf->send_timeout);
+
+ } else if (upstream->write->timer_set) {
+ ngx_del_timer(upstream->write);
+ }
+
+ if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (upstream->read->active && !upstream->read->ready) {
+ ngx_add_timer(upstream->read, u->conf->read_timeout);
+
+ } else if (upstream->read->timer_set) {
+ ngx_del_timer(upstream->read);
+ }
+
+ if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
+ != NGX_OK)
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (ngx_handle_read_event(downstream->read, 0) != NGX_OK) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ if (downstream->write->active && !downstream->write->ready) {
+ ngx_add_timer(downstream->write, clcf->send_timeout);
+
+ } else if (downstream->write->timer_set) {
+ ngx_del_timer(downstream->write);
+ }
+}
+
+
+static void
ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)
{
ngx_event_t *wev;
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index 2c910cd9c..29ebf9bd9 100644
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -284,6 +284,8 @@ struct ngx_http_upstream_s {
ngx_http_upstream_resolved_t *resolved;
+ ngx_buf_t from_client;
+
ngx_buf_t buffer;
off_t length;
@@ -329,6 +331,7 @@ struct ngx_http_upstream_s {
unsigned buffering:1;
unsigned keepalive:1;
+ unsigned upgrade:1;
unsigned request_sent:1;
unsigned header_sent:1;
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
index 0c6872fe2..b8190b030 100644
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -1747,7 +1747,11 @@ ngx_http_variable_sent_connection(ngx_http_request_t *r,
size_t len;
char *p;
- if (r->keepalive) {
+ if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
+ len = sizeof("upgrade") - 1;
+ p = "upgrade";
+
+ } else if (r->keepalive) {
len = sizeof("keep-alive") - 1;
p = "keep-alive";