upstream choice modules
diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c
index f22de51..ab21477 100644
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -13,9 +13,6 @@
 typedef struct {
     ngx_http_upstream_conf_t       upstream;
 
-    ngx_http_upstream_srv_conf_t  *upstream_peers;
-    ngx_peers_t                   *peers0;
-
     ngx_str_t                      index;
 
     ngx_array_t                   *flushes;
@@ -121,6 +118,11 @@
 static char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,
     void *data);
 
+static char *ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf);
+static char *ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf);
+
 
 static ngx_http_fastcgi_request_start_t  ngx_http_fastcgi_request_start = {
     { 1,                                               /* version */
@@ -310,16 +312,16 @@
 
     { ngx_string("fastcgi_upstream_max_fails"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_num_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.max_fails),
+      ngx_http_fastcgi_upstream_max_fails_unsupported,
+      0,
+      0,
       NULL },
 
     { ngx_string("fastcgi_upstream_fail_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_sec_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.fail_timeout),
+      ngx_http_fastcgi_upstream_fail_timeout_unsupported,
+      0,
+      0,
       NULL },
 
     { ngx_string("fastcgi_param"),
@@ -411,8 +413,6 @@
 
     u->peer.log = r->connection->log;
     u->peer.log_error = NGX_ERROR_ERR;
-    u->peer.peers = flcf->upstream_peers->peers;
-    u->peer.tries = flcf->upstream_peers->peers->number;
 #if (NGX_THREADS)
     u->peer.lock = &r->connection->lock;
 #endif
@@ -1547,9 +1547,6 @@
     conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
     conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
 
-    conf->upstream.max_fails = NGX_CONF_UNSET_UINT;
-    conf->upstream.fail_timeout = NGX_CONF_UNSET;
-
     conf->upstream.pass_request_headers = NGX_CONF_UNSET;
     conf->upstream.pass_request_body = NGX_CONF_UNSET;
 
@@ -1573,7 +1570,6 @@
     uintptr_t                    *code;
     ngx_str_t                    *header;
     ngx_uint_t                    i, j;
-    ngx_peer_t                   *peer;
     ngx_array_t                   hide_headers;
     ngx_keyval_t                 *src;
     ngx_hash_key_t               *hk;
@@ -1707,25 +1703,6 @@
                                        |NGX_HTTP_UPSTREAM_FT_OFF;
     }
 
-    ngx_conf_merge_uint_value(conf->upstream.max_fails,
-                              prev->upstream.max_fails, 1);
-
-    ngx_conf_merge_sec_value(conf->upstream.fail_timeout,
-                              prev->upstream.fail_timeout, 10);
-
-    if (conf->upstream_peers) {
-        peer = conf->upstream_peers->peers->peer;
-        for (i = 0; i < conf->upstream_peers->peers->number; i++) {
-            ngx_conf_init_uint_value(peer[i].weight, 1);
-            peer[i].current_weight = peer[i].weight;
-            ngx_conf_init_uint_value(peer[i].max_fails,
-                              conf->upstream.max_fails);
-            ngx_conf_init_value(peer[i].fail_timeout,
-                              conf->upstream.fail_timeout);
-        }
-
-    }
-
     ngx_conf_merge_path_value(conf->upstream.temp_path,
                               prev->upstream.temp_path,
                               NGX_HTTP_FASTCGI_TEMP_PATH, 1, 2, 0,
@@ -1844,8 +1821,8 @@
 
 peers:
 
-    if (conf->upstream_peers == NULL) {
-        conf->upstream_peers = prev->upstream_peers;
+    if (conf->upstream.upstream == NULL) {
+        conf->upstream.upstream = prev->upstream.upstream;
         conf->upstream.schema = prev->upstream.schema;
     }
 
@@ -2033,10 +2010,10 @@
     ngx_memzero(&u, sizeof(ngx_url_t));
 
     u.url = value[1];
-    u.upstream = 1;
+    u.no_resolve = 1;
 
-    lcf->upstream_peers = ngx_http_upstream_add(cf, &u);
-    if (lcf->upstream_peers == NULL) {
+    lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+    if (lcf->upstream.upstream == NULL) {
         return NGX_CONF_ERROR;
     }
 
@@ -2084,3 +2061,29 @@
 
     return NGX_CONF_OK;
 }
+
+
+static char *
+ngx_http_fastcgi_upstream_max_fails_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf)
+{
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+         "\"fastcgi_upstream_max_fails\" is not supported, "
+         "use the \"max_fails\" parameter of the \"server\" directive ",
+         "inside the \"upstream\" block");
+
+    return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_fastcgi_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf)
+{
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+         "\"fastcgi_upstream_fail_timeout\" is not supported, "
+         "use the \"fail_timeout\" parameter of the \"server\" directive ",
+         "inside the \"upstream\" block");
+
+    return NGX_CONF_ERROR;
+}
diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c
index 63bda64..bd8b837 100644
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -12,7 +12,6 @@
 
 typedef struct {
     ngx_http_upstream_conf_t   upstream;
-    ngx_peers_t               *peers;
 } ngx_http_memcached_loc_conf_t;
 
 
@@ -39,6 +38,11 @@
 static char *ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 
+static char *ngx_http_memcached_upstream_max_fails_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf);
+static char *ngx_http_memcached_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf);
+
 
 static ngx_conf_bitmask_t  ngx_http_memcached_next_upstream_masks[] = {
     { ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
@@ -96,16 +100,16 @@
 
     { ngx_string("memcached_upstream_max_fails"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_num_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_memcached_loc_conf_t, upstream.max_fails),
+      ngx_http_memcached_upstream_max_fails_unsupported,
+      0,
+      0,
       NULL },
 
     { ngx_string("memcached_upstream_fail_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_sec_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_memcached_loc_conf_t, upstream.fail_timeout),
+      ngx_http_memcached_upstream_fail_timeout_unsupported,
+      0,
+      0,
       NULL },
 
       ngx_null_command
@@ -178,8 +182,6 @@
 
     u->peer.log = r->connection->log;
     u->peer.log_error = NGX_ERROR_ERR;
-    u->peer.peers = mlcf->peers;
-    u->peer.tries = mlcf->peers->number;
 #if (NGX_THREADS)
     u->peer.lock = &r->connection->lock;
 #endif
@@ -511,13 +513,8 @@
 
     conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
 
-    conf->upstream.max_fails = NGX_CONF_UNSET_UINT;
-    conf->upstream.fail_timeout = NGX_CONF_UNSET;
-
-    /* "fastcgi_cyclic_temp_file" is disabled */
-    conf->upstream.cyclic_temp_file = 0;
-
     /* the hardcoded values */
+    conf->upstream.cyclic_temp_file = 0;
     conf->upstream.buffering = 0;
     conf->upstream.ignore_client_abort = 0;
     conf->upstream.send_lowat = 0;
@@ -540,8 +537,6 @@
     ngx_http_memcached_loc_conf_t *prev = parent;
     ngx_http_memcached_loc_conf_t *conf = child;
 
-    ngx_uint_t  i;
-
     ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
                               prev->upstream.connect_timeout, 60000);
 
@@ -566,20 +561,6 @@
                                        |NGX_HTTP_UPSTREAM_FT_OFF;
     }
 
-    ngx_conf_merge_uint_value(conf->upstream.max_fails,
-                              prev->upstream.max_fails, 1);
-
-    ngx_conf_merge_sec_value(conf->upstream.fail_timeout,
-                              prev->upstream.fail_timeout, 10);
-
-    if (conf->peers && conf->peers->number > 1) {
-        for (i = 0; i < conf->peers->number; i++) {
-            conf->peers->peer[i].weight = 1;
-            conf->peers->peer[i].max_fails = conf->upstream.max_fails;
-            conf->peers->peer[i].fail_timeout = conf->upstream.fail_timeout;
-        }
-    }
-
     return NGX_CONF_OK;
 }
 
@@ -602,16 +583,14 @@
     ngx_memzero(&u, sizeof(ngx_url_t));
 
     u.url = value[1];
-    u.uri_part = 1;
+    u.no_resolve = 1;
+    /* u.uri_part = 1;  may be used as namespace */
 
-    if (ngx_parse_url(cf, &u) != NGX_OK) {
-        if (u.err) {
-            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                               "%s in \"%V\"", u.err, &u.url);
-        }
+    lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+    if (lcf->upstream.upstream == NULL) {
+        return NGX_CONF_ERROR;
     }
 
-    lcf->peers = u.peers;
     lcf->upstream.schema.len = sizeof("memcached://") - 1;
     lcf->upstream.schema.data = (u_char *) "memcached://";
 
@@ -627,3 +606,29 @@
 
     return NGX_CONF_OK;
 }
+
+
+static char *
+ngx_http_memcached_upstream_max_fails_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf)
+{
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+         "\"memcached_upstream_max_fails\" is not supported, "
+         "use the \"max_fails\" parameter of the \"server\" directive ",
+         "inside the \"upstream\" block");
+
+    return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_memcached_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf)
+{
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+         "\"memcached_upstream_fail_timeout\" is not supported, "
+         "use the \"fail_timeout\" parameter of the \"server\" directive ",
+         "inside the \"upstream\" block");
+
+    return NGX_CONF_ERROR;
+}
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index f17f0a7..2095f6a 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -35,8 +35,6 @@
 typedef struct {
     ngx_http_upstream_conf_t       upstream;
 
-    ngx_http_upstream_srv_conf_t  *upstream_peers;
-
     ngx_array_t                   *flushes;
     ngx_array_t                   *body_set_len;
     ngx_array_t                   *body_set;
@@ -107,6 +105,11 @@
 
 static char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);
 
+static char *ngx_http_proxy_upstream_max_fails_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf);
+static char *ngx_http_proxy_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf);
+
 
 static ngx_conf_post_t  ngx_http_proxy_lowat_post =
     { ngx_http_proxy_lowat_check };
@@ -297,16 +300,16 @@
 
     { ngx_string("proxy_upstream_max_fails"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_num_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_proxy_loc_conf_t, upstream.max_fails),
+      ngx_http_proxy_upstream_max_fails_unsupported,
+      0,
+      0,
       NULL },
 
     { ngx_string("proxy_upstream_fail_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
-      ngx_conf_set_sec_slot,
-      NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_proxy_loc_conf_t, upstream.fail_timeout),
+      ngx_http_proxy_upstream_fail_timeout_unsupported,
+      0,
+      0,
       NULL },
 
     { ngx_string("proxy_pass_header"),
@@ -419,8 +422,6 @@
 
     u->peer.log = r->connection->log;
     u->peer.log_error = NGX_ERROR_ERR;
-    u->peer.peers = plcf->upstream_peers->peers;
-    u->peer.tries = plcf->upstream_peers->peers->number;
 #if (NGX_THREADS)
     u->peer.lock = &r->connection->lock;
 #endif
@@ -1498,9 +1499,6 @@
     conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
     conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;
 
-    conf->upstream.max_fails = NGX_CONF_UNSET_UINT;
-    conf->upstream.fail_timeout = NGX_CONF_UNSET;
-
     conf->upstream.pass_request_headers = NGX_CONF_UNSET;
     conf->upstream.pass_request_body = NGX_CONF_UNSET;
 
@@ -1527,7 +1525,6 @@
     uintptr_t                    *code;
     ngx_str_t                    *header;
     ngx_uint_t                    i, j;
-    ngx_peer_t                   *peer;
     ngx_array_t                   hide_headers;
     ngx_keyval_t                 *src, *s, *h;
     ngx_hash_key_t               *hk;
@@ -1660,24 +1657,6 @@
                                        |NGX_HTTP_UPSTREAM_FT_OFF;
     }
 
-    ngx_conf_merge_uint_value(conf->upstream.max_fails,
-                              prev->upstream.max_fails, 1);
-
-    ngx_conf_merge_sec_value(conf->upstream.fail_timeout,
-                              prev->upstream.fail_timeout, 10);
-
-    if (conf->upstream_peers) {
-        peer = conf->upstream_peers->peers->peer;
-        for (i = 0; i < conf->upstream_peers->peers->number; i++) {
-            ngx_conf_init_uint_value(peer[i].weight, 1);
-            peer[i].current_weight = peer[i].weight;
-            ngx_conf_init_uint_value(peer[i].max_fails,
-                              conf->upstream.max_fails);
-            ngx_conf_init_value(peer[i].fail_timeout,
-                              conf->upstream.fail_timeout);
-        }
-    }
-
     ngx_conf_merge_path_value(conf->upstream.temp_path,
                               prev->upstream.temp_path,
                               NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0,
@@ -1834,8 +1813,8 @@
 
 peers:
 
-    if (conf->upstream_peers == NULL) {
-        conf->upstream_peers = prev->upstream_peers;
+    if (conf->upstream.upstream == NULL) {
+        conf->upstream.upstream = prev->upstream.upstream;
 
         conf->host_header = prev->host_header;
         conf->port_text = prev->port_text;
@@ -2180,11 +2159,11 @@
     u.url.len = url->len - add;
     u.url.data = url->data + add;
     u.default_portn = port;
+    u.no_resolve = 1;
     u.uri_part = 1;
-    u.upstream = 1;
 
-    plcf->upstream_peers = ngx_http_upstream_add(cf, &u);
-    if (plcf->upstream_peers == NULL) {
+    plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+    if (plcf->upstream.upstream == NULL) {
         return NGX_CONF_ERROR;
     }
 
@@ -2345,3 +2324,29 @@
 
     return NGX_CONF_OK;
 }
+
+
+static char *
+ngx_http_proxy_upstream_max_fails_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf)
+{
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+         "\"proxy_upstream_max_fails\" is not supported, "
+         "use the \"max_fails\" parameter of the \"server\" directive ",
+         "inside the \"upstream\" block");
+
+    return NGX_CONF_ERROR;
+}
+
+
+static char *
+ngx_http_proxy_upstream_fail_timeout_unsupported(ngx_conf_t *cf,
+    ngx_command_t *cmd, void *conf)
+{
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+         "\"proxy_upstream_fail_timeout\" is not supported, "
+         "use the \"fail_timeout\" parameter of the \"server\" directive ",
+         "inside the \"upstream\" block");
+
+    return NGX_CONF_ERROR;
+}
diff --git a/src/http/modules/ngx_http_upstream_ip_hash_module.c b/src/http/modules/ngx_http_upstream_ip_hash_module.c
new file mode 100644
index 0000000..1de019e
--- /dev/null
+++ b/src/http/modules/ngx_http_upstream_ip_hash_module.c
@@ -0,0 +1,228 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    /* the round robin data must be first */
+    ngx_http_upstream_rr_peer_data_t   rrp;
+
+    ngx_uint_t                         hash;
+
+    /* AF_INET only */
+    u_char                             addr[3];
+
+    u_char                             tries;
+
+    ngx_event_get_peer_pt              get_rr_peer;
+} ngx_http_upstream_ip_hash_peer_data_t;
+
+
+static ngx_int_t ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
+    ngx_http_upstream_srv_conf_t *us);
+static ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc,
+    void *data);
+static char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
+
+static ngx_command_t  ngx_http_upstream_ip_hash_commands[] = {
+
+    { ngx_string("ip_hash"),
+      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,
+      ngx_http_upstream_ip_hash,
+      0,
+      0,
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_upstream_ip_hash_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    NULL,                                  /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    NULL,                                  /* create location configuration */
+    NULL                                   /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_upstream_ip_hash_module = {
+    NGX_MODULE_V1,
+    &ngx_http_upstream_ip_hash_module_ctx, /* module context */
+    ngx_http_upstream_ip_hash_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
+};
+
+
+ngx_int_t
+ngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)
+{
+    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    us->peer.init = ngx_http_upstream_init_ip_hash_peer;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,
+    ngx_http_upstream_srv_conf_t *us)
+{
+    struct sockaddr_in                     *sin;
+    ngx_http_upstream_ip_hash_peer_data_t  *iphp;
+
+    iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t));
+    if (iphp == NULL) {
+        return NGX_ERROR;
+    }
+
+    r->upstream->peer.data = &iphp->rrp;
+
+    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;
+
+    /* AF_INET only */
+    sin = (struct sockaddr_in *) r->connection->sockaddr;
+    iphp->addr[0] = (u_char) ((sin->sin_addr.s_addr >> 24) & 0xff);
+    iphp->addr[1] = (u_char) ((sin->sin_addr.s_addr >> 16) & 0xff);
+    iphp->addr[2] = (u_char) ((sin->sin_addr.s_addr >> 8) & 0xff);
+
+    iphp->hash = 89;
+    iphp->tries = 0;
+    iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
+{
+    ngx_http_upstream_ip_hash_peer_data_t  *iphp = data;
+
+    time_t                        now;
+    uintptr_t                     m;
+    ngx_uint_t                    i, n, p, hash;
+    ngx_http_upstream_rr_peer_t  *peer;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+                   "get ip hash peer, try: %ui", pc->tries);
+
+    /* TODO: cached */
+
+    if (iphp->tries > 20 || iphp->rrp.peers->number == 1) {
+        return iphp->get_rr_peer(pc, &iphp->rrp);
+    }
+
+    now = ngx_time();
+
+    pc->cached = 0;
+    pc->connection = NULL;
+
+    hash = iphp->hash;
+
+    for ( ;; ) {
+
+        for (i = 0; i < 3; i++) {
+            hash = (hash * 113 + iphp->addr[i]) % 6271;
+        }
+
+        p = hash % iphp->rrp.peers->number;
+
+        n = p / (8 * sizeof(uintptr_t));
+        m = 1 << p % (8 * sizeof(uintptr_t));
+
+        if (!(iphp->rrp.tried[n] & m)) {
+
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+                           "get ip hash peer, hash: %ui %04XA", p, m);
+
+            peer = &iphp->rrp.peers->peer[p];
+
+            /* ngx_lock_mutex(iphp->rrp.peers->mutex); */
+
+            if (!peer->down) {
+
+		if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
+		    break;
+		}
+
+		if (now - peer->accessed > peer->fail_timeout) {
+		    peer->fails = 0;
+		    break;
+		}
+
+            } else {
+                iphp->rrp.tried[n] |= m;
+            }
+
+            /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
+
+            pc->tries--;
+        }
+
+        if (++iphp->tries >= 20) {
+            return iphp->get_rr_peer(pc, &iphp->rrp);
+        }
+    }
+
+    pc->sockaddr = peer->sockaddr;
+    pc->socklen = peer->socklen;
+    pc->name = &peer->name;
+#if (NGX_SSL)
+    pc->ssl_session = peer->ssl_session;
+#endif
+
+    /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
+
+    iphp->rrp.tried[n] |= m;
+    iphp->hash = hash;
+
+    return NGX_OK;
+}
+
+
+static char *
+ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_upstream_srv_conf_t  *uscf;
+
+    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);
+
+    uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;
+
+    uscf->flags = NGX_HTTP_UPSTREAM_CREATE
+                  |NGX_HTTP_UPSTREAM_MAX_FAILS
+                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
+                  |NGX_HTTP_UPSTREAM_DOWN;
+
+    return NGX_CONF_OK;
+}