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; +}