Merge branch 'nginx' (nginx-1.15.1).
Change-Id: I34e436130d0a9bc89861dfa8f57102c476d0e5f3
Signed-off-by: Piotr Sikora <piotrsikora@google.com>
diff --git a/.hgtags b/.hgtags
index 1b26568..d6477c0 100644
--- a/.hgtags
+++ b/.hgtags
@@ -426,3 +426,4 @@
64179f242cb55fc206bca59de9bfdc4cf5ebcec7 release-1.13.11
051e5fa03b92b8a564f6b12debd483d267391e82 release-1.13.12
990b3e885636d763b97ed02d0d2cfc161a4e0c09 release-1.15.0
+4189160cb946bb38d0bc0a452b5eb4cdd8979fb5 release-1.15.1
diff --git a/BUILD b/BUILD
index 9d519dd..6266d5d 100644
--- a/BUILD
+++ b/BUILD
@@ -1039,6 +1039,21 @@
)
cc_library(
+ name = "http_upstream_random",
+ srcs = [
+ "src/http/modules/ngx_http_upstream_random_module.c",
+ ],
+ copts = nginx_copts,
+ defines = [
+ "NGX_HTTP_UPSTREAM_RANDOM",
+ ],
+ deps = [
+ ":core",
+ ":http",
+ ],
+)
+
+cc_library(
name = "http_userid",
srcs = [
"src/http/modules/ngx_http_userid_filter_module.c",
@@ -1353,6 +1368,21 @@
],
)
+cc_library(
+ name = "stream_upstream_random",
+ srcs = [
+ "src/stream/ngx_stream_upstream_random_module.c",
+ ],
+ copts = nginx_copts,
+ defines = [
+ "NGX_STREAM_UPSTREAM_RANDOM",
+ ],
+ deps = [
+ ":core",
+ ":stream",
+ ],
+)
+
filegroup(
name = "modules",
srcs = [
@@ -1408,6 +1438,7 @@
":http_upstream_ip_hash",
":http_upstream_keepalive",
":http_upstream_least_conn",
+ ":http_upstream_random",
":http_userid",
":http_uwsgi",
":mail",
@@ -1425,6 +1456,7 @@
":stream_ssl_preread",
":stream_upstream_hash",
":stream_upstream_least_conn",
+ ":stream_upstream_random",
"@ngx_brotli//:http_brotli_filter",
"@ngx_brotli//:http_brotli_static",
],
@@ -1503,5 +1535,5 @@
preinst = "@nginx_pkgoss//:debian_preinst",
prerm = "@nginx_pkgoss//:debian_prerm",
section = "httpd",
- version = "1.15.0",
+ version = "1.15.1",
)
diff --git a/auto/modules b/auto/modules
index 8e14758..846028c 100644
--- a/auto/modules
+++ b/auto/modules
@@ -878,6 +878,17 @@
. $NGX_AUTO/module
fi
+ if [ $HTTP_UPSTREAM_RANDOM = YES ]; then
+ ngx_module_name=ngx_http_upstream_random_module
+ ngx_module_incs=
+ ngx_module_deps=
+ ngx_module_srcs=src/http/modules/ngx_http_upstream_random_module.c
+ ngx_module_libs=
+ ngx_module_link=$HTTP_UPSTREAM_RANDOM
+
+ . $NGX_AUTO/module
+ fi
+
if [ $HTTP_UPSTREAM_KEEPALIVE = YES ]; then
ngx_module_name=ngx_http_upstream_keepalive_module
ngx_module_incs=
@@ -1143,6 +1154,16 @@
. $NGX_AUTO/module
fi
+ if [ $STREAM_UPSTREAM_RANDOM = YES ]; then
+ ngx_module_name=ngx_stream_upstream_random_module
+ ngx_module_deps=
+ ngx_module_srcs=src/stream/ngx_stream_upstream_random_module.c
+ ngx_module_libs=
+ ngx_module_link=$STREAM_UPSTREAM_RANDOM
+
+ . $NGX_AUTO/module
+ fi
+
if [ $STREAM_UPSTREAM_ZONE = YES ]; then
have=NGX_STREAM_UPSTREAM_ZONE . $NGX_AUTO/have
diff --git a/auto/options b/auto/options
index 8b02f54..6466741 100644
--- a/auto/options
+++ b/auto/options
@@ -102,6 +102,7 @@
HTTP_UPSTREAM_HASH=YES
HTTP_UPSTREAM_IP_HASH=YES
HTTP_UPSTREAM_LEAST_CONN=YES
+HTTP_UPSTREAM_RANDOM=YES
HTTP_UPSTREAM_KEEPALIVE=YES
HTTP_UPSTREAM_ZONE=YES
@@ -126,6 +127,7 @@
STREAM_RETURN=YES
STREAM_UPSTREAM_HASH=YES
STREAM_UPSTREAM_LEAST_CONN=YES
+STREAM_UPSTREAM_RANDOM=YES
STREAM_UPSTREAM_ZONE=YES
STREAM_SSL_PREREAD=NO
@@ -273,6 +275,8 @@
--without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;;
--without-http_upstream_least_conn_module)
HTTP_UPSTREAM_LEAST_CONN=NO ;;
+ --without-http_upstream_random_module)
+ HTTP_UPSTREAM_RANDOM=NO ;;
--without-http_upstream_keepalive_module) HTTP_UPSTREAM_KEEPALIVE=NO ;;
--without-http_upstream_zone_module) HTTP_UPSTREAM_ZONE=NO ;;
@@ -325,6 +329,8 @@
STREAM_UPSTREAM_HASH=NO ;;
--without-stream_upstream_least_conn_module)
STREAM_UPSTREAM_LEAST_CONN=NO ;;
+ --without-stream_upstream_random_module)
+ STREAM_UPSTREAM_RANDOM=NO ;;
--without-stream_upstream_zone_module)
STREAM_UPSTREAM_ZONE=NO ;;
@@ -485,6 +491,8 @@
disable ngx_http_upstream_ip_hash_module
--without-http_upstream_least_conn_module
disable ngx_http_upstream_least_conn_module
+ --without-http_upstream_random_module
+ disable ngx_http_upstream_random_module
--without-http_upstream_keepalive_module
disable ngx_http_upstream_keepalive_module
--without-http_upstream_zone_module
@@ -535,6 +543,8 @@
disable ngx_stream_upstream_hash_module
--without-stream_upstream_least_conn_module
disable ngx_stream_upstream_least_conn_module
+ --without-stream_upstream_random_module
+ disable ngx_stream_upstream_random_module
--without-stream_upstream_zone_module
disable ngx_stream_upstream_zone_module
diff --git a/build.bzl b/build.bzl
index 3572698..52e23f6 100644
--- a/build.bzl
+++ b/build.bzl
@@ -663,7 +663,7 @@
name = "nginx_pkgoss",
build_file_content = _PKGOSS_BUILD_FILE.format(nginx = nginx) +
_PKGOSS_BUILD_FILE_TAIL,
- commit = "6afed1e761a8a163cdbdef51063eee91330a93ec", # nginx-1.15.0
+ commit = "0b2d9e6711220cbafad9ee1263edb0a35ff9944a", # nginx-1.15.1
remote = "https://nginx.googlesource.com/nginx-pkgoss",
)
diff --git a/conf/mime.types b/conf/mime.types
index 8a2348a..2961256 100644
--- a/conf/mime.types
+++ b/conf/mime.types
@@ -24,7 +24,9 @@
image/x-jng jng;
image/x-ms-bmp bmp;
- application/font-woff woff;
+ font/woff woff;
+ font/woff2 woff2;
+
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 6bbff2c..6d4c5d9 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,73 @@
<change_log title="nginx">
+<changes ver="1.15.1" date="2018-07-03">
+
+<change type="feature">
+<para lang="ru">
+директива random в блоке upstream.
+</para>
+<para lang="en">
+the "random" directive inside the "upstream" block.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+улучшена производительность при использовании директив hash и ip_hash
+совместно с директивой zone.
+</para>
+<para lang="en">
+improved performance when using the "hash" and "ip_hash" directives
+with the "zone" directive.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+параметр reuseport директивы listen
+теперь использует SO_REUSEPORT_LB на FreeBSD 12.
+</para>
+<para lang="en">
+the "reuseport" parameter of the "listen" directive
+now uses SO_REUSEPORT_LB on FreeBSD 12.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+HTTP/2 server push не работал, если SSL терминировался прокси-сервером
+перед nginx'ом.
+</para>
+<para lang="en">
+HTTP/2 server push did not work if SSL was terminated by a proxy server
+in front of nginx.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+директива tcp_nopush всегда использовалась для соединений к бэкендам.
+</para>
+<para lang="en">
+the "tcp_nopush" directive was always used on backend connections.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+при отправке сохранённого на диск тела запроса на gRPC-бэкенд
+могли возникать ошибки.
+</para>
+<para lang="en">
+sending a disk-buffered request body to a gRPC backend
+might fail.
+</para>
+</change>
+
+</changes>
+
+
<changes ver="1.15.0" date="2018-06-05">
<change type="change">
diff --git a/src/core/nginx.h b/src/core/nginx.h
index 9c9679e..35d8749 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -13,8 +13,8 @@
#define NGINX_NAME "nginx"
#endif
-#define nginx_version 1015000
-#define NGINX_VERSION "1.15.0"
+#define nginx_version 1015001
+#define NGINX_VERSION "1.15.1"
#define NGINX_VER NGINX_NAME "/" NGINX_VERSION
#ifdef NGX_BUILD
diff --git a/src/core/ngx_connection.c b/src/core/ngx_connection.c
index dc60679..61ea4c2 100644
--- a/src/core/ngx_connection.c
+++ b/src/core/ngx_connection.c
@@ -281,6 +281,22 @@
reuseport = 0;
olen = sizeof(int);
+#ifdef SO_REUSEPORT_LB
+
+ if (getsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT_LB,
+ (void *) &reuseport, &olen)
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "getsockopt(SO_REUSEPORT_LB) %V failed, ignored",
+ &ls[i].addr_text);
+
+ } else {
+ ls[i].reuseport = reuseport ? 1 : 0;
+ }
+
+#else
+
if (getsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT,
(void *) &reuseport, &olen)
== -1)
@@ -292,6 +308,7 @@
} else {
ls[i].reuseport = reuseport ? 1 : 0;
}
+#endif
#endif
@@ -430,6 +447,20 @@
int reuseport = 1;
+#ifdef SO_REUSEPORT_LB
+
+ if (setsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT_LB,
+ (const void *) &reuseport, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "setsockopt(SO_REUSEPORT_LB) %V failed, "
+ "ignored",
+ &ls[i].addr_text);
+ }
+
+#else
+
if (setsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT,
(const void *) &reuseport, sizeof(int))
== -1)
@@ -438,6 +469,7 @@
"setsockopt(SO_REUSEPORT) %V failed, ignored",
&ls[i].addr_text);
}
+#endif
ls[i].add_reuseport = 0;
}
@@ -488,6 +520,27 @@
reuseport = 1;
+#ifdef SO_REUSEPORT_LB
+
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT_LB,
+ (const void *) &reuseport, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ "setsockopt(SO_REUSEPORT_LB) %V failed",
+ &ls[i].addr_text);
+
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ ngx_close_socket_n " %V failed",
+ &ls[i].addr_text);
+ }
+
+ return NGX_ERROR;
+ }
+
+#else
+
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT,
(const void *) &reuseport, sizeof(int))
== -1)
@@ -504,6 +557,7 @@
return NGX_ERROR;
}
+#endif
}
#endif
diff --git a/src/core/ngx_resolver.c b/src/core/ngx_resolver.c
index f904091..d969704 100644
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -141,25 +141,24 @@
ngx_pool_cleanup_t *cln;
ngx_resolver_connection_t *rec;
+ r = ngx_pcalloc(cf->pool, sizeof(ngx_resolver_t));
+ if (r == NULL) {
+ return NULL;
+ }
+
+ r->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));
+ if (r->event == NULL) {
+ return NULL;
+ }
+
cln = ngx_pool_cleanup_add(cf->pool, 0);
if (cln == NULL) {
return NULL;
}
cln->handler = ngx_resolver_cleanup;
-
- r = ngx_calloc(sizeof(ngx_resolver_t), cf->log);
- if (r == NULL) {
- return NULL;
- }
-
cln->data = r;
- r->event = ngx_calloc(sizeof(ngx_event_t), cf->log);
- if (r->event == NULL) {
- return NULL;
- }
-
ngx_rbtree_init(&r->name_rbtree, &r->name_sentinel,
ngx_resolver_rbtree_insert_value);
@@ -276,6 +275,11 @@
}
}
+ if (n && r->connections.nelts == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "no name servers defined");
+ return NULL;
+ }
+
return r;
}
@@ -288,52 +292,42 @@
ngx_uint_t i;
ngx_resolver_connection_t *rec;
- if (r) {
- ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
- "cleanup resolver");
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "cleanup resolver");
- ngx_resolver_cleanup_tree(r, &r->name_rbtree);
+ ngx_resolver_cleanup_tree(r, &r->name_rbtree);
- ngx_resolver_cleanup_tree(r, &r->srv_rbtree);
+ ngx_resolver_cleanup_tree(r, &r->srv_rbtree);
- ngx_resolver_cleanup_tree(r, &r->addr_rbtree);
+ ngx_resolver_cleanup_tree(r, &r->addr_rbtree);
#if (NGX_HAVE_INET6)
- ngx_resolver_cleanup_tree(r, &r->addr6_rbtree);
+ ngx_resolver_cleanup_tree(r, &r->addr6_rbtree);
#endif
- if (r->event) {
- if (r->event->timer_set) {
- ngx_del_timer(r->event);
- }
+ if (r->event->timer_set) {
+ ngx_del_timer(r->event);
+ }
- ngx_free(r->event);
+ rec = r->connections.elts;
+
+ for (i = 0; i < r->connections.nelts; i++) {
+ if (rec[i].udp) {
+ ngx_close_connection(rec[i].udp);
}
-
- rec = r->connections.elts;
-
- for (i = 0; i < r->connections.nelts; i++) {
- if (rec[i].udp) {
- ngx_close_connection(rec[i].udp);
- }
-
- if (rec[i].tcp) {
- ngx_close_connection(rec[i].tcp);
- }
-
- if (rec[i].read_buf) {
- ngx_resolver_free(r, rec[i].read_buf->start);
- ngx_resolver_free(r, rec[i].read_buf);
- }
-
- if (rec[i].write_buf) {
- ngx_resolver_free(r, rec[i].write_buf->start);
- ngx_resolver_free(r, rec[i].write_buf);
- }
+ if (rec[i].tcp) {
+ ngx_close_connection(rec[i].tcp);
}
- ngx_free(r);
+ if (rec[i].read_buf) {
+ ngx_resolver_free(r, rec[i].read_buf->start);
+ ngx_resolver_free(r, rec[i].read_buf);
+ }
+
+ if (rec[i].write_buf) {
+ ngx_resolver_free(r, rec[i].write_buf->start);
+ ngx_resolver_free(r, rec[i].write_buf);
+ }
}
}
@@ -4402,7 +4396,7 @@
if (c == NULL) {
if (ngx_close_socket(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,
- ngx_close_socket_n "failed");
+ ngx_close_socket_n " failed");
}
return NGX_ERROR;
@@ -4488,7 +4482,7 @@
if (c == NULL) {
if (ngx_close_socket(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,
- ngx_close_socket_n "failed");
+ ngx_close_socket_n " failed");
}
return NGX_ERROR;
diff --git a/src/event/ngx_event_connect.c b/src/event/ngx_event_connect.c
index e7f28c9..714fc47 100644
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -55,7 +55,7 @@
if (c == NULL) {
if (ngx_close_socket(s) == -1) {
ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
- ngx_close_socket_n "failed");
+ ngx_close_socket_n " failed");
}
return NGX_ERROR;
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
index 59cdf79..f53fe24 100644
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -35,7 +35,6 @@
static ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl,
ngx_str_t *sess_ctx);
-ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
static int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn,
ngx_ssl_session_t *sess);
static ngx_ssl_session_t *ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
index 300d927..0baa85f 100644
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -3868,6 +3868,7 @@
static ngx_chain_t *
ngx_http_grpc_get_buf(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx)
{
+ u_char *start;
ngx_buf_t *b;
ngx_chain_t *cl;
@@ -3877,29 +3878,33 @@
}
b = cl->buf;
+ start = b->start;
- b->tag = (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter;
- b->temporary = 1;
- b->flush = 1;
-
- if (b->start == NULL) {
+ if (start == NULL) {
/*
* each buffer is large enough to hold two window update
* frames in a row
*/
- b->start = ngx_palloc(r->pool, 2 * sizeof(ngx_http_grpc_frame_t) + 8);
- if (b->start == NULL) {
+ start = ngx_palloc(r->pool, 2 * sizeof(ngx_http_grpc_frame_t) + 8);
+ if (start == NULL) {
return NULL;
}
- b->pos = b->start;
- b->last = b->start;
-
- b->end = b->start + 2 * sizeof(ngx_http_grpc_frame_t) + 8;
}
+ ngx_memzero(b, sizeof(ngx_buf_t));
+
+ b->start = start;
+ b->pos = start;
+ b->last = start;
+ b->end = start + 2 * sizeof(ngx_http_grpc_frame_t) + 8;
+
+ b->tag = (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter;
+ b->temporary = 1;
+ b->flush = 1;
+
return cl;
}
diff --git a/src/http/modules/ngx_http_upstream_hash_module.c b/src/http/modules/ngx_http_upstream_hash_module.c
index d67f34d..6c247b5 100644
--- a/src/http/modules/ngx_http_upstream_hash_module.c
+++ b/src/http/modules/ngx_http_upstream_hash_module.c
@@ -176,7 +176,7 @@
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get hash peer, try: %ui", pc->tries);
- ngx_http_upstream_rr_peers_wlock(hp->rrp.peers);
+ ngx_http_upstream_rr_peers_rlock(hp->rrp.peers);
if (hp->tries > 20 || hp->rrp.peers->single) {
ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
@@ -228,10 +228,13 @@
goto next;
}
+ ngx_http_upstream_rr_peer_lock(hp->rrp.peers, peer);
+
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get hash peer, value:%uD, peer:%ui", hp->hash, p);
if (peer->down) {
+ ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
goto next;
}
@@ -239,10 +242,12 @@
&& peer->fails >= peer->max_fails
&& now - peer->checked <= peer->fail_timeout)
{
+ ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
goto next;
}
if (peer->max_conns && peer->conns >= peer->max_conns) {
+ ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
goto next;
}
@@ -268,6 +273,7 @@
peer->checked = now;
}
+ ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);
ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);
hp->rrp.tried[n] |= m;
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
index 296108f..1fa01d9 100644
--- a/src/http/modules/ngx_http_upstream_ip_hash_module.c
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
@@ -161,7 +161,7 @@
/* TODO: cached */
- ngx_http_upstream_rr_peers_wlock(iphp->rrp.peers);
+ ngx_http_upstream_rr_peers_rlock(iphp->rrp.peers);
if (iphp->tries > 20 || iphp->rrp.peers->single) {
ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
@@ -201,7 +201,10 @@
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
"get ip hash peer, hash: %ui %04XL", p, (uint64_t) m);
+ ngx_http_upstream_rr_peer_lock(iphp->rrp.peers, peer);
+
if (peer->down) {
+ ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);
goto next;
}
@@ -209,10 +212,12 @@
&& peer->fails >= peer->max_fails
&& now - peer->checked <= peer->fail_timeout)
{
+ ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);
goto next;
}
if (peer->max_conns && peer->conns >= peer->max_conns) {
+ ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);
goto next;
}
@@ -238,6 +243,7 @@
peer->checked = now;
}
+ ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);
ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);
iphp->rrp.tried[n] |= m;
diff --git a/src/http/modules/ngx_http_upstream_random_module.c b/src/http/modules/ngx_http_upstream_random_module.c
new file mode 100644
index 0000000..6f169c8
--- /dev/null
+++ b/src/http/modules/ngx_http_upstream_random_module.c
@@ -0,0 +1,502 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+ ngx_http_upstream_rr_peer_t *peer;
+ ngx_uint_t range;
+} ngx_http_upstream_random_range_t;
+
+
+typedef struct {
+ ngx_uint_t two;
+ ngx_http_upstream_random_range_t *ranges;
+} ngx_http_upstream_random_srv_conf_t;
+
+
+typedef struct {
+ /* the round robin data must be first */
+ ngx_http_upstream_rr_peer_data_t rrp;
+
+ ngx_http_upstream_random_srv_conf_t *conf;
+ u_char tries;
+} ngx_http_upstream_random_peer_data_t;
+
+
+static ngx_int_t ngx_http_upstream_init_random(ngx_conf_t *cf,
+ ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_update_random(ngx_pool_t *pool,
+ ngx_http_upstream_srv_conf_t *us);
+
+static ngx_int_t ngx_http_upstream_init_random_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_get_random_peer(ngx_peer_connection_t *pc,
+ void *data);
+static ngx_int_t ngx_http_upstream_get_random2_peer(ngx_peer_connection_t *pc,
+ void *data);
+static ngx_uint_t ngx_http_upstream_peek_random_peer(
+ ngx_http_upstream_rr_peers_t *peers,
+ ngx_http_upstream_random_peer_data_t *rp);
+static void *ngx_http_upstream_random_create_conf(ngx_conf_t *cf);
+static char *ngx_http_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_http_upstream_random_commands[] = {
+
+ { ngx_string("random"),
+ NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE12,
+ ngx_http_upstream_random,
+ NGX_HTTP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_http_module_t ngx_http_upstream_random_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_http_upstream_random_create_conf, /* create server configuration */
+ NULL, /* merge server configuration */
+
+ NULL, /* create location configuration */
+ NULL /* merge location configuration */
+};
+
+
+ngx_module_t ngx_http_upstream_random_module = {
+ NGX_MODULE_V1,
+ &ngx_http_upstream_random_module_ctx, /* module context */
+ ngx_http_upstream_random_commands, /* module directives */
+ NGX_HTTP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_upstream_init_random(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, "init random");
+
+ if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ us->peer.init = ngx_http_upstream_init_random_peer;
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ if (us->shm_zone) {
+ return NGX_OK;
+ }
+#endif
+
+ return ngx_http_upstream_update_random(cf->pool, us);
+}
+
+
+static ngx_int_t
+ngx_http_upstream_update_random(ngx_pool_t *pool,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ size_t size;
+ ngx_uint_t i, total_weight;
+ ngx_http_upstream_rr_peer_t *peer;
+ ngx_http_upstream_rr_peers_t *peers;
+ ngx_http_upstream_random_range_t *ranges;
+ ngx_http_upstream_random_srv_conf_t *rcf;
+
+ rcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_random_module);
+
+ peers = us->peer.data;
+
+ size = peers->number * sizeof(ngx_http_upstream_random_range_t);
+
+ ranges = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log);
+ if (ranges == NULL) {
+ return NGX_ERROR;
+ }
+
+ total_weight = 0;
+
+ for (peer = peers->peer, i = 0; peer; peer = peer->next, i++) {
+ ranges[i].peer = peer;
+ ranges[i].range = total_weight;
+ total_weight += peer->weight;
+ }
+
+ rcf->ranges = ranges;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_random_peer(ngx_http_request_t *r,
+ ngx_http_upstream_srv_conf_t *us)
+{
+ ngx_http_upstream_random_srv_conf_t *rcf;
+ ngx_http_upstream_random_peer_data_t *rp;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "init random peer");
+
+ rcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_random_module);
+
+ rp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_random_peer_data_t));
+ if (rp == NULL) {
+ return NGX_ERROR;
+ }
+
+ r->upstream->peer.data = &rp->rrp;
+
+ if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (rcf->two) {
+ r->upstream->peer.get = ngx_http_upstream_get_random2_peer;
+
+ } else {
+ r->upstream->peer.get = ngx_http_upstream_get_random_peer;
+ }
+
+ rp->conf = rcf;
+ rp->tries = 0;
+
+ ngx_http_upstream_rr_peers_rlock(rp->rrp.peers);
+
+#if (NGX_HTTP_UPSTREAM_ZONE)
+ if (rp->rrp.peers->shpool && rcf->ranges == NULL) {
+ if (ngx_http_upstream_update_random(NULL, us) != NGX_OK) {
+ ngx_http_upstream_rr_peers_unlock(rp->rrp.peers);
+ return NGX_ERROR;
+ }
+ }
+#endif
+
+ ngx_http_upstream_rr_peers_unlock(rp->rrp.peers);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_random_peer_data_t *rp = data;
+
+ time_t now;
+ uintptr_t m;
+ ngx_uint_t i, n;
+ ngx_http_upstream_rr_peer_t *peer;
+ ngx_http_upstream_rr_peers_t *peers;
+ ngx_http_upstream_rr_peer_data_t *rrp;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get random peer, try: %ui", pc->tries);
+
+ rrp = &rp->rrp;
+ peers = rrp->peers;
+
+ ngx_http_upstream_rr_peers_rlock(peers);
+
+ if (rp->tries > 20 || peers->single) {
+ ngx_http_upstream_rr_peers_unlock(peers);
+ return ngx_http_upstream_get_round_robin_peer(pc, rrp);
+ }
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ now = ngx_time();
+
+ for ( ;; ) {
+
+ i = ngx_http_upstream_peek_random_peer(peers, rp);
+
+ peer = rp->conf->ranges[i].peer;
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (rrp->tried[n] & m) {
+ goto next;
+ }
+
+ ngx_http_upstream_rr_peer_lock(peers, peer);
+
+ if (peer->down) {
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ goto next;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ goto next;
+ }
+
+ if (peer->max_conns && peer->conns >= peer->max_conns) {
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ goto next;
+ }
+
+ break;
+
+ next:
+
+ if (++rp->tries > 20) {
+ ngx_http_upstream_rr_peers_unlock(peers);
+ return ngx_http_upstream_get_round_robin_peer(pc, rrp);
+ }
+ }
+
+ rrp->current = peer;
+
+ if (now - peer->checked > peer->fail_timeout) {
+ peer->checked = now;
+ }
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ peer->conns++;
+
+ ngx_http_upstream_rr_peer_unlock(peers, peer);
+ ngx_http_upstream_rr_peers_unlock(peers);
+
+ rrp->tried[n] |= m;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_http_upstream_random_peer_data_t *rp = data;
+
+ time_t now;
+ uintptr_t m;
+ ngx_uint_t i, n, p;
+ ngx_http_upstream_rr_peer_t *peer, *prev;
+ ngx_http_upstream_rr_peers_t *peers;
+ ngx_http_upstream_rr_peer_data_t *rrp;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+ "get random2 peer, try: %ui", pc->tries);
+
+ rrp = &rp->rrp;
+ peers = rrp->peers;
+
+ ngx_http_upstream_rr_peers_wlock(peers);
+
+ if (rp->tries > 20 || peers->single) {
+ ngx_http_upstream_rr_peers_unlock(peers);
+ return ngx_http_upstream_get_round_robin_peer(pc, rrp);
+ }
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ now = ngx_time();
+
+ prev = NULL;
+
+#if (NGX_SUPPRESS_WARN)
+ p = 0;
+#endif
+
+ for ( ;; ) {
+
+ i = ngx_http_upstream_peek_random_peer(peers, rp);
+
+ peer = rp->conf->ranges[i].peer;
+
+ if (peer == prev) {
+ goto next;
+ }
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (rrp->tried[n] & m) {
+ goto next;
+ }
+
+ if (peer->down) {
+ goto next;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ goto next;
+ }
+
+ if (peer->max_conns && peer->conns >= peer->max_conns) {
+ goto next;
+ }
+
+ if (prev) {
+ if (peer->conns * prev->weight > prev->conns * peer->weight) {
+ peer = prev;
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
+ }
+
+ break;
+ }
+
+ prev = peer;
+ p = i;
+
+ next:
+
+ if (++rp->tries > 20) {
+ ngx_http_upstream_rr_peers_unlock(peers);
+ return ngx_http_upstream_get_round_robin_peer(pc, rrp);
+ }
+ }
+
+ rrp->current = peer;
+
+ if (now - peer->checked > peer->fail_timeout) {
+ peer->checked = now;
+ }
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ peer->conns++;
+
+ ngx_http_upstream_rr_peers_unlock(peers);
+
+ rrp->tried[n] |= m;
+
+ return NGX_OK;
+}
+
+
+static ngx_uint_t
+ngx_http_upstream_peek_random_peer(ngx_http_upstream_rr_peers_t *peers,
+ ngx_http_upstream_random_peer_data_t *rp)
+{
+ ngx_uint_t i, j, k, x;
+
+ x = ngx_random() % peers->total_weight;
+
+ i = 0;
+ j = peers->number;
+
+ while (j - i > 1) {
+ k = (i + j) / 2;
+
+ if (x < rp->conf->ranges[k].range) {
+ j = k;
+
+ } else {
+ i = k;
+ }
+ }
+
+ return i;
+}
+
+
+static void *
+ngx_http_upstream_random_create_conf(ngx_conf_t *cf)
+{
+ ngx_http_upstream_random_srv_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_random_srv_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->two = 0;
+ */
+
+ return conf;
+}
+
+
+static char *
+ngx_http_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_upstream_random_srv_conf_t *rcf = conf;
+
+ ngx_str_t *value;
+ ngx_http_upstream_srv_conf_t *uscf;
+
+ uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+
+ if (uscf->peer.init_upstream) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "load balancing method redefined");
+ }
+
+ uscf->peer.init_upstream = ngx_http_upstream_init_random;
+
+ uscf->flags = NGX_HTTP_UPSTREAM_CREATE
+ |NGX_HTTP_UPSTREAM_WEIGHT
+ |NGX_HTTP_UPSTREAM_MAX_CONNS
+ |NGX_HTTP_UPSTREAM_MAX_FAILS
+ |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
+ |NGX_HTTP_UPSTREAM_DOWN;
+
+ if (cf->args->nelts == 1) {
+ return NGX_CONF_OK;
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "two") == 0) {
+ rcf->two = 1;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (cf->args->nelts == 2) {
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strcmp(value[2].data, "least_conn") != 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 7a57739..b27dcb2 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -2318,6 +2318,7 @@
sr->unparsed_uri = r->unparsed_uri;
sr->method_name = ngx_http_core_get_method;
sr->http_protocol = r->http_protocol;
+ sr->schema = r->schema;
ngx_http_set_exten(sr);
diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c
index 844054c..d9a1dbe 100644
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -307,6 +307,11 @@
break;
}
+ if ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')
+ {
+ break;
+ }
+
switch (ch) {
case ':':
r->schema_end = p;
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 0218ad2..657f5cd 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1141,7 +1141,12 @@
return;
}
- if (r->host_start && r->host_end) {
+ if (r->schema_end) {
+ r->schema.len = r->schema_end - r->schema_start;
+ r->schema.data = r->schema_start;
+ }
+
+ if (r->host_end) {
host.len = r->host_end - r->host_start;
host.data = r->host_start;
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index 4f798d3..1dd2505 100644
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -412,6 +412,7 @@
ngx_str_t method_name;
ngx_str_t http_protocol;
+ ngx_str_t schema;
ngx_chain_t *out;
ngx_http_request_t *main;
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 87a9b87..ca3c2e3 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -1560,6 +1560,10 @@
c->sendfile &= r->connection->sendfile;
u->output.sendfile = c->sendfile;
+ if (r->connection->tcp_nopush == NGX_TCP_NOPUSH_DISABLED) {
+ c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
+ }
+
if (c->pool == NULL) {
/* we need separate pool here to be able to cache SSL connections */
@@ -2012,6 +2016,18 @@
return;
}
+ if (c->write->ready && c->tcp_nopush == NGX_TCP_NOPUSH_SET) {
+ if (ngx_tcp_push(c->fd) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,
+ ngx_tcp_push_n " failed");
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;
+ }
+
return;
}
@@ -2909,7 +2925,8 @@
}
if (r->request_body && r->request_body->temp_file
- && r == r->main && !r->preserve_body)
+ && r == r->main && !r->preserve_body
+ && !u->conf->preserve_output)
{
ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd);
r->request_body->temp_file->file.fd = NGX_INVALID_FILE;
diff --git a/src/http/v2/ngx_http_v2.c b/src/http/v2/ngx_http_v2.c
index 360905c..e2be480 100644
--- a/src/http/v2/ngx_http_v2.c
+++ b/src/http/v2/ngx_http_v2.c
@@ -2653,18 +2653,13 @@
r->method_name = ngx_http_core_get_method;
r->method = NGX_HTTP_GET;
- r->schema_start = (u_char *) "https";
-
-#if (NGX_HTTP_SSL)
- if (fc->ssl) {
- r->schema_end = r->schema_start + 5;
-
- } else
-#endif
- {
- r->schema_end = r->schema_start + 4;
+ r->schema.data = ngx_pstrdup(pool, &parent->request->schema);
+ if (r->schema.data == NULL) {
+ goto close;
}
+ r->schema.len = parent->request->schema.len;
+
value.data = ngx_pstrdup(pool, path);
if (value.data == NULL) {
goto close;
@@ -3524,7 +3519,10 @@
static ngx_int_t
ngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)
{
- if (r->schema_start) {
+ u_char c, ch;
+ ngx_uint_t i;
+
+ if (r->schema.len) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent duplicate :scheme header");
@@ -3538,8 +3536,27 @@
return NGX_DECLINED;
}
- r->schema_start = value->data;
- r->schema_end = value->data + value->len;
+ for (i = 0; i < value->len; i++) {
+ ch = value->data[i];
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ continue;
+ }
+
+ if (((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')
+ && i > 0)
+ {
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
+ "client sent invalid :scheme header: \"%V\"", value);
+
+ return NGX_DECLINED;
+ }
+
+ r->schema = *value;
return NGX_OK;
}
@@ -3602,14 +3619,14 @@
static const u_char ending[] = " HTTP/2.0";
if (r->method_name.len == 0
- || r->schema_start == NULL
+ || r->schema.len == 0
|| r->unparsed_uri.len == 0)
{
if (r->method_name.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :method header");
- } else if (r->schema_start == NULL) {
+ } else if (r->schema.len == 0) {
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
"client sent no :scheme header");
diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c
index 1b19b8a..9a6b4f3 100644
--- a/src/http/v2/ngx_http_v2_filter_module.c
+++ b/src/http/v2/ngx_http_v2_filter_module.c
@@ -953,15 +953,15 @@
ph = ngx_http_v2_push_headers;
+ len = ngx_max(r->schema.len, path->len);
+
if (binary[0].len) {
- tmp = ngx_palloc(r->pool, path->len);
+ tmp = ngx_palloc(r->pool, len);
if (tmp == NULL) {
return NGX_ERROR;
}
} else {
- len = path->len;
-
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);
@@ -1003,7 +1003,7 @@
len = (h2c->table_update ? 1 : 0)
+ 1
+ 1 + NGX_HTTP_V2_INT_OCTETS + path->len
- + 1;
+ + 1 + NGX_HTTP_V2_INT_OCTETS + r->schema.len;
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
len += binary[i].len;
@@ -1034,18 +1034,20 @@
*pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);
pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);
-#if (NGX_HTTP_SSL)
- if (fc->ssl) {
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
- "http2 push header: \":scheme: https\"");
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,
+ "http2 push header: \":scheme: %V\"", &r->schema);
+
+ if (r->schema.len == 5 && ngx_strncmp(r->schema.data, "https", 5) == 0) {
*pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);
- } else
-#endif
+ } else if (r->schema.len == 4
+ && ngx_strncmp(r->schema.data, "http", 4) == 0)
{
- ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,
- "http2 push header: \":scheme: http\"");
*pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
+
+ } else {
+ *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);
+ pos = ngx_http_v2_write_value(pos, r->schema.data, r->schema.len, tmp);
}
for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {
diff --git a/src/ngx_modules.c b/src/ngx_modules.c
index cd1db63..dea7b9d 100644
--- a/src/ngx_modules.c
+++ b/src/ngx_modules.c
@@ -159,6 +159,9 @@
#if (NGX_HTTP_UPSTREAM_LEAST_CONN)
extern ngx_module_t ngx_http_upstream_least_conn_module;
#endif
+#if (NGX_HTTP_UPSTREAM_RANDOM)
+extern ngx_module_t ngx_http_upstream_random_module;
+#endif
#if (NGX_HTTP_UPSTREAM_KEEPALIVE)
extern ngx_module_t ngx_http_upstream_keepalive_module;
#endif
@@ -279,6 +282,9 @@
#if (NGX_STREAM_UPSTREAM_LEAST_CONN)
extern ngx_module_t ngx_stream_upstream_least_conn_module;
#endif
+#if (NGX_STREAM_UPSTREAM_RANDOM)
+extern ngx_module_t ngx_stream_upstream_random_module;
+#endif
#if (NGX_STREAM_UPSTREAM_ZONE)
extern ngx_module_t ngx_stream_upstream_zone_module;
#endif
@@ -442,6 +448,9 @@
#if (NGX_HTTP_UPSTREAM_LEAST_CONN)
&ngx_http_upstream_least_conn_module,
#endif
+#if (NGX_HTTP_UPSTREAM_RANDOM)
+ &ngx_http_upstream_random_module,
+#endif
#if (NGX_HTTP_UPSTREAM_KEEPALIVE)
&ngx_http_upstream_keepalive_module,
#endif
@@ -562,6 +571,9 @@
#if (NGX_STREAM_UPSTREAM_LEAST_CONN)
&ngx_stream_upstream_least_conn_module,
#endif
+#if (NGX_STREAM_UPSTREAM_RANDOM)
+ &ngx_stream_upstream_random_module,
+#endif
#if (NGX_STREAM_UPSTREAM_ZONE)
&ngx_stream_upstream_zone_module,
#endif
@@ -727,6 +739,9 @@
#if (NGX_HTTP_UPSTREAM_LEAST_CONN)
"ngx_http_upstream_least_conn_module",
#endif
+#if (NGX_HTTP_UPSTREAM_RANDOM)
+ "ngx_http_upstream_random_module",
+#endif
#if (NGX_HTTP_UPSTREAM_KEEPALIVE)
"ngx_http_upstream_keepalive_module",
#endif
@@ -847,6 +862,9 @@
#if (NGX_STREAM_UPSTREAM_LEAST_CONN)
"ngx_stream_upstream_least_conn_module",
#endif
+#if (NGX_STREAM_UPSTREAM_RANDOM)
+ "ngx_stream_upstream_random_module",
+#endif
#if (NGX_STREAM_UPSTREAM_ZONE)
"ngx_stream_upstream_zone_module",
#endif
@@ -1070,6 +1088,9 @@
#if !(NGX_HTTP_UPSTREAM_LEAST_CONN)
ngx_write_stderr(" --without-http_upstream_least_conn_module");
#endif
+#if !(NGX_HTTP_UPSTREAM_RANDOM)
+ ngx_write_stderr(" --without-http_upstream_random_module");
+#endif
#if !(NGX_HTTP_UPSTREAM_ZONE)
ngx_write_stderr(" --without-http_upstream_zone_module");
#endif
@@ -1134,6 +1155,9 @@
#if !(NGX_STREAM_UPSTREAM_LEAST_CONN)
ngx_write_stderr(" --without-stream_upstream_least_conn_module");
#endif
+#if !(NGX_STREAM_UPSTREAM_RANDOM)
+ ngx_write_stderr(" --without-stream_upstream_random_module");
+#endif
#if !(NGX_STREAM_UPSTREAM_ZONE)
ngx_write_stderr(" --without-stream_upstream_zone_module");
#endif
diff --git a/src/stream/ngx_stream_upstream_hash_module.c b/src/stream/ngx_stream_upstream_hash_module.c
index 79ad742..4fa9a2d 100644
--- a/src/stream/ngx_stream_upstream_hash_module.c
+++ b/src/stream/ngx_stream_upstream_hash_module.c
@@ -176,7 +176,7 @@
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
"get hash peer, try: %ui", pc->tries);
- ngx_stream_upstream_rr_peers_wlock(hp->rrp.peers);
+ ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers);
if (hp->tries > 20 || hp->rrp.peers->single) {
ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
@@ -227,10 +227,13 @@
goto next;
}
+ ngx_stream_upstream_rr_peer_lock(hp->rrp.peers, peer);
+
ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,
"get hash peer, value:%uD, peer:%ui", hp->hash, p);
if (peer->down) {
+ ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer);
goto next;
}
@@ -238,10 +241,12 @@
&& peer->fails >= peer->max_fails
&& now - peer->checked <= peer->fail_timeout)
{
+ ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer);
goto next;
}
if (peer->max_conns && peer->conns >= peer->max_conns) {
+ ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer);
goto next;
}
@@ -267,6 +272,7 @@
peer->checked = now;
}
+ ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer);
ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);
hp->rrp.tried[n] |= m;
diff --git a/src/stream/ngx_stream_upstream_random_module.c b/src/stream/ngx_stream_upstream_random_module.c
new file mode 100644
index 0000000..402c1b2
--- /dev/null
+++ b/src/stream/ngx_stream_upstream_random_module.c
@@ -0,0 +1,502 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_stream.h>
+
+
+typedef struct {
+ ngx_stream_upstream_rr_peer_t *peer;
+ ngx_uint_t range;
+} ngx_stream_upstream_random_range_t;
+
+
+typedef struct {
+ ngx_uint_t two;
+ ngx_stream_upstream_random_range_t *ranges;
+} ngx_stream_upstream_random_srv_conf_t;
+
+
+typedef struct {
+ /* the round robin data must be first */
+ ngx_stream_upstream_rr_peer_data_t rrp;
+
+ ngx_stream_upstream_random_srv_conf_t *conf;
+ u_char tries;
+} ngx_stream_upstream_random_peer_data_t;
+
+
+static ngx_int_t ngx_stream_upstream_init_random(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us);
+static ngx_int_t ngx_stream_upstream_update_random(ngx_pool_t *pool,
+ ngx_stream_upstream_srv_conf_t *us);
+
+static ngx_int_t ngx_stream_upstream_init_random_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us);
+static ngx_int_t ngx_stream_upstream_get_random_peer(ngx_peer_connection_t *pc,
+ void *data);
+static ngx_int_t ngx_stream_upstream_get_random2_peer(ngx_peer_connection_t *pc,
+ void *data);
+static ngx_uint_t ngx_stream_upstream_peek_random_peer(
+ ngx_stream_upstream_rr_peers_t *peers,
+ ngx_stream_upstream_random_peer_data_t *rp);
+static void *ngx_stream_upstream_random_create_conf(ngx_conf_t *cf);
+static char *ngx_stream_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_stream_upstream_random_commands[] = {
+
+ { ngx_string("random"),
+ NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE12,
+ ngx_stream_upstream_random,
+ NGX_STREAM_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_stream_module_t ngx_stream_upstream_random_module_ctx = {
+ NULL, /* preconfiguration */
+ NULL, /* postconfiguration */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_stream_upstream_random_create_conf, /* create server configuration */
+ NULL /* merge server configuration */
+};
+
+
+ngx_module_t ngx_stream_upstream_random_module = {
+ NGX_MODULE_V1,
+ &ngx_stream_upstream_random_module_ctx, /* module context */
+ ngx_stream_upstream_random_commands, /* module directives */
+ NGX_STREAM_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_stream_upstream_init_random(ngx_conf_t *cf,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0, "init random");
+
+ if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ us->peer.init = ngx_stream_upstream_init_random_peer;
+
+#if (NGX_STREAM_UPSTREAM_ZONE)
+ if (us->shm_zone) {
+ return NGX_OK;
+ }
+#endif
+
+ return ngx_stream_upstream_update_random(cf->pool, us);
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_update_random(ngx_pool_t *pool,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ size_t size;
+ ngx_uint_t i, total_weight;
+ ngx_stream_upstream_rr_peer_t *peer;
+ ngx_stream_upstream_rr_peers_t *peers;
+ ngx_stream_upstream_random_range_t *ranges;
+ ngx_stream_upstream_random_srv_conf_t *rcf;
+
+ rcf = ngx_stream_conf_upstream_srv_conf(us,
+ ngx_stream_upstream_random_module);
+ peers = us->peer.data;
+
+ size = peers->number * sizeof(ngx_stream_upstream_random_range_t);
+
+ ranges = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log);
+ if (ranges == NULL) {
+ return NGX_ERROR;
+ }
+
+ total_weight = 0;
+
+ for (peer = peers->peer, i = 0; peer; peer = peer->next, i++) {
+ ranges[i].peer = peer;
+ ranges[i].range = total_weight;
+ total_weight += peer->weight;
+ }
+
+ rcf->ranges = ranges;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_init_random_peer(ngx_stream_session_t *s,
+ ngx_stream_upstream_srv_conf_t *us)
+{
+ ngx_stream_upstream_random_srv_conf_t *rcf;
+ ngx_stream_upstream_random_peer_data_t *rp;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
+ "init random peer");
+
+ rcf = ngx_stream_conf_upstream_srv_conf(us,
+ ngx_stream_upstream_random_module);
+
+ rp = ngx_palloc(s->connection->pool,
+ sizeof(ngx_stream_upstream_random_peer_data_t));
+ if (rp == NULL) {
+ return NGX_ERROR;
+ }
+
+ s->upstream->peer.data = &rp->rrp;
+
+ if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (rcf->two) {
+ s->upstream->peer.get = ngx_stream_upstream_get_random2_peer;
+
+ } else {
+ s->upstream->peer.get = ngx_stream_upstream_get_random_peer;
+ }
+
+ rp->conf = rcf;
+ rp->tries = 0;
+
+ ngx_stream_upstream_rr_peers_rlock(rp->rrp.peers);
+
+#if (NGX_STREAM_UPSTREAM_ZONE)
+ if (rp->rrp.peers->shpool && rcf->ranges == NULL) {
+ if (ngx_stream_upstream_update_random(NULL, us) != NGX_OK) {
+ ngx_stream_upstream_rr_peers_unlock(rp->rrp.peers);
+ return NGX_ERROR;
+ }
+ }
+#endif
+
+ ngx_stream_upstream_rr_peers_unlock(rp->rrp.peers);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_stream_upstream_random_peer_data_t *rp = data;
+
+ time_t now;
+ uintptr_t m;
+ ngx_uint_t i, n;
+ ngx_stream_upstream_rr_peer_t *peer;
+ ngx_stream_upstream_rr_peers_t *peers;
+ ngx_stream_upstream_rr_peer_data_t *rrp;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get random peer, try: %ui", pc->tries);
+
+ rrp = &rp->rrp;
+ peers = rrp->peers;
+
+ ngx_stream_upstream_rr_peers_rlock(peers);
+
+ if (rp->tries > 20 || peers->single) {
+ ngx_stream_upstream_rr_peers_unlock(peers);
+ return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
+ }
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ now = ngx_time();
+
+ for ( ;; ) {
+
+ i = ngx_stream_upstream_peek_random_peer(peers, rp);
+
+ peer = rp->conf->ranges[i].peer;
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (rrp->tried[n] & m) {
+ goto next;
+ }
+
+ ngx_stream_upstream_rr_peer_lock(peers, peer);
+
+ if (peer->down) {
+ ngx_stream_upstream_rr_peer_unlock(peers, peer);
+ goto next;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ ngx_stream_upstream_rr_peer_unlock(peers, peer);
+ goto next;
+ }
+
+ if (peer->max_conns && peer->conns >= peer->max_conns) {
+ ngx_stream_upstream_rr_peer_unlock(peers, peer);
+ goto next;
+ }
+
+ break;
+
+ next:
+
+ if (++rp->tries > 20) {
+ ngx_stream_upstream_rr_peers_unlock(peers);
+ return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
+ }
+ }
+
+ rrp->current = peer;
+
+ if (now - peer->checked > peer->fail_timeout) {
+ peer->checked = now;
+ }
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ peer->conns++;
+
+ ngx_stream_upstream_rr_peer_unlock(peers, peer);
+ ngx_stream_upstream_rr_peers_unlock(peers);
+
+ rrp->tried[n] |= m;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_stream_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_stream_upstream_random_peer_data_t *rp = data;
+
+ time_t now;
+ uintptr_t m;
+ ngx_uint_t i, n, p;
+ ngx_stream_upstream_rr_peer_t *peer, *prev;
+ ngx_stream_upstream_rr_peers_t *peers;
+ ngx_stream_upstream_rr_peer_data_t *rrp;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,
+ "get random2 peer, try: %ui", pc->tries);
+
+ rrp = &rp->rrp;
+ peers = rrp->peers;
+
+ ngx_stream_upstream_rr_peers_wlock(peers);
+
+ if (rp->tries > 20 || peers->single) {
+ ngx_stream_upstream_rr_peers_unlock(peers);
+ return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
+ }
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ now = ngx_time();
+
+ prev = NULL;
+
+#if (NGX_SUPPRESS_WARN)
+ p = 0;
+#endif
+
+ for ( ;; ) {
+
+ i = ngx_stream_upstream_peek_random_peer(peers, rp);
+
+ peer = rp->conf->ranges[i].peer;
+
+ if (peer == prev) {
+ goto next;
+ }
+
+ n = i / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));
+
+ if (rrp->tried[n] & m) {
+ goto next;
+ }
+
+ if (peer->down) {
+ goto next;
+ }
+
+ if (peer->max_fails
+ && peer->fails >= peer->max_fails
+ && now - peer->checked <= peer->fail_timeout)
+ {
+ goto next;
+ }
+
+ if (peer->max_conns && peer->conns >= peer->max_conns) {
+ goto next;
+ }
+
+ if (prev) {
+ if (peer->conns * prev->weight > prev->conns * peer->weight) {
+ peer = prev;
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
+ }
+
+ break;
+ }
+
+ prev = peer;
+ p = i;
+
+ next:
+
+ if (++rp->tries > 20) {
+ ngx_stream_upstream_rr_peers_unlock(peers);
+ return ngx_stream_upstream_get_round_robin_peer(pc, rrp);
+ }
+ }
+
+ rrp->current = peer;
+
+ if (now - peer->checked > peer->fail_timeout) {
+ peer->checked = now;
+ }
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ peer->conns++;
+
+ ngx_stream_upstream_rr_peers_unlock(peers);
+
+ rrp->tried[n] |= m;
+
+ return NGX_OK;
+}
+
+
+static ngx_uint_t
+ngx_stream_upstream_peek_random_peer(ngx_stream_upstream_rr_peers_t *peers,
+ ngx_stream_upstream_random_peer_data_t *rp)
+{
+ ngx_uint_t i, j, k, x;
+
+ x = ngx_random() % peers->total_weight;
+
+ i = 0;
+ j = peers->number;
+
+ while (j - i > 1) {
+ k = (i + j) / 2;
+
+ if (x < rp->conf->ranges[k].range) {
+ j = k;
+
+ } else {
+ i = k;
+ }
+ }
+
+ return i;
+}
+
+
+static void *
+ngx_stream_upstream_random_create_conf(ngx_conf_t *cf)
+{
+ ngx_stream_upstream_random_srv_conf_t *conf;
+
+ conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_random_srv_conf_t));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->two = 0;
+ */
+
+ return conf;
+}
+
+
+static char *
+ngx_stream_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_stream_upstream_random_srv_conf_t *rcf = conf;
+
+ ngx_str_t *value;
+ ngx_stream_upstream_srv_conf_t *uscf;
+
+ uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);
+
+ if (uscf->peer.init_upstream) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "load balancing method redefined");
+ }
+
+ uscf->peer.init_upstream = ngx_stream_upstream_init_random;
+
+ uscf->flags = NGX_STREAM_UPSTREAM_CREATE
+ |NGX_STREAM_UPSTREAM_WEIGHT
+ |NGX_STREAM_UPSTREAM_MAX_CONNS
+ |NGX_STREAM_UPSTREAM_MAX_FAILS
+ |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT
+ |NGX_STREAM_UPSTREAM_DOWN;
+
+ if (cf->args->nelts == 1) {
+ return NGX_CONF_OK;
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "two") == 0) {
+ rcf->two = 1;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[1]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (cf->args->nelts == 2) {
+ return NGX_CONF_OK;
+ }
+
+ if (ngx_strcmp(value[2].data, "least_conn") != 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}