changeset 260:0effe91f6083 NGINX_0_5_0

nginx 0.5.0 *) Change: the parameters in the "%name" form in the "log_format" directive are not supported anymore. *) Change: the "proxy_upstream_max_fails", "proxy_upstream_fail_timeout", "fastcgi_upstream_max_fails", "fastcgi_upstream_fail_timeout", "memcached_upstream_max_fails", and "memcached_upstream_fail_timeout" directives are not supported anymore. *) Feature: the "server" directive in the "upstream" context supports the "max_fails", "fail_timeout", and "down" parameters. *) Feature: the "ip_hash" directive inside the "upstream" block. *) Feature: the WAIT status in the "Auth-Status" header line of the IMAP/POP3 proxy authentication server response. *) Bugfix: nginx could not be built on 64-bit platforms; bug appeared in 0.4.14.
author Igor Sysoev <http://sysoev.ru>
date Mon, 04 Dec 2006 00:00:00 +0300
parents c68f18041059
children e6ae240d5952
files CHANGES CHANGES.ru auto/modules auto/options auto/sources src/core/nginx.h src/core/ngx_core.h src/core/ngx_inet.c src/core/ngx_inet.h src/core/ngx_slab.c src/event/ngx_event_connect.c src/event/ngx_event_connect.h src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_gzip_filter_module.c src/http/modules/ngx_http_log_module.c src/http/modules/ngx_http_log_module.h src/http/modules/ngx_http_memcached_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_upstream_ip_hash_module.c src/http/modules/ngx_http_userid_filter_module.c src/http/modules/perl/nginx.pm src/http/modules/perl/nginx.xs src/http/ngx_http.h src/http/ngx_http_request.c src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h src/http/ngx_http_upstream_round_robin.c src/http/ngx_http_upstream_round_robin.h src/imap/ngx_imap.h src/imap/ngx_imap_auth_http_module.c src/imap/ngx_imap_handler.c src/imap/ngx_imap_proxy_module.c
diffstat 32 files changed, 1516 insertions(+), 1654 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,21 +1,47 @@
 
+Changes with nginx 0.5.0                                         04 Dec 2006
+
+    *) Change: the parameters in the "%name" form in the "log_format" 
+       directive are not supported anymore.
+
+    *) Change: the "proxy_upstream_max_fails", 
+       "proxy_upstream_fail_timeout", "fastcgi_upstream_max_fails", 
+       "fastcgi_upstream_fail_timeout", "memcached_upstream_max_fails", and 
+       "memcached_upstream_fail_timeout" directives are not supported 
+       anymore.
+
+    *) Feature: the "server" directive in the "upstream" context supports 
+       the "max_fails", "fail_timeout", and "down" parameters.
+
+    *) Feature: the "ip_hash" directive inside the "upstream" block.
+
+    *) Feature: the WAIT status in the "Auth-Status" header line of the 
+       IMAP/POP3 proxy authentication server response.
+
+    *) Bugfix: nginx could not be built on 64-bit platforms; bug appeared 
+       in 0.4.14.
+
+
 Changes with nginx 0.4.14                                        27 Nov 2006
 
     *) Feature: the "proxy_pass_error_message" directive in IMAP/POP3 proxy.
 
-    *) Bugfix: ngx_http_perl_module did nto work with perl built with the 
+    *) Feature: now configure detects system PCRE library on FreeBSD, 
+       Linux, and NetBSD.
+
+    *) Bugfix: ngx_http_perl_module did not work with perl built with the 
        threads support; bug appeared in 0.3.38.
 
-    *) Bugfix: !!!!!!!!!! segfault perl.
-
-    *) Bugfix: !!!!!!!!!! recursive perl.
+    *) Bugfix: ngx_http_perl_module did not work if perl was called 
+       recursively.
 
     *) Bugfix: nginx ignored a host name in an request line.
 
-    *) Bugfix: !!!!!!!!!! endless loop when too many FastCGI sent too many 
-       to stderr
-
-    *) Bugfix: !!!!!!!! negative upstream response time
+    *) Bugfix: a worker process may got caught in an endless loop, if a 
+       FastCGI server sent too many data to the stderr.
+
+    *) Bugfix: the $upstream_response_time variable may be negative if the 
+       system time was changed backward.
 
     *) Bugfix: the "Auth-Login-Attempt" parameter was not sent to IMAP/POP3 
        proxy authentication server when POP3 was used.
@@ -353,7 +379,7 @@ Changes with nginx 0.3.53               
     *) Change: the "add_header" directive adds the string to 204, 301, and 
        302 responses.
 
-    *) Feature: the "server" directive if the "upstream" context supports 
+    *) Feature: the "server" directive in the "upstream" context supports 
        the "weight" parameter.
 
     *) Feature: the "server_name" directive supports the "*" wildcard.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,23 +1,49 @@
 
+Изменения в nginx 0.5.0                                           04.12.2006
+
+    *) Изменение: параметры в виде "%name" в директиве log_format больше не 
+       поддерживаются.
+
+    *) Изменение: директивы proxy_upstream_max_fails, 
+       proxy_upstream_fail_timeout, fastcgi_upstream_max_fails, и 
+       fastcgi_upstream_fail_timeout, memcached_upstream_max_fails и 
+       memcached_upstream_fail_timeout больше не поддерживаются.
+
+    *) Добавление: директива server в блоке upstream поддерживает параметры 
+       max_fails, fail_timeout и down.
+
+    *) Добавление: директива ip_hash в блоке upstream.
+
+    *) Добавление: статус WAIT в строке "Auth-Status" в заголовка ответа 
+       сервера аутентификации IMAP/POP3 прокси.
+
+    *) Исправление: nginx не собирался на 64-битных платформах; ошибка 
+       появилась в 0.4.14.
+
+
 Изменения в nginx 0.4.14                                          27.11.2006
 
     *) Добавление: директива proxy_pass_error_message в IMAP/POP3 прокси.
 
+    *) Добавление: теперь configure определяет библиотеку PCRE на FreeBSD, 
+       Linux и NetBSD.
+
     *) Исправление: ngx_http_perl_module не работал с перлом, собранным с 
        поддержкой потоков; ошибка появилась в 0.3.38.
 
-    *) Исправление: !!!!!!!!!!
-
-    *) Исправление: !!!!!!!!!!
+    *) Исправление: ngx_http_perl_module не работал корректно, если перл 
+       вызывался рекурсивно.
 
     *) Исправление: nginx игнорировал имя сервера в строке запроса.
 
-    *) Исправление: !!!!!!!!!!
-
-    *) Исправление: !!!!!!!!
+    *) Исправление: если FastCGI сервер передавал много в stderr, то 
+       рабочий процесс мог зациклиться.
+
+    *) Исправление: при изменении системного времени переменная 
+       $upstream_response_time могла быть отрицательной.
 
     *) Исправление: при использовании POP3 серверу аутентификации IMAP/POP3 
-       прокси не передавлся параметр Auth-Login-Attempt.
+       прокси не передавался параметр Auth-Login-Attempt.
 
     *) Исправление: при ошибке соединения с сервером аутентификации 
        IMAP/POP3 прокси мог произойти segmentation fault.
--- a/auto/modules
+++ b/auto/modules
@@ -255,6 +255,11 @@ if [ $HTTP_FLV = YES ]; then
     HTTP_SRCS="$HTTP_SRCS $HTTP_FLV_SRCS"
 fi
 
+if [ $HTTP_UPSTREAM_IP_HASH = YES ]; then
+    HTTP_MODULES="$HTTP_MODULES $HTTP_UPSTREAM_IP_HASH_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_UPSTREAM_IP_HASH_SRCS"
+fi
+
 # STUB
 #USE_MD5=YES
 #HTTP_SRCS="$HTTP_SRCS $HTPP_CACHE_SRCS"
--- a/auto/options
+++ b/auto/options
@@ -71,6 +71,7 @@ HTTP_MEMCACHED=YES
 HTTP_EMPTY_GIF=YES
 HTTP_BROWSER=YES
 HTTP_FLV=NO
+HTTP_UPSTREAM_IP_HASH=YES
 
 # STUB
 HTTP_STUB_STATUS=NO
@@ -170,6 +171,7 @@ do
         --without-http_memcached_module) HTTP_MEMCACHED=NO          ;;
         --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO          ;;
         --without-http_browser_module)   HTTP_BROWSER=NO            ;;
+        --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;;
 
         --with-http_perl_module)         HTTP_PERL=YES              ;;
         --with-perl_modules_path=*)      NGX_PERL_MODULES="$value"  ;;
@@ -271,6 +273,8 @@ cat << END
   --without-http_memcached_module    disable ngx_http_memcached_module
   --without-http_empty_gif_module    disable ngx_http_empty_gif_module
   --without-http_browser_module      disable ngx_http_browser_module
+  --without-http_upstream_ip_hash_module
+                                     disable ngx_http_upstream_ip_hash_module
 
   --with-http_perl_module            enable ngx_http_perl_module
   --with-perl_modules_path=PATH      set path to the perl modules
--- a/auto/sources
+++ b/auto/sources
@@ -270,8 +270,8 @@ HTTP_DEPS="src/http/ngx_http.h \
            src/http/ngx_http_variables.h \
            src/http/ngx_http_script.h \
            src/http/ngx_http_upstream.h \
-           src/http/ngx_http_busy_lock.h \
-           src/http/modules/ngx_http_log_module.h"
+           src/http/ngx_http_upstream_round_robin.h \
+           src/http/ngx_http_busy_lock.h"
 
 HTTP_SRCS="src/http/ngx_http.c \
            src/http/ngx_http_core_module.c \
@@ -286,6 +286,7 @@ HTTP_SRCS="src/http/ngx_http.c \
            src/http/ngx_http_variables.c \
            src/http/ngx_http_script.c \
            src/http/ngx_http_upstream.c \
+           src/http/ngx_http_upstream_round_robin.c \
            src/http/ngx_http_parse_time.c \
            src/http/modules/ngx_http_static_module.c \
            src/http/modules/ngx_http_index_module.c \
@@ -399,6 +400,10 @@ HTTP_FLV_MODULE=ngx_http_flv_module
 HTTP_FLV_SRCS=src/http/modules/ngx_http_flv_module.c
 
 
+HTTP_UPSTREAM_IP_HASH_MODULE=ngx_http_upstream_ip_hash_module
+HTTP_UPSTREAM_IP_HASH_SRCS=src/http/modules/ngx_http_upstream_ip_hash_module.c
+
+
 IMAP_INCS="src/imap"
 
 IMAP_DEPS="src/imap/ngx_imap.h"
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.4.14"
+#define NGINX_VERSION      "0.5.0"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -19,7 +19,6 @@ typedef struct ngx_open_file_s   ngx_ope
 typedef struct ngx_command_s     ngx_command_t;
 typedef struct ngx_file_s        ngx_file_t;
 typedef struct ngx_event_s       ngx_event_t;
-typedef struct ngx_peers_s       ngx_peers_t;
 typedef struct ngx_connection_s  ngx_connection_t;
 
 typedef void (*ngx_event_handler_pt)(ngx_event_t *ev);
--- a/src/core/ngx_inet.c
+++ b/src/core/ngx_inet.c
@@ -6,8 +6,6 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
-#include <ngx_event.h>
-#include <ngx_event_connect.h>
 
 
 /*
@@ -239,8 +237,6 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t 
 
 #if (NGX_HAVE_UNIX_DOMAIN)
 
-        u->type = NGX_PARSE_URL_UNIX;
-
         p += 5;
         len -= 5;
 
@@ -271,8 +267,8 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t 
             return NGX_ERROR;
         }
 
-        u->peers = ngx_pcalloc(cf->pool, sizeof(ngx_peers_t));
-        if (u->peers == NULL) {
+        u->addrs = ngx_pcalloc(cf->pool, sizeof(ngx_peer_addr_t));
+        if (u->addrs == NULL) {
             return NGX_ERROR;
         }
 
@@ -281,16 +277,15 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t 
             return NGX_ERROR;
         }
 
-        u->peers->number = 1;
+        u->naddrs = 1;
 
         saun->sun_family = AF_UNIX;
         (void) ngx_cpystrn((u_char *) saun->sun_path, p, len + 1);
 
-        u->peers->peer[0].sockaddr = (struct sockaddr *) saun;
-        u->peers->peer[0].socklen = sizeof(struct sockaddr_un);
-        u->peers->peer[0].name.len = len + 5;
-        u->peers->peer[0].name.data = u->url.data;
-        u->peers->peer[0].uri_separator = ":";
+        u->addrs[0].sockaddr = (struct sockaddr *) saun;
+        u->addrs[0].socklen = sizeof(struct sockaddr_un);
+        u->addrs[0].name.len = len + 5;
+        u->addrs[0].name.data = u->url.data;
 
         u->host_header.len = sizeof("localhost") - 1;
         u->host_header.data = (u_char *) "localhost";
@@ -310,8 +305,6 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t 
         return NGX_ERROR;
     }
 
-    u->type = NGX_PARSE_URL_INET;
-
     u->host.data = p;
     u->host_header.len = len;
     u->host_header.data = p;
@@ -323,7 +316,7 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t 
             u->host.len = i;
 
             if (!u->uri_part) {
-                u->port.len = &p[len] - u->port.data;
+                u->port.len = len - (i + 1);
                 break;
             }
         }
@@ -338,8 +331,8 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t 
             }
 
             if (u->port.data == NULL) {
-                u->default_port = 1;
-                goto port;
+                u->no_port = 1;
+                goto no_port;
             }
 
             u->port.len = &p[i] - u->port.data;
@@ -375,10 +368,10 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t 
         port = ngx_atoi(p, len);
 
         if (port == NGX_ERROR) {
-            u->default_port = 1;
             u->host.len = len;
+            u->no_port = 1;
 
-            goto port;
+            goto no_port;
         }
 
         u->port.len = len;
@@ -388,9 +381,10 @@ ngx_parse_url(ngx_conf_t *cf, ngx_url_t 
 
     u->portn = (in_port_t) port;
 
-port:
+no_port:
 
     if (u->listen) {
+
         if (u->portn == 0) {
             if (u->default_portn == 0) {
                 u->err = "no port";
@@ -435,13 +429,9 @@ port:
         return NGX_OK;
     }
 
-    if (u->default_port) {
+    if (u->no_port) {
 
-        if (u->upstream) {
-            return NGX_OK;
-        }
-
-        if (u->default_portn == 0) {
+        if (u->default_portn == 0 && !u->upstream) {
             u->err = "no port";
             return NGX_ERROR;
         }
@@ -455,12 +445,8 @@ port:
 
         u->port.len = ngx_sprintf(u->port.data, "%d", u->portn) - u->port.data;
 
-    } else if (u->portn) {
-        if (u->portn == u->default_portn) {
-            u->default_port = 1;
-        }
+    } else if (u->portn == 0) {
 
-    } else {
         u->err = "no port";
         return NGX_ERROR;
     }
@@ -470,14 +456,11 @@ port:
         return NGX_ERROR;
     }
 
-    u->peers = ngx_inet_resolve_peer(cf, &u->host, u->portn);
-
-    if (u->peers == NULL) {
-        return NGX_ERROR;
+    if (u->no_resolve) {
+        return NGX_OK;
     }
 
-    if (u->peers == NGX_CONF_ERROR) {
-        u->err = "host not found";
+    if (ngx_inet_resolve_host(cf, u) != NGX_OK) {
         return NGX_ERROR;
     }
 
@@ -485,23 +468,22 @@ port:
 }
 
 
-ngx_peers_t *
-ngx_inet_resolve_peer(ngx_conf_t *cf, ngx_str_t *name, in_port_t port)
+ngx_int_t
+ngx_inet_resolve_host(ngx_conf_t *cf, ngx_url_t *u)
 {
     u_char              *host;
     size_t               len;
     in_addr_t            in_addr;
     ngx_uint_t           i;
-    ngx_peers_t         *peers;
     struct hostent      *h;
     struct sockaddr_in  *sin;
 
-    host = ngx_palloc(cf->temp_pool, name->len + 1);
+    host = ngx_palloc(cf->temp_pool, u->host.len + 1);
     if (host == NULL) {
-        return NULL;
+        return NGX_ERROR;
     }
 
-    (void) ngx_cpystrn(host, name->data, name->len + 1);
+    (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);
 
     /* AF_INET only */
 
@@ -511,92 +493,88 @@ ngx_inet_resolve_peer(ngx_conf_t *cf, ng
         h = gethostbyname((char *) host);
 
         if (h == NULL || h->h_addr_list[0] == NULL) {
-            return NGX_CONF_ERROR;
+            u->err = "host not found";
+            return NGX_ERROR;
         }
 
-        for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ }
+        if (u->one_addr == 0) {
+            for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ }
+
+        } else {
+            i = 1;
+        }
 
         /* MP: ngx_shared_palloc() */
 
-        peers = ngx_pcalloc(cf->pool,
-                            sizeof(ngx_peers_t) + sizeof(ngx_peer_t) * (i - 1));
-        if (peers == NULL) {
-            return NULL;
+        u->addrs = ngx_pcalloc(cf->pool, i * sizeof(ngx_peer_addr_t));
+        if (u->addrs == NULL) {
+            return NGX_ERROR;
         }
 
-        peers->number = i;
+        u->naddrs = i;
 
         for (i = 0; h->h_addr_list[i] != NULL; i++) {
 
             sin = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in));
             if (sin == NULL) {
-                return NULL;
+                return NGX_ERROR;
             }
 
             sin->sin_family = AF_INET;
-            sin->sin_port = htons(port);
+            sin->sin_port = htons(u->portn);
             sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]);
 
-            peers->peer[i].sockaddr = (struct sockaddr *) sin;
-            peers->peer[i].socklen = sizeof(struct sockaddr_in);
+            u->addrs[i].sockaddr = (struct sockaddr *) sin;
+            u->addrs[i].socklen = sizeof(struct sockaddr_in);
 
             len = INET_ADDRSTRLEN - 1 + 1 + sizeof(":65536") - 1;
 
-            peers->peer[i].name.data = ngx_palloc(cf->pool, len);
-            if (peers->peer[i].name.data == NULL) {
-                return NULL;
+            u->addrs[i].name.data = ngx_palloc(cf->pool, len);
+            if (u->addrs[i].name.data == NULL) {
+                return NGX_ERROR;
             }
 
             len = ngx_sock_ntop(AF_INET, (struct sockaddr *) sin,
-                                peers->peer[i].name.data, len);
+                                u->addrs[i].name.data, len);
 
-            peers->peer[i].name.len =
-                                    ngx_sprintf(&peers->peer[i].name.data[len],
-                                                ":%d", port)
-                                      - peers->peer[i].name.data;
-
-            peers->peer[i].uri_separator = "";
-
-            peers->peer[i].weight = NGX_CONF_UNSET_UINT;
-            peers->peer[i].max_fails = NGX_CONF_UNSET_UINT;
-            peers->peer[i].fail_timeout = NGX_CONF_UNSET;
+            u->addrs[i].name.len = ngx_sprintf(&u->addrs[i].name.data[len],
+                                               ":%d", u->portn)
+                                      - u->addrs[i].name.data;
         }
 
     } else {
 
         /* MP: ngx_shared_palloc() */
 
-        peers = ngx_pcalloc(cf->pool, sizeof(ngx_peers_t));
-        if (peers == NULL) {
-            return NULL;
+        u->addrs = ngx_pcalloc(cf->pool, sizeof(ngx_peer_addr_t));
+        if (u->addrs == NULL) {
+            return NGX_ERROR;
         }
 
         sin = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in));
         if (sin == NULL) {
-            return NULL;
+            return NGX_ERROR;
         }
 
-        peers->number = 1;
+        u->naddrs = 1;
 
         sin->sin_family = AF_INET;
-        sin->sin_port = htons(port);
+        sin->sin_port = htons(u->portn);
         sin->sin_addr.s_addr = in_addr;
 
-        peers->peer[0].sockaddr = (struct sockaddr *) sin;
-        peers->peer[0].socklen = sizeof(struct sockaddr_in);
+        u->addrs[0].sockaddr = (struct sockaddr *) sin;
+        u->addrs[0].socklen = sizeof(struct sockaddr_in);
 
-        peers->peer[0].name.data = ngx_palloc(cf->pool,
-                                              name->len + sizeof(":65536") - 1);
-        if (peers->peer[0].name.data == NULL) {
-            return NULL;
+        u->addrs[0].name.data = ngx_palloc(cf->pool,
+                                           u->host.len + sizeof(":65536") - 1);
+        if (u->addrs[0].name.data == NULL) {
+            return NGX_ERROR;
         }
 
-        peers->peer[0].name.len = ngx_sprintf(peers->peer[0].name.data, "%V:%d",
-                                              name, port)
-                                  - peers->peer[0].name.data;
-
-        peers->peer[0].uri_separator = "";
+        u->addrs[0].name.len = ngx_sprintf(u->addrs[0].name.data, "%V:%d",
+                                           &u->host, u->portn)
+                                  - u->addrs[0].name.data;
     }
 
-    return peers;
+    return NGX_OK;
 }
--- a/src/core/ngx_inet.h
+++ b/src/core/ngx_inet.h
@@ -12,80 +12,51 @@
 #include <ngx_core.h>
 
 
-#define NGX_PARSE_URL_INET   1
-#define NGX_PARSE_URL_UNIX   2
-
-
 typedef struct {
-    in_addr_t           addr;
-    in_addr_t           mask;
+    in_addr_t         addr;
+    in_addr_t         mask;
 } ngx_inet_cidr_t;
 
 
-typedef struct {
-    struct sockaddr    *sockaddr;
-    socklen_t           socklen;
-
-    ngx_str_t           name;
-    char               *uri_separator;
-
-    ngx_uint_t          current_weight;
-    ngx_uint_t          weight;
-
-    ngx_uint_t          fails;
-    time_t              accessed;
-
-    ngx_uint_t          max_fails;
-    time_t              fail_timeout;
-
-#if (NGX_SSL)
-    ngx_ssl_session_t  *ssl_session;
-#endif
-} ngx_peer_t;
-
-
-struct ngx_peers_s {
-    ngx_uint_t          current;
-
-    ngx_uint_t          number;
-    ngx_uint_t          last_cached;
-
- /* ngx_mutex_t        *mutex; */
-    ngx_connection_t  **cached;
-
-    ngx_peer_t          peer[1];
-};
-
-
 typedef union {
-    in_addr_t           in_addr;
+    in_addr_t         in_addr;
 } ngx_url_addr_t;
 
 
 typedef struct {
-    ngx_int_t           type;
+    struct sockaddr  *sockaddr;
+    socklen_t         socklen;
+    ngx_str_t         name;
+} ngx_peer_addr_t;
 
-    ngx_peers_t        *peers;
 
-    ngx_str_t           url;
-    ngx_str_t           host;
-    ngx_str_t           host_header;
-    ngx_str_t           port;
-    ngx_str_t           uri;
+typedef struct {
+    ngx_int_t         type;
+
+    ngx_str_t         url;
+    ngx_str_t         host;
+    ngx_str_t         host_header;
+    ngx_str_t         port;
+    ngx_str_t         uri;
 
-    in_port_t           portn;
-    in_port_t           default_portn;
+    in_port_t         portn;
+    in_port_t         default_portn;
 
-    unsigned            listen:1;
-    unsigned            uri_part:1;
-    unsigned            upstream:1;
+    unsigned          listen:1;
+    unsigned          uri_part:1;
+    unsigned          upstream:1;
+    unsigned          no_resolve:1;
+    unsigned          one_addr:1;
 
-    unsigned            default_port:1;
-    unsigned            wildcard:1;
+    unsigned          wildcard:1;
+    unsigned          no_port:1;
+
+    ngx_url_addr_t    addr;
 
-    ngx_url_addr_t      addr;
+    ngx_peer_addr_t  *addrs;
+    ngx_uint_t        naddrs;
 
-    char               *err;
+    char             *err;
 } ngx_url_t;
 
 
@@ -93,8 +64,8 @@ size_t ngx_sock_ntop(int family, struct 
 size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len);
 ngx_int_t ngx_ptocidr(ngx_str_t *text, void *cidr);
 ngx_int_t ngx_parse_url(ngx_conf_t *cf, ngx_url_t *u);
-ngx_peers_t *ngx_inet_resolve_peer(ngx_conf_t *cf, ngx_str_t *name,
-    in_port_t port);
+ngx_int_t ngx_inet_resolve_host(ngx_conf_t *cf, ngx_url_t *u);
+
 
 
 #endif /* _NGX_INET_H_INCLUDED_ */
--- a/src/core/ngx_slab.c
+++ b/src/core/ngx_slab.c
@@ -149,8 +149,8 @@ void *
 ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size)
 {
     size_t            s;
-    uintptr_t         p, mask, *bitmap;
-    ngx_uint_t        i, n, m, slot, shift, map;
+    uintptr_t         p, n, m, mask, *bitmap;
+    ngx_uint_t        i, slot, shift, map;
     ngx_slab_page_t  *page, *prev, *slots;
 
     ngx_shmtx_lock(&pool->mutex);
@@ -287,7 +287,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si
             do {
                 if ((page->slab & NGX_SLAB_MAP_MASK) != mask) {
 
-                    for (m = 1 << NGX_SLAB_MAP_SHIFT, i = 0;
+                    for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;
                          m & mask;
                          m <<= 1, i++)
                     {
@@ -369,7 +369,7 @@ ngx_slab_alloc(ngx_slab_pool_t *pool, si
 
         } else { /* size < ngx_pagesize */
 
-            page->slab = (1 << NGX_SLAB_MAP_SHIFT) | shift;
+            page->slab = ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT) | shift;
             page->next = &slots[slot];
             page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;
 
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -14,145 +14,21 @@ ngx_int_t
 ngx_event_connect_peer(ngx_peer_connection_t *pc)
 {
     int                rc;
-    ngx_uint_t         level, i;
     u_int              event;
-    time_t             now;
     ngx_err_t          err;
-    ngx_peer_t        *peer;
+    ngx_uint_t         level;
     ngx_socket_t       s;
     ngx_event_t       *rev, *wev;
     ngx_connection_t  *c;
 
-    now = ngx_time();
-
-    /* ngx_lock_mutex(pc->peers->mutex); */
-
-    if (pc->peers->last_cached) {
-
-        /* cached connection */
-
-        c = pc->peers->cached[pc->peers->last_cached];
-        pc->peers->last_cached--;
-
-        /* ngx_unlock_mutex(pc->peers->mutex); */
-
-#if (NGX_THREADS)
-        c->read->lock = c->read->own_lock;
-        c->write->lock = c->write->own_lock;
-#endif
-
-        pc->connection = c;
-        pc->cached = 1;
-
-        return NGX_OK;
+    rc = pc->get(pc, pc->data);
+    if (rc != NGX_OK) {
+        return rc;
     }
 
-    pc->cached = 0;
-    pc->connection = NULL;
-
-    if (pc->peers->number == 1) {
-        peer = &pc->peers->peer[0];
-
-    } else {
-
-        /* there are several peers */
-
-        if (pc->tries == pc->peers->number) {
-
-            /* it's a first try - get a current peer */
-
-            for ( ;; ) {
-                pc->cur_peer = pc->peers->current;
-
-                peer = &pc->peers->peer[pc->cur_peer];
-
-                if (peer->max_fails == 0 || peer->fails <= peer->max_fails) {
-                    break;
-                }
-
-                if (now - peer->accessed > peer->fail_timeout) {
-                    peer->fails = 0;
-                    break;
-                }
-
-                pc->peers->current++;
-
-                if (pc->peers->current >= pc->peers->number) {
-                    pc->peers->current = 0;
-                }
-
-                pc->tries--;
-
-                if (pc->tries) {
-                    continue;
-                }
-
-                goto failed;
-            }
-
-            peer->current_weight--;
-
-            if (peer->current_weight == 0) {
-                peer->current_weight = peer->weight;
-
-                pc->peers->current++;
+    s = ngx_socket(pc->sockaddr->sa_family, SOCK_STREAM, 0);
 
-                if (pc->peers->current >= pc->peers->number) {
-                    pc->peers->current = 0;
-                }
-            }
-
-        } else {
-            for ( ;; ) {
-                peer = &pc->peers->peer[pc->cur_peer];
-
-                if (peer->max_fails == 0 || peer->fails <= peer->max_fails) {
-                    break;
-                }
-
-                if (now - peer->accessed > peer->fail_timeout) {
-                    peer->fails = 0;
-                    break;
-                }
-
-                pc->cur_peer++;
-
-                if (pc->cur_peer >= pc->peers->number) {
-                    pc->cur_peer = 0;
-                }
-
-                pc->tries--;
-
-                if (pc->tries) {
-                    continue;
-                }
-
-                goto failed;
-            }
-
-            peer->current_weight--;
-
-            if (peer->current_weight == 0) {
-                peer->current_weight = peer->weight;
-
-                if (pc->cur_peer == pc->peers->current) {
-                    pc->peers->current++;
-
-                    if (pc->peers->current >= pc->peers->number) {
-                        pc->peers->current = 0;
-                    }
-                }
-            }
-        }
-    }
-
-    /* ngx_unlock_mutex(pc->peers->mutex); */
-
-
-    s = ngx_socket(peer->sockaddr->sa_family, SOCK_STREAM, 0);
-
-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, 0,
-                   "socket %d", s);
+    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, 0, "socket %d", s);
 
     if (s == -1) {
         ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
@@ -211,7 +87,7 @@ ngx_event_connect_peer(ngx_peer_connecti
 
     c->log_error = pc->log_error;
 
-    if (peer->sockaddr->sa_family != AF_INET) {
+    if (pc->sockaddr->sa_family != AF_INET) {
         c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;
         c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;
 
@@ -254,9 +130,9 @@ ngx_event_connect_peer(ngx_peer_connecti
     }
 
     ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pc->log, 0,
-                   "connect to %V, fd:%d #%d", &peer->name, s, c->number);
+                   "connect to %V, fd:%d #%d", pc->name, s, c->number);
 
-    rc = connect(s, peer->sockaddr, peer->socklen);
+    rc = connect(s, pc->sockaddr, pc->socklen);
 
     if (rc == -1) {
         err = ngx_socket_errno;
@@ -272,7 +148,7 @@ ngx_event_connect_peer(ngx_peer_connecti
             }
 
             ngx_log_error(level, c->log, err, "connect() to %V failed",
-                          &peer->name);
+                          pc->name);
 
             return NGX_DECLINED;
         }
@@ -352,51 +228,11 @@ ngx_event_connect_peer(ngx_peer_connecti
     wev->ready = 1;
 
     return NGX_OK;
-
-failed:
-
-    /* all peers failed, mark them as live for quick recovery */
-
-    for (i = 0; i < pc->peers->number; i++) {
-        pc->peers->peer[i].fails = 0;
-    }
-
-    /* ngx_unlock_mutex(pc->peers->mutex); */
-
-    return NGX_BUSY;
 }
 
 
-void
-ngx_event_connect_peer_failed(ngx_peer_connection_t *pc, ngx_uint_t down)
+ngx_int_t
+ngx_event_get_peer(ngx_peer_connection_t *pc, void *data)
 {
-    time_t       now;
-    ngx_peer_t  *peer;
-
-    if (down) {
-        now = ngx_time();
-
-        /* ngx_lock_mutex(pc->peers->mutex); */
-
-        peer = &pc->peers->peer[pc->cur_peer];
-
-        peer->fails++;
-        peer->accessed = now;
-
-        if (peer->current_weight > 1) {
-            peer->current_weight /= 2;
-        }
-
-        /* ngx_unlock_mutex(pc->peers->mutex); */
-    }
-
-    pc->cur_peer++;
-
-    if (pc->cur_peer >= pc->peers->number) {
-        pc->cur_peer = 0;
-    }
-
-    if (pc->tries) {
-        pc->tries--;
-    }
+    return NGX_OK;
 }
--- a/src/event/ngx_event_connect.h
+++ b/src/event/ngx_event_connect.h
@@ -13,27 +13,56 @@
 #include <ngx_event.h>
 
 
-typedef struct {
-    ngx_peers_t        *peers;
-    ngx_uint_t          cur_peer;
-    ngx_uint_t          tries;
+#define NGX_PEER_KEEPALIVE   1
+#define NGX_PEER_NEXT        2
+#define NGX_PEER_FAILED      4
+
+
+typedef struct ngx_peer_connection_s  ngx_peer_connection_t;
+
+typedef ngx_int_t (*ngx_event_get_peer_pt)(ngx_peer_connection_t *pc,
+    void *data);
+#if (NGX_SSL)
+typedef void (*ngx_event_save_peer_pt)(ngx_peer_connection_t *pc, void *data);
+#endif
+typedef void (*ngx_event_free_peer_pt)(ngx_peer_connection_t *pc, void *data,
+    ngx_uint_t state);
+
 
-    ngx_connection_t   *connection;
-#if (NGX_THREADS)
-    ngx_atomic_t       *lock;
+struct ngx_peer_connection_s {
+    ngx_connection_t        *connection;
+
+    struct sockaddr         *sockaddr;
+    socklen_t                socklen;
+    ngx_str_t               *name;
+
+    ngx_uint_t               tries;
+
+    ngx_event_get_peer_pt    get;
+    ngx_event_free_peer_pt   free;
+    void                    *data;
+
+#if (NGX_SSL)
+    ngx_ssl_session_t       *ssl_session;
+    ngx_event_save_peer_pt   save_session;
 #endif
 
-    int                 rcvbuf;
+#if (NGX_THREADS)
+    ngx_atomic_t            *lock;
+#endif
 
-    ngx_log_t          *log;
+    int                      rcvbuf;
 
-    unsigned            cached:1;
-    unsigned            log_error:2;  /* ngx_connection_log_error_e */
-} ngx_peer_connection_t;
+    ngx_log_t               *log;
+
+    unsigned                 cached:1;
+    unsigned                 log_error:2;  /* ngx_connection_log_error_e */
+};
 
 
 ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc);
-void ngx_event_connect_peer_failed(ngx_peer_connection_t *pc, ngx_uint_t down);
+ngx_int_t ngx_event_get_peer(ngx_peer_connection_t *pc, void *data);
+
 
 
 #endif /* _NGX_EVENT_CONNECT_H_INCLUDED_ */
--- 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_pass(ngx_c
 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 @@ static ngx_command_t  ngx_http_fastcgi_c
 
     { 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 @@ ngx_http_fastcgi_handler(ngx_http_reques
 
     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 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
     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 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
     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_fastcgi_merge_loc_conf(ngx_conf
                                        |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 @@ ngx_http_fastcgi_merge_loc_conf(ngx_conf
 
 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_http_fastcgi_pass(ngx_conf_t *cf, ng
     ngx_memzero(&u, sizeof(ngx_url_t));
 
     u.url = value[1];
-    u.upstream = 1;
-
-    lcf->upstream_peers = ngx_http_upstream_add(cf, &u);
-    if (lcf->upstream_peers == NULL) {
+    u.no_resolve = 1;
+
+    lcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
+    if (lcf->upstream.upstream == NULL) {
         return NGX_CONF_ERROR;
     }
 
@@ -2084,3 +2061,29 @@ ngx_http_fastcgi_lowat_check(ngx_conf_t 
 
     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;
+}
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -76,9 +76,6 @@ static void *ngx_http_gzip_filter_alloc(
 static void ngx_http_gzip_filter_free(void *opaque, void *address);
 static void ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx);
 
-static u_char *ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op);
-
 static ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);
 static ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
@@ -230,14 +227,6 @@ ngx_module_t  ngx_http_gzip_filter_modul
 };
 
 
-static ngx_http_log_op_name_t ngx_http_gzip_log_fmt_ops[] = {
-    { ngx_string("gzip_ratio"), NGX_INT32_LEN + 3,
-                                NULL, NULL, ngx_http_gzip_log_ratio },
-    { ngx_null_string, 0, NULL, NULL, NULL }
-};
-
-
-
 static u_char  gzheader[10] = { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
 
 #if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
@@ -907,39 +896,6 @@ ngx_http_gzip_filter_free(void *opaque, 
 }
 
 
-static u_char *
-ngx_http_gzip_log_ratio(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    ngx_uint_t            zint, zfrac;
-    ngx_http_gzip_ctx_t  *ctx;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);
-
-    if (ctx == NULL || ctx->zout == 0) {
-        *buf = '-';
-        return buf + 1;
-    }
-
-    zint = (ngx_uint_t) (ctx->zin / ctx->zout);
-    zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);
-
-    if ((ctx->zin * 1000 / ctx->zout) % 10 > 4) {
-
-        /* the rounding, e.g., 2.125 to 2.13 */
-
-        zfrac++;
-
-        if (zfrac > 99) {
-            zint++;
-            zfrac = 0;
-        }
-    }
-
-    return ngx_sprintf(buf, "%ui.%02ui", zint, zfrac);
-}
-
-
 static void
 ngx_http_gzip_error(ngx_http_gzip_ctx_t *ctx)
 {
@@ -961,8 +917,7 @@ ngx_http_gzip_error(ngx_http_gzip_ctx_t 
 static ngx_int_t
 ngx_http_gzip_add_variables(ngx_conf_t *cf)
 {
-    ngx_http_variable_t     *var;
-    ngx_http_log_op_name_t  *op;
+    ngx_http_variable_t  *var;
 
     var = ngx_http_add_variable(cf, &ngx_http_gzip_ratio, NGX_HTTP_VAR_NOHASH);
     if (var == NULL) {
@@ -971,17 +926,6 @@ ngx_http_gzip_add_variables(ngx_conf_t *
 
     var->get_handler = ngx_http_gzip_ratio_variable;
 
-    for (op = ngx_http_gzip_log_fmt_ops; op->name.len; op++) { /* void */ }
-    op->run = NULL;
-
-    for (op = ngx_http_log_fmt_ops; op->run; op++) {
-        if (op->name.len == 0) {
-            op = (ngx_http_log_op_name_t *) op->run;
-        }
-    }
-
-    op->run = (ngx_http_log_op_run_pt) ngx_http_gzip_log_fmt_ops;
-
     return NGX_OK;
 }
 
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -10,6 +10,22 @@
 #include <nginx.h>
 
 
+typedef struct ngx_http_log_op_s  ngx_http_log_op_t;
+
+typedef u_char *(*ngx_http_log_op_run_pt) (ngx_http_request_t *r, u_char *buf,
+    ngx_http_log_op_t *op);
+
+typedef size_t (*ngx_http_log_op_getlen_pt) (ngx_http_request_t *r,
+    uintptr_t data);
+
+
+struct ngx_http_log_op_s {
+    size_t                      len;
+    ngx_http_log_op_getlen_pt   getlen;
+    ngx_http_log_op_run_pt      run;
+    uintptr_t                   data;
+};
+
 
 typedef struct {
     ngx_str_t                   name;
@@ -43,8 +59,6 @@ typedef struct {
 } ngx_http_log_var_t;
 
 
-static u_char *ngx_http_log_addr(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op);
 static u_char *ngx_http_log_connection(ngx_http_request_t *r, u_char *buf,
     ngx_http_log_op_t *op);
 static u_char *ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf,
@@ -64,41 +78,6 @@ static u_char *ngx_http_log_body_bytes_s
 static u_char *ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,
     ngx_http_log_op_t *op);
 
-static size_t ngx_http_log_request_getlen(ngx_http_request_t *r,
-    uintptr_t data);
-static u_char *ngx_http_log_request(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op);
-
-static ngx_int_t ngx_http_log_header_in_compile(ngx_conf_t *cf,
-    ngx_http_log_op_t *op, ngx_str_t *value);
-static size_t ngx_http_log_header_in_getlen(ngx_http_request_t *r,
-    uintptr_t data);
-static u_char *ngx_http_log_header_in(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op);
-static size_t ngx_http_log_unknown_header_in_getlen(ngx_http_request_t *r,
-    uintptr_t data);
-static u_char *ngx_http_log_unknown_header_in(ngx_http_request_t *r,
-    u_char *buf, ngx_http_log_op_t *op);
-
-static ngx_int_t ngx_http_log_header_out_compile(ngx_conf_t *cf,
-    ngx_http_log_op_t *op, ngx_str_t *value);
-static size_t ngx_http_log_header_out_getlen(ngx_http_request_t *r,
-    uintptr_t data);
-static u_char *ngx_http_log_header_out(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op);
-static size_t ngx_http_log_unknown_header_out_getlen(ngx_http_request_t *r,
-    uintptr_t data);
-static u_char *ngx_http_log_unknown_header_out(ngx_http_request_t *r,
-    u_char *buf, ngx_http_log_op_t *op);
-
-static u_char *ngx_http_log_connection_header_out(ngx_http_request_t *r,
-    u_char *buf, ngx_http_log_op_t *op);
-static u_char *ngx_http_log_transfer_encoding_header_out(ngx_http_request_t *r,
-    u_char *buf, ngx_http_log_op_t *op);
-
-static ngx_table_elt_t *ngx_http_log_unknown_header(ngx_list_t *headers,
-    ngx_str_t *value);
-
 static ngx_int_t ngx_http_log_variable_compile(ngx_conf_t *cf,
     ngx_http_log_op_t *op, ngx_str_t *value);
 static size_t ngx_http_log_variable_getlen(ngx_http_request_t *r,
@@ -107,7 +86,6 @@ static u_char *ngx_http_log_variable(ngx
     ngx_http_log_op_t *op);
 
 
-static ngx_int_t ngx_http_log_set_formats(ngx_conf_t *cf);
 static void *ngx_http_log_create_main_conf(ngx_conf_t *cf);
 static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf);
 static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent,
@@ -143,7 +121,7 @@ static ngx_command_t  ngx_http_log_comma
 
 
 static ngx_http_module_t  ngx_http_log_module_ctx = {
-    ngx_http_log_set_formats,              /* preconfiguration */
+    NULL,                                  /* preconfiguration */
     ngx_http_log_init,                     /* postconfiguration */
 
     ngx_http_log_create_main_conf,         /* create main configuration */
@@ -202,39 +180,6 @@ static ngx_http_log_var_t  ngx_http_log_
 };
 
 
-ngx_http_log_op_name_t  ngx_http_log_fmt_ops[] = {
-    { ngx_string("addr"), INET_ADDRSTRLEN - 1, NULL, NULL, ngx_http_log_addr },
-    { ngx_string("conn"), NGX_ATOMIC_T_LEN, NULL, NULL,
-                          ngx_http_log_connection },
-    { ngx_string("pipe"), 1, NULL, NULL, ngx_http_log_pipe },
-    { ngx_string("time"), sizeof("28/Sep/1970:12:00:00 +0600") - 1,
-                          NULL, NULL, ngx_http_log_time },
-    { ngx_string("msec"), NGX_TIME_T_LEN + 4, NULL, NULL, ngx_http_log_msec },
-    { ngx_string("request_time"), NGX_TIME_T_LEN, NULL, NULL,
-                          ngx_http_log_request_time },
-    { ngx_string("status"), 3, NULL, NULL, ngx_http_log_status },
-    { ngx_string("length"), NGX_OFF_T_LEN,
-                          NULL, NULL, ngx_http_log_bytes_sent },
-    { ngx_string("apache_length"), NGX_OFF_T_LEN,
-                          NULL, NULL, ngx_http_log_body_bytes_sent },
-    { ngx_string("request_length"), NGX_SIZE_T_LEN,
-                          NULL, NULL, ngx_http_log_request_length },
-
-    { ngx_string("request"), 0, NULL,
-                          ngx_http_log_request_getlen,
-                          ngx_http_log_request },
-
-    { ngx_string("i"), 0, ngx_http_log_header_in_compile, NULL,
-                          ngx_http_log_header_in },
-    { ngx_string("o"), 0, ngx_http_log_header_out_compile, NULL,
-                          ngx_http_log_header_out },
-    { ngx_string("v"), 0, ngx_http_log_variable_compile, NULL,
-                          ngx_http_log_variable },
-
-    { ngx_null_string, 0, NULL, NULL, NULL }
-};
-
-
 ngx_int_t
 ngx_http_log_handler(ngx_http_request_t *r)
 {
@@ -367,14 +312,6 @@ ngx_http_log_copy_long(ngx_http_request_
 
 
 static u_char *
-ngx_http_log_addr(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
-{
-    return ngx_cpymem(buf, r->connection->addr_text.data,
-                      r->connection->addr_text.len);
-}
-
-
-static u_char *
 ngx_http_log_connection(ngx_http_request_t *r, u_char *buf,
     ngx_http_log_op_t *op)
 {
@@ -426,20 +363,6 @@ ngx_http_log_request_time(ngx_http_reque
 }
 
 
-static size_t
-ngx_http_log_request_getlen(ngx_http_request_t *r, uintptr_t data)
-{
-    return r->request_line.len;
-}
-
-
-static u_char *
-ngx_http_log_request(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
-{
-    return ngx_cpymem(buf, r->request_line.data, r->request_line.len);
-}
-
-
 static u_char *
 ngx_http_log_status(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)
 {
@@ -483,363 +406,6 @@ ngx_http_log_request_length(ngx_http_req
 
 
 static ngx_int_t
-ngx_http_log_header_in_compile(ngx_conf_t *cf, ngx_http_log_op_t *op,
-    ngx_str_t *value)
-{
-    ngx_uint_t  i;
-
-    op->len = 0;
-
-    for (i = 0; ngx_http_headers_in[i].name.len != 0; i++) {
-
-        if (ngx_http_headers_in[i].name.len != value->len) {
-            continue;
-        }
-
-        /* STUB: "Cookie" speacial handling */
-        if (ngx_http_headers_in[i].offset == 0) {
-            continue;
-        }
-
-        if (ngx_strncasecmp(ngx_http_headers_in[i].name.data, value->data,
-                            value->len) == 0)
-        {
-            op->getlen = ngx_http_log_header_in_getlen;
-            op->run = ngx_http_log_header_in;
-            op->data = ngx_http_headers_in[i].offset;
-
-            return NGX_OK;
-        }
-    }
-
-    op->getlen = ngx_http_log_unknown_header_in_getlen;
-    op->run = ngx_http_log_unknown_header_in;
-    op->data = (uintptr_t) value;
-
-    return NGX_OK;
-}
-
-
-static size_t
-ngx_http_log_header_in_getlen(ngx_http_request_t *r, uintptr_t data)
-{
-    ngx_table_elt_t  *h;
-
-    h = *(ngx_table_elt_t **) ((char *) &r->headers_in + data);
-
-    if (h) {
-        return h->value.len;
-    }
-
-    return 1;
-}
-
-
-static u_char *
-ngx_http_log_header_in(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    ngx_table_elt_t  *h;
-
-    h = *(ngx_table_elt_t **) ((char *) &r->headers_in + op->data);
-
-    if (h) {
-        return ngx_cpymem(buf, h->value.data, h->value.len);
-    }
-
-    *buf = '-';
-
-    return buf + 1;
-}
-
-
-static size_t
-ngx_http_log_unknown_header_in_getlen(ngx_http_request_t *r, uintptr_t data)
-{
-    ngx_table_elt_t  *h;
-
-    h = ngx_http_log_unknown_header(&r->headers_in.headers, (ngx_str_t *) data);
-
-    if (h) {
-        return h->value.len;
-    }
-
-    return 1;
-}
-
-
-static u_char *
-ngx_http_log_unknown_header_in(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    ngx_table_elt_t  *h;
-
-    h = ngx_http_log_unknown_header(&r->headers_in.headers,
-                                    (ngx_str_t *) op->data);
-
-    if (h) {
-        return ngx_cpymem(buf, h->value.data, h->value.len);
-    }
-
-    *buf = '-';
-
-    return buf + 1;
-}
-
-
-static ngx_int_t
-ngx_http_log_header_out_compile(ngx_conf_t *cf, ngx_http_log_op_t *op,
-    ngx_str_t *value)
-{
-    ngx_uint_t  i;
-
-    op->len = 0;
-
-    for (i = 0; ngx_http_headers_out[i].name.len != 0; i++) {
-
-        if (ngx_http_headers_out[i].name.len != value->len) {
-            continue;
-        }
-
-        if (ngx_strncasecmp(ngx_http_headers_out[i].name.data, value->data,
-                            value->len) == 0)
-        {
-            op->getlen = ngx_http_log_header_out_getlen;
-            op->run = ngx_http_log_header_out;
-            op->data = ngx_http_headers_out[i].offset;
-
-            return NGX_OK;
-        }
-    }
-
-    if (value->len == sizeof("Connection") - 1
-        && ngx_strncasecmp(value->data, "Connection", value->len) == 0)
-    {
-        op->len = sizeof("keep-alive") - 1;
-        op->getlen = NULL;
-        op->run = ngx_http_log_connection_header_out;
-        op->data = 0;
-        return NGX_OK;
-    }
-
-    if (value->len == sizeof("Transfer-Encoding") - 1
-        && ngx_strncasecmp(value->data, "Transfer-Encoding", value->len) == 0)
-    {
-        op->len = sizeof("chunked") - 1;
-        op->getlen = NULL;
-        op->run = ngx_http_log_transfer_encoding_header_out;
-        op->data = 0;
-        return NGX_OK;
-    }
-
-    op->getlen = ngx_http_log_unknown_header_out_getlen;
-    op->run = ngx_http_log_unknown_header_out;
-    op->data = (uintptr_t) value;
-
-    return NGX_OK;
-}
-
-
-static size_t
-ngx_http_log_header_out_getlen(ngx_http_request_t *r, uintptr_t data)
-{
-    ngx_table_elt_t  *h;
-
-    h = *(ngx_table_elt_t **) ((char *) &r->headers_out + data);
-
-    if (h) {
-        return h->value.len;
-    }
-
-    /*
-     * No header pointer was found.
-     * However, some headers: "Date", "Server", "Content-Length",
-     * and "Last-Modified" have a special handling in the header filter,
-     * but we do not set up their pointers in the filter,
-     * because they are too seldom needed to be logged.
-     */
-
-    if (data == offsetof(ngx_http_headers_out_t, date)) {
-        return ngx_cached_http_time.len;
-    }
-
-    if (data == offsetof(ngx_http_headers_out_t, server)) {
-        return (sizeof(NGINX_VER) - 1);
-    }
-
-    if (data == offsetof(ngx_http_headers_out_t, content_length)) {
-        if (r->headers_out.content_length_n == -1) {
-            return 1;
-        }
-
-        return NGX_OFF_T_LEN;
-    }
-
-    if (data == offsetof(ngx_http_headers_out_t, last_modified)) {
-        if (r->headers_out.last_modified_time == -1) {
-            return 1;
-        }
-
-        return sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;
-    }
-
-    return 1;
-}
-
-
-static u_char *
-ngx_http_log_header_out(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    ngx_table_elt_t  *h;
-
-    h = *(ngx_table_elt_t **) ((char *) &r->headers_out + op->data);
-
-    if (h) {
-        return ngx_cpymem(buf, h->value.data, h->value.len);
-    }
-
-    /*
-     * No header pointer was found.
-     * However, some headers: "Date", "Server", "Content-Length",
-     * and "Last-Modified" have a special handling in the header filter,
-     * but we do not set up their pointers in the filter,
-     * because they are too seldom needed to be logged.
-     */
-
-    if (op->data == offsetof(ngx_http_headers_out_t, date)) {
-        return ngx_cpymem(buf, ngx_cached_http_time.data,
-                          ngx_cached_http_time.len);
-    }
-
-    if (op->data == offsetof(ngx_http_headers_out_t, server)) {
-        return ngx_cpymem(buf, NGINX_VER, sizeof(NGINX_VER) - 1);
-    }
-
-    if (op->data == offsetof(ngx_http_headers_out_t, content_length)) {
-        if (r->headers_out.content_length_n == -1) {
-            *buf = '-';
-
-            return buf + 1;
-        }
-
-        return ngx_sprintf(buf, "%O", r->headers_out.content_length_n);
-    }
-
-    if (op->data == offsetof(ngx_http_headers_out_t, last_modified)) {
-        if (r->headers_out.last_modified_time == -1) {
-            *buf = '-';
-
-            return buf + 1;
-        }
-
-        return ngx_http_time(buf, r->headers_out.last_modified_time);
-    }
-
-    *buf = '-';
-
-    return buf + 1;
-}
-
-
-static size_t
-ngx_http_log_unknown_header_out_getlen(ngx_http_request_t *r, uintptr_t data)
-{
-    ngx_table_elt_t  *h;
-
-    h = ngx_http_log_unknown_header(&r->headers_out.headers,
-                                    (ngx_str_t *) data);
-
-    if (h) {
-        return h->value.len;
-    }
-
-    return 1;
-}
-
-
-static u_char *
-ngx_http_log_unknown_header_out(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    ngx_table_elt_t  *h;
-
-    h = ngx_http_log_unknown_header(&r->headers_out.headers,
-                                    (ngx_str_t *) op->data);
-
-    if (h) {
-        return ngx_cpymem(buf, h->value.data, h->value.len);
-    }
-
-    *buf = '-';
-
-    return buf + 1;
-}
-
-
-static ngx_table_elt_t *
-ngx_http_log_unknown_header(ngx_list_t *headers, ngx_str_t *value)
-{
-    ngx_uint_t        i;
-    ngx_list_part_t  *part;
-    ngx_table_elt_t  *h;
-
-    part = &headers->part;
-    h = part->elts;
-
-    for (i = 0; /* void */; i++) {
-
-        if (i >= part->nelts) {
-            if (part->next == NULL) {
-                break;
-            }
-
-            part = part->next;
-            h = part->elts;
-            i = 0;
-        }
-
-        if (h[i].key.len != value->len) {
-            continue;
-        }
-
-        if (ngx_strncasecmp(h[i].key.data, value->data, value->len) == 0) {
-            return &h[i];
-        }
-    }
-
-    return NULL;
-}
-
-
-static u_char *
-ngx_http_log_connection_header_out(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    if (r->keepalive) {
-        return ngx_cpymem(buf, "keep-alive", sizeof("keep-alive") - 1);
-
-    } else {
-        return ngx_cpymem(buf, "close", sizeof("close") - 1);
-    }
-}
-
-
-static u_char *
-ngx_http_log_transfer_encoding_header_out(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    if (r->chunked) {
-        return ngx_cpymem(buf, "chunked", sizeof("chunked") - 1);
-    }
-
-    *buf = '-';
-
-    return buf + 1;
-}
-
-
-static ngx_int_t
 ngx_http_log_variable_compile(ngx_conf_t *cf, ngx_http_log_op_t *op,
     ngx_str_t *value)
 {
@@ -890,18 +456,6 @@ ngx_http_log_variable(ngx_http_request_t
 }
 
 
-static ngx_int_t
-ngx_http_log_set_formats(ngx_conf_t *cf)
-{
-    ngx_http_log_op_name_t  *op;
-
-    for (op = ngx_http_log_fmt_ops; op->name.len; op++) { /* void */ }
-    op->run = NULL;
-
-    return NGX_OK;
-}
-
-
 static void *
 ngx_http_log_create_main_conf(ngx_conf_t *cf)
 {
@@ -1152,20 +706,36 @@ static char *
 ngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *ops,
     ngx_array_t *args, ngx_uint_t s)
 {
-    u_char                  *data, *p, *fname, *arg_data, ch;
-    size_t                   i, len, fname_len, arg_len;
-    ngx_str_t               *value, var, *a;
-    ngx_uint_t               bracket;
-    ngx_http_log_op_t       *op;
-    ngx_http_log_var_t      *v;
-    ngx_http_log_op_name_t  *name;
-    static ngx_uint_t        warn;
+    u_char              *data, *p, ch;
+    size_t               i, len;
+    ngx_str_t           *value, var;
+    ngx_uint_t           bracket;
+    ngx_http_log_op_t   *op;
+    ngx_http_log_var_t  *v;
 
     value = args->elts;
-    arg_data = NULL;
 
     for ( /* void */ ; s < args->nelts; s++) {
 
+        for (i = 0; i < value[s].len; i++) {
+            if (value[s].data[i] != '%') {
+                continue;
+            }
+
+            ch = value[s].data[i + 1];
+
+            if ((ch >= 'A' && ch <= 'Z')
+                 || (ch >= 'a' && ch <= 'z')
+                 || ch == '{')
+            {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                     "the parameters in the \"%%name\" form are not supported, "
+                     "use the \"$variable\" instead");
+
+                return NGX_CONF_ERROR;
+            }
+        }
+
         i = 0;
 
         while (i < value[s].len) {
@@ -1177,112 +747,7 @@ ngx_http_log_compile_format(ngx_conf_t *
 
             data = &value[s].data[i];
 
-            if (value[s].data[i] == '%') {
-                i++;
-
-                if (i == value[s].len) {
-                    goto invalid;
-                }
-
-                if (value[s].data[i] == '{') {
-                    i++;
-
-                    arg_data = &value[s].data[i];
-
-                    while (i < value[s].len && value[s].data[i] != '}') {
-                        i++;
-                    }
-
-                    arg_len = &value[s].data[i] - arg_data;
-
-                    if (i == value[s].len || arg_len == 0) {
-                        goto invalid;
-                    }
-
-                    i++;
-
-                } else {
-                    arg_len = 0;
-                }
-
-                if (warn == 0) {
-                    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                        "the parameters in the \"%%name\" form are deprecated, "
-                        "use the \"$variable\" instead");
-                    warn = 1;
-                }
-
-                fname = &value[s].data[i];
-
-                while (i < value[s].len
-                       && ((value[s].data[i] >= 'a' && value[s].data[i] <= 'z')
-                           || value[s].data[i] == '_'))
-                {
-                    i++;
-                }
-
-                fname_len = &value[s].data[i] - fname;
-
-                if (fname_len == 0) {
-                    goto invalid;
-                }
-
-                for (name = ngx_http_log_fmt_ops; name->run; name++) {
-                    if (name->name.len == 0) {
-                        name = (ngx_http_log_op_name_t *) name->run;
-                    }
-
-                    if (name->name.len == fname_len
-                        && ngx_strncmp(name->name.data, fname, fname_len) == 0)
-                    {
-                        if (name->compile == NULL) {
-                            if (arg_len) {
-                                fname[fname_len] = '\0';
-                                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                                               "\"%s\" must not have argument",
-                                               data);
-                                return NGX_CONF_ERROR;
-                            }
-
-                            op->len = name->len;
-                            op->getlen = name->getlen;
-                            op->run = name->run;
-                            op->data = 0;
-
-                            break;
-                        }
-
-                        if (arg_len == 0) {
-                            fname[fname_len] = '\0';
-                            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                                               "\"%s\" requires argument",
-                                               data);
-                            return NGX_CONF_ERROR;
-                        }
-
-                        a = ngx_palloc(cf->pool, sizeof(ngx_str_t));
-                        if (a == NULL) {
-                            return NGX_CONF_ERROR;
-                        }
-
-                        a->len = arg_len;
-                        a->data = arg_data;
-
-                        if (name->compile(cf, op, a) == NGX_ERROR) {
-                            return NGX_CONF_ERROR;
-                        }
-
-                        break;
-                    }
-                }
-
-                if (name->name.len == 0) {
-                    goto invalid;
-                }
-
-                continue;
-
-            } else if (value[s].data[i] == '$') {
+            if (value[s].data[i] == '$') {
 
                 if (++i == value[s].len) {
                     goto invalid;
@@ -1364,10 +829,7 @@ ngx_http_log_compile_format(ngx_conf_t *
 
             i++;
 
-            while (i < value[s].len
-                   && value[s].data[i] != '$'
-                   && value[s].data[i] != '%')
-            {
+            while (i < value[s].len && value[s].data[i] != '$') {
                 i++;
             }
 
deleted file mode 100644
--- a/src/http/modules/ngx_http_log_module.h
+++ /dev/null
@@ -1,48 +0,0 @@
-
-/*
- * Copyright (C) Igor Sysoev
- */
-
-
-#ifndef _NGX_HTTP_LOG_MODULE_H_INCLUDED_
-#define _NGX_HTTP_LOG_MODULE_H_INCLUDED_
-
-
-#include <ngx_config.h>
-#include <ngx_core.h>
-#include <ngx_http.h>
-
-
-typedef struct ngx_http_log_op_s  ngx_http_log_op_t;
-
-typedef u_char *(*ngx_http_log_op_run_pt) (ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op);
-
-typedef size_t (*ngx_http_log_op_getlen_pt) (ngx_http_request_t *r,
-    uintptr_t data);
-
-typedef ngx_int_t (*ngx_http_log_op_compile_pt) (ngx_conf_t *cf,
-    ngx_http_log_op_t *op, ngx_str_t *value);
-
-
-struct ngx_http_log_op_s {
-    size_t                      len;
-    ngx_http_log_op_getlen_pt   getlen;
-    ngx_http_log_op_run_pt      run;
-    uintptr_t                   data;
-};
-
-
-typedef struct {
-    ngx_str_t                   name;
-    size_t                      len;
-    ngx_http_log_op_compile_pt  compile;
-    ngx_http_log_op_getlen_pt   getlen;
-    ngx_http_log_op_run_pt      run;
-} ngx_http_log_op_name_t;
-
-
-extern ngx_http_log_op_name_t ngx_http_log_fmt_ops[];
-
-
-#endif /* _NGX_HTTP_LOG_MODULE_H_INCLUDED_ */
--- 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_merge_lo
 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 @@ static ngx_command_t  ngx_http_memcached
 
     { 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 @@ ngx_http_memcached_handler(ngx_http_requ
 
     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 @@ ngx_http_memcached_create_loc_conf(ngx_c
 
     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 */
+    /* the hardcoded values */
     conf->upstream.cyclic_temp_file = 0;
-
-    /* the hardcoded values */
     conf->upstream.buffering = 0;
     conf->upstream.ignore_client_abort = 0;
     conf->upstream.send_lowat = 0;
@@ -540,8 +537,6 @@ ngx_http_memcached_merge_loc_conf(ngx_co
     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_memcached_merge_loc_conf(ngx_co
                                        |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_http_memcached_pass(ngx_conf_t *cf, 
     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 @@ ngx_http_memcached_pass(ngx_conf_t *cf, 
 
     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;
+}
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -35,8 +35,6 @@ struct ngx_http_proxy_redirect_s {
 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_redirect(ngx
 
 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 @@ static ngx_command_t  ngx_http_proxy_com
 
     { 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 @@ ngx_http_proxy_handler(ngx_http_request_
 
     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 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
     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 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
     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_proxy_merge_loc_conf(ngx_conf_t
                                        |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 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t
 
 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 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
     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 @@ ngx_http_proxy_lowat_check(ngx_conf_t *c
 
     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;
+}
new file mode 100644
--- /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;
+}
--- a/src/http/modules/ngx_http_userid_filter_module.c
+++ b/src/http/modules/ngx_http_userid_filter_module.c
@@ -46,15 +46,6 @@ static void ngx_http_userid_get_uid(ngx_
 static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,
     ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
 
-static size_t ngx_http_userid_log_uid_got_getlen(ngx_http_request_t *r,
-    uintptr_t data);
-static u_char *ngx_http_userid_log_uid_got(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op);
-static size_t ngx_http_userid_log_uid_set_getlen(ngx_http_request_t *r,
-    uintptr_t data);
-static u_char *ngx_http_userid_log_uid_set(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op);
-
 static ngx_int_t ngx_http_userid_add_variables(ngx_conf_t *cf);
 static ngx_int_t ngx_http_userid_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
@@ -191,17 +182,6 @@ ngx_module_t  ngx_http_userid_filter_mod
 };
 
 
-static ngx_http_log_op_name_t ngx_http_userid_log_fmt_ops[] = {
-    { ngx_string("uid_got"), 0, NULL,
-                                ngx_http_userid_log_uid_got_getlen,
-                                ngx_http_userid_log_uid_got },
-    { ngx_string("uid_set"), 0, NULL,
-                                ngx_http_userid_log_uid_set_getlen,
-                                ngx_http_userid_log_uid_set },
-    { ngx_null_string, 0, NULL, NULL, NULL }
-};
-
-
 static ngx_str_t  ngx_http_userid_got = ngx_string("uid_got");
 static ngx_str_t  ngx_http_userid_set = ngx_string("uid_set");
 
@@ -462,99 +442,10 @@ ngx_http_userid_set_uid(ngx_http_request
 }
 
 
-static size_t
-ngx_http_userid_log_uid_got_getlen(ngx_http_request_t *r, uintptr_t data)
-{
-    ngx_http_userid_ctx_t   *ctx;
-    ngx_http_userid_conf_t  *conf;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
-
-    if (ctx == NULL || ctx->uid_got[3] == 0) {
-        return 1;
-    }
-
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
-
-    return conf->name.len + 1 + 32;
-}
-
-
-static u_char *
-ngx_http_userid_log_uid_got(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    ngx_http_userid_ctx_t   *ctx;
-    ngx_http_userid_conf_t  *conf;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
-
-    if (ctx == NULL || ctx->uid_got[3] == 0) {
-        *buf = '-';
-        return buf + 1;
-    }
-
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
-
-    buf = ngx_copy(buf, conf->name.data, conf->name.len);
-
-    *buf++ = '=';
-
-    return ngx_sprintf(buf, "%08XD%08XD%08XD%08XD",
-                       ctx->uid_got[0], ctx->uid_got[1],
-                       ctx->uid_got[2], ctx->uid_got[3]);
-}
-
-
-static size_t
-ngx_http_userid_log_uid_set_getlen(ngx_http_request_t *r, uintptr_t data)
-{
-    ngx_http_userid_ctx_t   *ctx;
-    ngx_http_userid_conf_t  *conf;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
-
-    if (ctx == NULL || ctx->uid_set[3] == 0) {
-        return 1;
-    }
-
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
-
-    return conf->name.len + 1 + 32;
-}
-
-
-static u_char *
-ngx_http_userid_log_uid_set(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    ngx_http_userid_ctx_t   *ctx;
-    ngx_http_userid_conf_t  *conf;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);
-
-    if (ctx == NULL || ctx->uid_set[3] == 0) {
-        *buf = '-';
-        return buf + 1;
-    }
-
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
-
-    buf = ngx_copy(buf, conf->name.data, conf->name.len);
-
-    *buf++ = '=';
-
-    return ngx_sprintf(buf, "%08XD%08XD%08XD%08XD",
-                       ctx->uid_set[0], ctx->uid_set[1],
-                       ctx->uid_set[2], ctx->uid_set[3]);
-}
-
-
 static ngx_int_t
 ngx_http_userid_add_variables(ngx_conf_t *cf)
 {
-    ngx_http_variable_t     *var;
-    ngx_http_log_op_name_t  *op;
+    ngx_http_variable_t  *var;
 
     var = ngx_http_add_variable(cf, &ngx_http_userid_got, NGX_HTTP_VAR_NOHASH);
     if (var == NULL) {
@@ -572,18 +463,6 @@ ngx_http_userid_add_variables(ngx_conf_t
     var->get_handler = ngx_http_userid_variable;
     var->data = offsetof(ngx_http_userid_ctx_t, uid_set);
 
-
-    for (op = ngx_http_userid_log_fmt_ops; op->name.len; op++) { /* void */ }
-    op->run = NULL;
-
-    for (op = ngx_http_log_fmt_ops; op->run; op++) {
-        if (op->name.len == 0) {
-            op = (ngx_http_log_op_name_t *) op->run;
-        }
-    }
-
-    op->run = (ngx_http_log_op_run_pt) ngx_http_userid_log_fmt_ops;
-
     return NGX_OK;
 }
 
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -17,7 +17,7 @@ our @EXPORT = qw(
     HTTP_SERVER_ERROR
 );
 
-our $VERSION = '0.4.14';
+our $VERSION = '0.5.0';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -4,6 +4,8 @@
  */
 
 
+#define PERL_NO_GET_CONTEXT
+
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.h>
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -32,9 +32,9 @@ typedef u_char *(*ngx_http_log_handler_p
 #include <ngx_http_variables.h>
 #include <ngx_http_request.h>
 #include <ngx_http_upstream.h>
+#include <ngx_http_upstream_round_robin.h>
 #include <ngx_http_config.h>
 #include <ngx_http_busy_lock.h>
-#include <ngx_http_log_module.h>
 #include <ngx_http_core_module.h>
 #include <ngx_http_script.h>
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -2409,9 +2409,9 @@ static u_char *
 ngx_http_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr,
     u_char *buf, size_t len)
 {
-    u_char                 *p;
-    ngx_http_upstream_t    *u;
-    ngx_peer_connection_t  *peer;
+    char                 *uri_separator;
+    u_char               *p;
+    ngx_http_upstream_t  *u;
 
     if (r->server_name.data) {
         p = ngx_snprintf(buf, len, ", server: %V", &r->server_name);
@@ -2451,14 +2451,19 @@ ngx_http_log_error_handler(ngx_http_requ
 
     u = sr->upstream;
 
-    if (u) {
-        peer = &u->peer;
+    if (u && u->peer.name) {
+
+        uri_separator = "";
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+        if (u->peer.sockaddr && u->peer.sockaddr->sa_family == AF_UNIX) {
+            uri_separator = ":";
+        }
+#endif
 
         p = ngx_snprintf(buf, len, ", upstream: \"%V%V%s%V\"",
-                         &u->conf->schema,
-                         &peer->peers->peer[peer->cur_peer].name,
-                         peer->peers->peer[peer->cur_peer].uri_separator,
-                         &u->uri);
+                         &u->conf->schema, u->peer.name,
+                         uri_separator, &u->uri);
         len -= p - buf;
         buf = p;
     }
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -70,15 +70,6 @@ static ngx_int_t ngx_http_upstream_copy_
     ngx_table_elt_t *h, ngx_uint_t offset);
 #endif
 
-static size_t ngx_http_upstream_log_status_getlen(ngx_http_request_t *r,
-    uintptr_t data);
-static u_char *ngx_http_upstream_log_status(ngx_http_request_t *r,
-    u_char *buf, ngx_http_log_op_t *op);
-static size_t ngx_http_upstream_log_response_time_getlen(ngx_http_request_t *r,
-    uintptr_t data);
-static u_char *ngx_http_upstream_log_response_time(ngx_http_request_t *r,
-    u_char *buf, ngx_http_log_op_t *op);
-
 static ngx_int_t ngx_http_upstream_add_variables(ngx_conf_t *cf);
 static ngx_int_t ngx_http_upstream_status_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
@@ -96,8 +87,6 @@ static char *ngx_http_upstream_init_main
 static void ngx_http_upstream_ssl_init_connection(ngx_http_request_t *,
     ngx_http_upstream_t *u, ngx_connection_t *c);
 static void ngx_http_upstream_ssl_handshake(ngx_connection_t *c);
-static void ngx_http_upstream_ssl_shutdown(ngx_connection_t *c,
-    ngx_peer_t *peer);
 #endif
 
 
@@ -224,7 +213,7 @@ static ngx_command_t  ngx_http_upstream_
       NULL },
 
     { ngx_string("server"),
-      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,
+      NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,
       ngx_http_upstream_server,
       NGX_HTTP_SRV_CONF_OFFSET,
       0,
@@ -265,17 +254,6 @@ ngx_module_t  ngx_http_upstream_module =
 };
 
 
-static ngx_http_log_op_name_t  ngx_http_upstream_log_fmt_ops[] = {
-    { ngx_string("upstream_status"), 0, NULL,
-                                    ngx_http_upstream_log_status_getlen,
-                                    ngx_http_upstream_log_status },
-    { ngx_string("upstream_response_time"), 0, NULL,
-                                    ngx_http_upstream_log_response_time_getlen,
-                                    ngx_http_upstream_log_response_time },
-    { ngx_null_string, 0, NULL, NULL, NULL }
-};
-
-
 static ngx_http_variable_t  ngx_http_upstream_vars[] = {
 
     { ngx_string("upstream_status"), NULL,
@@ -328,13 +306,16 @@ ngx_http_upstream_init(ngx_http_request_
         u->request_bufs = r->request_body->bufs;
     }
 
+    if (u->conf->upstream->peer.init(r, u->conf->upstream) != NGX_OK) {
+        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
     if (u->create_request(r) != NGX_OK) {
         ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
     }
 
-    u->peer.log = r->connection->log;
-
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
     u->output.sendfile = r->connection->sendfile;
@@ -346,7 +327,7 @@ ngx_http_upstream_init(ngx_http_request_
 
     u->writer.pool = r->pool;
 
-    if (ngx_array_init(&u->states, r->pool, u->peer.peers->number,
+    if (ngx_array_init(&u->states, r->pool, 1,
                        sizeof(ngx_http_upstream_state_t))
         != NGX_OK)
     {
@@ -548,7 +529,7 @@ ngx_http_upstream_connect(ngx_http_reque
         return;
     }
 
-    u->state->peer = &u->peer.peers->peer[u->peer.cur_peer].name;
+    u->state->peer = u->peer.name;
 
     if (rc == NGX_BUSY) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");
@@ -637,8 +618,7 @@ static void
 ngx_http_upstream_ssl_init_connection(ngx_http_request_t *r,
     ngx_http_upstream_t *u, ngx_connection_t *c)
 {
-    ngx_int_t    rc;
-    ngx_peer_t  *peer;
+    ngx_int_t   rc;
 
     if (ngx_ssl_create_connection(u->conf->ssl, c,
                                   NGX_SSL_BUFFER|NGX_SSL_CLIENT)
@@ -652,9 +632,7 @@ ngx_http_upstream_ssl_init_connection(ng
     c->sendfile = 0;
     u->output.sendfile = 0;
 
-    peer = &u->peer.peers->peer[u->peer.cur_peer];
-
-    if (ngx_ssl_set_session(c, peer->ssl_session) != NGX_OK) {
+    if (ngx_ssl_set_session(c, u->peer.ssl_session) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u,
                                            NGX_HTTP_INTERNAL_SERVER_ERROR);
         return;
@@ -684,6 +662,8 @@ ngx_http_upstream_ssl_handshake(ngx_conn
 
     if (c->ssl->handshaked) {
 
+        u->peer.save_session(&u->peer, u->peer.data);
+
         c->write->handler = ngx_http_upstream_send_request_handler;
         c->read->handler = ngx_http_upstream_process_header;
 
@@ -992,7 +972,7 @@ ngx_http_upstream_process_header(ngx_eve
     }
 
     n = u->peer.connection->recv(u->peer.connection, u->buffer.last,
-                                 u->buffer.end - u->buffer.last);
+                                      u->buffer.end - u->buffer.last);
 
     if (n == NGX_AGAIN) {
 #if 0
@@ -1605,7 +1585,7 @@ ngx_http_upstream_process_non_buffered_b
     ngx_buf_t                 *b;
     ngx_int_t                  rc;
     ngx_uint_t                 do_write;
-    ngx_connection_t          *c, *client;
+    ngx_connection_t          *c, *downstream, *upstream;
     ngx_http_request_t        *r;
     ngx_http_upstream_t       *u;
     ngx_http_core_loc_conf_t  *clcf;
@@ -1638,7 +1618,8 @@ ngx_http_upstream_process_non_buffered_b
         return;
     }
 
-    client = r->connection;
+    downstream = r->connection;
+    upstream = u->peer.connection;
 
     b = &u->buffer;
 
@@ -1653,7 +1634,7 @@ ngx_http_upstream_process_non_buffered_b
             if (u->out_bufs || u->busy_bufs) {
                 rc = ngx_http_output_filter(r, u->out_bufs);
 
-                if (client->destroyed) {
+                if (downstream->destroyed) {
                     return;
                 }
 
@@ -1669,8 +1650,8 @@ ngx_http_upstream_process_non_buffered_b
             if (u->busy_bufs == NULL) {
 
                 if (u->length == 0
-                    || u->peer.connection->read->eof
-                    || u->peer.connection->read->error)
+                    || upstream->read->eof
+                    || upstream->read->error)
                 {
                     ngx_http_upstream_finalize_request(r, u, 0);
                     return;
@@ -1687,9 +1668,9 @@ ngx_http_upstream_process_non_buffered_b
             size = u->length;
         }
 
-        if (size && u->peer.connection->read->ready) {
-
-            n = u->peer.connection->recv(u->peer.connection, b->last, size);
+        if (size && upstream->read->ready) {
+
+            n = upstream->recv(upstream, b->last, size);
 
             if (n == NGX_AGAIN) {
                 break;
@@ -1710,8 +1691,8 @@ ngx_http_upstream_process_non_buffered_b
         break;
     }
 
-    if (client->data == r) {
-        if (ngx_handle_write_event(client->write, clcf->send_lowat)
+    if (downstream->data == r) {
+        if (ngx_handle_write_event(downstream->write, clcf->send_lowat)
             == NGX_ERROR)
         {
             ngx_http_upstream_finalize_request(r, u, 0);
@@ -1719,23 +1700,23 @@ ngx_http_upstream_process_non_buffered_b
         }
     }
 
-    if (client->write->active) {
-        ngx_add_timer(client->write, clcf->send_timeout);
-
-    } else if (client->write->timer_set) {
-        ngx_del_timer(client->write);
+    if (downstream->write->active) {
+        ngx_add_timer(downstream->write, clcf->send_timeout);
+
+    } else if (downstream->write->timer_set) {
+        ngx_del_timer(downstream->write);
     }
 
-    if (ngx_handle_read_event(u->peer.connection->read, 0) == NGX_ERROR) {
+    if (ngx_handle_read_event(upstream->read, 0) == NGX_ERROR) {
         ngx_http_upstream_finalize_request(r, u, 0);
         return;
     }
 
-    if (u->peer.connection->read->active) {
-        ngx_add_timer(u->peer.connection->read, u->conf->read_timeout);
-
-    } else if (u->peer.connection->read->timer_set) {
-        ngx_del_timer(u->peer.connection->read);
+    if (upstream->read->active) {
+        ngx_add_timer(upstream->read, u->conf->read_timeout);
+
+    } else if (upstream->read->timer_set) {
+        ngx_del_timer(upstream->read);
     }
 }
 
@@ -1942,22 +1923,22 @@ static void
 ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,
     ngx_uint_t ft_type)
 {
-    ngx_uint_t  status, down;
+    ngx_uint_t  status, state;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http next upstream, %xD", ft_type);
+                   "http next upstream, %xi", ft_type);
 
 #if 0
     ngx_http_busy_unlock(u->conf->busy_lock, &u->busy_lock);
 #endif
 
     if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404) {
-        down = 0;
+        state = NGX_PEER_NEXT;
     } else {
-        down = 1;
+        state = NGX_PEER_FAILED;
     }
 
-    ngx_event_connect_peer_failed(&u->peer, down);
+    u->peer.free(&u->peer, u->peer.data, state);
 
     if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) {
         ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT,
@@ -2023,11 +2004,15 @@ ngx_http_upstream_next(ngx_http_request_
                        "close http upstream connection: %d",
                        u->peer.connection->fd);
 #if (NGX_HTTP_SSL)
+
         if (u->peer.connection->ssl) {
-            ngx_http_upstream_ssl_shutdown(u->peer.connection,
-                                       &u->peer.peers->peer[u->peer.cur_peer]);
+            u->peer.connection->ssl->no_wait_shutdown = 1;
+            u->peer.connection->ssl->no_send_shutdown = 1;
+
+            (void) ngx_ssl_shutdown(u->peer.connection);
         }
 #endif
+
         ngx_close_connection(u->peer.connection);
     }
 
@@ -2074,19 +2059,32 @@ ngx_http_upstream_finalize_request(ngx_h
 
     u->finalize_request(r, rc);
 
+    u->peer.free(&u->peer, u->peer.data, 0);
+
     if (u->peer.connection) {
-        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                       "close http upstream connection: %d",
-                       u->peer.connection->fd);
+
 #if (NGX_HTTP_SSL)
 
         /* TODO: do not shutdown persistent connection */
 
         if (u->peer.connection->ssl) {
-            ngx_http_upstream_ssl_shutdown(u->peer.connection,
-                                       &u->peer.peers->peer[u->peer.cur_peer]);
+
+            /*
+             * We send the "close notify" shutdown alert to the upstream only
+             * and do not wait its "close notify" shutdown alert.
+             * It is acceptable according to the TLS standard.
+             */
+
+            u->peer.connection->ssl->no_wait_shutdown = 1;
+
+            (void) ngx_ssl_shutdown(u->peer.connection);
         }
 #endif
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "close http upstream connection: %d",
+                       u->peer.connection->fd);
+
         ngx_close_connection(u->peer.connection);
     }
 
@@ -2125,35 +2123,6 @@ ngx_http_upstream_finalize_request(ngx_h
 }
 
 
-#if (NGX_HTTP_SSL)
-
-static void
-ngx_http_upstream_ssl_shutdown(ngx_connection_t *c, ngx_peer_t *peer)
-{
-    /* lock peer mutex */
-
-    if (peer->ssl_session) {
-        ngx_ssl_free_session(peer->ssl_session);
-    }
-
-    peer->ssl_session = ngx_ssl_get_session(c);
-
-    /* unlock peer mutex */
-
-    /*
-     * We send the "close notify" shutdown alert to the upstream only
-     * and do not wait its "close notify" shutdown alert.
-     * It is acceptable according to the TLS standard.
-     */
-
-    c->ssl->no_wait_shutdown = 1;
-
-    (void) ngx_ssl_shutdown(c);
-}
-
-#endif
-
-
 static ngx_int_t
 ngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
@@ -2481,108 +2450,10 @@ ngx_http_upstream_copy_content_encoding(
 #endif
 
 
-static size_t
-ngx_http_upstream_log_status_getlen(ngx_http_request_t *r, uintptr_t data)
-{
-    if (r->upstream) {
-        return r->upstream->states.nelts * (3 + 2);
-    }
-
-    return 1;
-}
-
-
-static u_char *
-ngx_http_upstream_log_status(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    ngx_uint_t                  i;
-    ngx_http_upstream_t        *u;
-    ngx_http_upstream_state_t  *state;
-
-    u = r->upstream;
-
-    if (u == NULL || u->states.nelts == 0) {
-        *buf = '-';
-        return buf + 1;
-    }
-
-    i = 0;
-    state = u->states.elts;
-
-    for ( ;; ) {
-        if (state[i].status == 0) {
-            *buf++ = '-';
-
-        } else {
-            buf = ngx_sprintf(buf, "%ui", state[i].status);
-        }
-
-        if (++i == u->states.nelts) {
-            return buf;
-        }
-
-        *buf++ = ',';
-        *buf++ = ' ';
-    }
-}
-
-
-static size_t
-ngx_http_upstream_log_response_time_getlen(ngx_http_request_t *r,
-    uintptr_t data)
-{
-    if (r->upstream) {
-        return r->upstream->states.nelts * (NGX_TIME_T_LEN + 4 + 2);
-    }
-
-    return 1;
-}
-
-
-static u_char *
-ngx_http_upstream_log_response_time(ngx_http_request_t *r, u_char *buf,
-    ngx_http_log_op_t *op)
-{
-    ngx_uint_t                  i;
-    ngx_http_upstream_t        *u;
-    ngx_http_upstream_state_t  *state;
-
-    u = r->upstream;
-
-    if (u == NULL || u->states.nelts == 0) {
-        *buf = '-';
-        return buf + 1;
-    }
-
-    i = 0;
-    state = u->states.elts;
-
-    for ( ;; ) {
-        if (state[i].status == 0) {
-            *buf++ = '-';
-
-        } else {
-            buf = ngx_sprintf(buf, "%d.%03d",
-                               state[i].response_time / 1000,
-                               state[i].response_time % 1000);
-        }
-
-        if (++i == u->states.nelts) {
-            return buf;
-        }
-
-        *buf++ = ',';
-        *buf++ = ' ';
-    }
-}
-
-
 static ngx_int_t
 ngx_http_upstream_add_variables(ngx_conf_t *cf)
 {
-    ngx_http_variable_t     *var, *v;
-    ngx_http_log_op_name_t  *op;
+    ngx_http_variable_t  *var, *v;
 
     for (v = ngx_http_upstream_vars; v->name.len; v++) {
         var = ngx_http_add_variable(cf, &v->name, v->flags);
@@ -2594,17 +2465,6 @@ ngx_http_upstream_add_variables(ngx_conf
         var->data = v->data;
     }
 
-    for (op = ngx_http_upstream_log_fmt_ops; op->name.len; op++) { /* void */ }
-    op->run = NULL;
-
-    for (op = ngx_http_log_fmt_ops; op->run; op++) {
-        if (op->name.len == 0) {
-            op = (ngx_http_log_op_name_t *) op->run;
-        }
-    }
-
-    op->run = (ngx_http_log_op_run_pt) ngx_http_upstream_log_fmt_ops;
-
     return NGX_OK;
 }
 
@@ -2724,31 +2584,41 @@ ngx_http_upstream_response_time_variable
 static char *
 ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)
 {
-    char                           *rv;
-    void                           *mconf;
-    ngx_str_t                      *value;
-    ngx_url_t                       u;
-    ngx_uint_t                      i, j, m, n;
-    ngx_conf_t                      pcf;
-    ngx_peers_t                   **peers;
-    ngx_http_module_t              *module;
-    ngx_http_conf_ctx_t            *ctx;
-    ngx_http_upstream_srv_conf_t   *uscf;
+    char                          *rv;
+    void                          *mconf;
+    ngx_str_t                     *value;
+    ngx_url_t                      u;
+    ngx_uint_t                     m;
+    ngx_conf_t                     pcf;
+    ngx_http_module_t             *module;
+    ngx_http_conf_ctx_t           *ctx, *http_ctx;
+    ngx_http_upstream_srv_conf_t  *uscf;
+
+    ngx_memzero(&u, sizeof(ngx_url_t));
+
+    value = cf->args->elts;
+    u.host = value[1];
+    u.upstream = 1;
+    u.no_resolve = 1;
+
+    uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE
+                                         |NGX_HTTP_UPSTREAM_WEIGHT
+                                         |NGX_HTTP_UPSTREAM_MAX_FAILS
+                                         |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT
+                                         |NGX_HTTP_UPSTREAM_DOWN
+                                         |NGX_HTTP_UPSTREAM_BACKUP);
+    if (uscf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
 
     ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));
     if (ctx == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    ngx_memzero(&u, sizeof(ngx_url_t));
-
-    value = cf->args->elts;
-    u.host = value[1];
-
-    uscf = ngx_http_upstream_add(cf, &u);
-    if (uscf == NULL) {
-        return NGX_CONF_ERROR;
-    }
+    http_ctx = cf->ctx;
+    ctx->main_conf = http_ctx->main_conf;
 
     /* the upstream{}'s srv_conf */
 
@@ -2759,6 +2629,8 @@ ngx_http_upstream(ngx_conf_t *cf, ngx_co
 
     ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf;
 
+    uscf->srv_conf = ctx->srv_conf;
+
 
     /* the upstream{}'s loc_conf */
 
@@ -2774,6 +2646,15 @@ ngx_http_upstream(ngx_conf_t *cf, ngx_co
 
         module = ngx_modules[m]->ctx;
 
+        if (module->create_srv_conf) {
+            mconf = module->create_srv_conf(cf);
+            if (mconf == NULL) {
+                return NGX_CONF_ERROR;
+            }
+
+            ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;
+        }
+
         if (module->create_loc_conf) {
             mconf = module->create_loc_conf(cf);
             if (mconf == NULL) {
@@ -2805,34 +2686,6 @@ ngx_http_upstream(ngx_conf_t *cf, ngx_co
         return NGX_CONF_ERROR;
     }
 
-    peers = uscf->servers->elts;
-
-    if (uscf->servers->nelts == 1) {
-        uscf->peers = peers[0];
-    }
-
-    n = 0;
-
-    for (i = 0; i < uscf->servers->nelts; i++) {
-        n += peers[i]->number;
-    }
-
-    uscf->peers = ngx_pcalloc(cf->pool,
-                           sizeof(ngx_peers_t) + sizeof(ngx_peer_t) * (n - 1));
-    if (uscf->peers == NULL) {
-        return NGX_CONF_ERROR;
-    }
-
-    uscf->peers->number = n;
-
-    n = 0;
-
-    for (i = 0; i < uscf->servers->nelts; i++) {
-        for (j = 0; j < peers[i]->number; j++) {
-            uscf->peers->peer[n++] = peers[i]->peer[j];
-        }
-    }
-
     return rv;
 }
 
@@ -2842,24 +2695,28 @@ ngx_http_upstream_server(ngx_conf_t *cf,
 {
     ngx_http_upstream_srv_conf_t  *uscf = conf;
 
-    ngx_str_t                    *value;
-    ngx_url_t                     u;
-    ngx_int_t                     weight;
-    ngx_uint_t                    i;
-    ngx_peers_t                 **peers;
+    time_t                       fail_timeout;
+    ngx_str_t                   *value, s;
+    ngx_url_t                    u;
+    ngx_int_t                    weight, max_fails;
+    ngx_uint_t                   i;
+    ngx_http_upstream_server_t  *us;
 
     if (uscf->servers == NULL) {
-        uscf->servers = ngx_array_create(cf->pool, 4, sizeof(ngx_peers_t *));
+        uscf->servers = ngx_array_create(cf->pool, 4,
+                                         sizeof(ngx_http_upstream_server_t));
         if (uscf->servers == NULL) {
             return NGX_CONF_ERROR;
         }
     }
 
-    peers = ngx_array_push(uscf->servers);
-    if (peers == NULL) {
+    us = ngx_array_push(uscf->servers);
+    if (us == NULL) {
         return NGX_CONF_ERROR;
     }
 
+    ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
+
     value = cf->args->elts;
 
     ngx_memzero(&u, sizeof(ngx_url_t));
@@ -2877,51 +2734,100 @@ ngx_http_upstream_server(ngx_conf_t *cf,
     }
 
     weight = 1;
-
-    if (cf->args->nelts == 3) {
-
-        value = &value[2];
-
-        if (ngx_strncmp(value->data, "weight=", 7) == 0) {
-
-            weight = ngx_atoi(&value->data[7], value->len - 7);
+    max_fails = 1;
+    fail_timeout = 10;
+
+    for (i = 2; i < cf->args->nelts; i++) {
+
+        if (ngx_strncmp(value[i].data, "weight=", 7) == 0) {
+
+            if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) {
+                goto invalid;
+            }
+
+            weight = ngx_atoi(&value[i].data[7], value[i].len - 7);
 
             if (weight == NGX_ERROR || weight == 0) {
                 goto invalid;
             }
 
-        } else {
-            goto invalid;
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "max_fails=", 10) == 0) {
+
+            if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) {
+                goto invalid;
+            }
+
+            max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);
+
+            if (max_fails == NGX_ERROR) {
+                goto invalid;
+            }
+
+            continue;
         }
+
+        if (ngx_strncmp(value[i].data, "fail_timeout=", 13) == 0) {
+
+            if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) {
+                goto invalid;
+            }
+
+            s.len = value[i].len - 13;
+            s.data = &value[i].data[13];
+
+            fail_timeout = ngx_parse_time(&s, 1);
+
+            if (fail_timeout < 0) {
+                goto invalid;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "down", 4) == 0) {
+
+            if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) {
+                goto invalid;
+            }
+
+            us->down = 1;
+
+            continue;
+        }
+
+        goto invalid;
     }
 
-    for (i = 0; i < u.peers->number; i++) {
-        u.peers->peer[i].weight = weight;
-        u.peers->peer[i].current_weight = weight;
-        u.peers->peer[i].max_fails = NGX_CONF_UNSET_UINT;
-        u.peers->peer[i].fail_timeout = NGX_CONF_UNSET;
-    }
-
-    *peers = u.peers;
+    us->addrs = u.addrs;
+    us->naddrs = u.naddrs;
+    us->weight = weight;
+    us->max_fails = max_fails;
+    us->fail_timeout = fail_timeout;
 
     return NGX_CONF_OK;
 
 invalid:
 
-    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", value);
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                       "invalid parameter \"%V\"", &value[i]);
 
     return NGX_CONF_ERROR;
 }
 
 
 ngx_http_upstream_srv_conf_t *
-ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u)
+ngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)
 {
     ngx_uint_t                      i;
+    ngx_http_upstream_server_t     *us;
     ngx_http_upstream_srv_conf_t   *uscf, **uscfp;
     ngx_http_upstream_main_conf_t  *umcf;
 
-    if (u->upstream) {
+    if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) {
+
         if (ngx_parse_url(cf, u) != NGX_OK) {
             if (u->err) {
                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
@@ -2930,17 +2836,6 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng
 
             return NULL;
         }
-
-        if (u->peers) {
-            uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t));
-            if (uscf == NULL) {
-                return NULL;
-            }
-
-            uscf->peers = u->peers;
-
-            return uscf;
-        }
     }
 
     umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
@@ -2948,15 +2843,28 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng
     uscfp = umcf->upstreams.elts;
 
     for (i = 0; i < umcf->upstreams.nelts; i++) {
-        if (uscfp[i]->host.len != u->host.len) {
+        if (uscfp[i]->host.len != u->host.len
+            || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len)
+               != 0)
+        {
             continue;
         }
 
-        if (ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len)
-            == 0)
+        if ((flags & NGX_HTTP_UPSTREAM_CREATE)
+             && (uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE))
         {
-            return uscfp[i];
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "duplicate upstream \"%V\"", &u->host);
+            return NULL;
         }
+
+        if (uscfp[i]->port == 0 && u->portn && !u->no_port) {
+            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                               "upstream \"%V\" port %d is ignored",
+                               &u->host, u->portn);
+        }
+
+        return uscfp[i];
     }
 
     uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t));
@@ -2964,10 +2872,29 @@ ngx_http_upstream_add(ngx_conf_t *cf, ng
         return NULL;
     }
 
+    uscf->flags = flags;
     uscf->host = u->host;
     uscf->file_name = cf->conf_file->file.name;
     uscf->line = cf->conf_file->line;
-    uscf->port = u->default_portn;
+    uscf->port = u->portn;
+
+    if (u->naddrs == 1) {
+        uscf->servers = ngx_array_create(cf->pool, 1,
+                                         sizeof(ngx_http_upstream_server_t));
+        if (uscf->servers == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        us = ngx_array_push(uscf->servers);
+        if (us == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        ngx_memzero(us, sizeof(ngx_http_upstream_server_t));
+
+        us->addrs = u->addrs;
+        us->naddrs = u->naddrs;
+    }
 
     uscfp = ngx_array_push(&umcf->upstreams);
     if (uscfp == NULL) {
@@ -3010,32 +2937,25 @@ ngx_http_upstream_init_main_conf(ngx_con
     ngx_array_t                     headers_in;
     ngx_hash_key_t                 *hk;
     ngx_hash_init_t                 hash;
+    ngx_http_upstream_init_pt       init;
     ngx_http_upstream_header_t     *header;
     ngx_http_upstream_srv_conf_t  **uscfp;
 
     uscfp = umcf->upstreams.elts;
 
     for (i = 0; i < umcf->upstreams.nelts; i++) {
-        if (uscfp[i]->peers) {
-            continue;
-        }
-
-        uscfp[i]->peers = ngx_inet_resolve_peer(cf, &uscfp[i]->host,
-                                                uscfp[i]->port);
-        if (uscfp[i]->peers == NULL) {
-            return NGX_CONF_ERROR;
-        }
-
-        if (uscfp[i]->peers == NGX_CONF_ERROR) {
-            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
-                          "upstream host \"%V\" is not found in %s:%ui",
-                          &uscfp[i]->host, uscfp[i]->file_name.data,
-                          uscfp[i]->line);
+
+        init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream:
+                                            ngx_http_upstream_init_round_robin;
+
+        if (init(cf, uscfp[i]) != NGX_OK) {
             return NGX_CONF_ERROR;
         }
     }
 
 
+    /* upstream_headers_in_hash */
+
     if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))
         != NGX_OK)
     {
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -44,23 +44,61 @@ typedef struct {
 typedef struct {
     ngx_hash_t                      headers_in_hash;
     ngx_array_t                     upstreams;
-                                           /* ngx_http_upstream_srv_conf_t */
+                                             /* ngx_http_upstream_srv_conf_t */
 } ngx_http_upstream_main_conf_t;
 
+typedef struct ngx_http_upstream_srv_conf_s  ngx_http_upstream_srv_conf_t;
+
+typedef ngx_int_t (*ngx_http_upstream_init_pt)(ngx_conf_t *cf,
+    ngx_http_upstream_srv_conf_t *us);
+typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r,
+    ngx_http_upstream_srv_conf_t *us);
+
+
+typedef struct {
+    ngx_http_upstream_init_pt       init_upstream;
+    ngx_http_upstream_init_peer_pt  init;
+    void                           *data;
+} ngx_http_upstream_peer_t;
+
 
 typedef struct {
-    ngx_peers_t                    *peers;
+    ngx_peer_addr_t                *addrs;
+    ngx_uint_t                      naddrs;
+    ngx_uint_t                      weight;
+    ngx_uint_t                      max_fails;
+    time_t                          fail_timeout;
+
+    unsigned                        down:1;
+    unsigned                        backup:1;
+} ngx_http_upstream_server_t;
+
 
-    ngx_array_t                    *servers;
+#define NGX_HTTP_UPSTREAM_CREATE        0x0001
+#define NGX_HTTP_UPSTREAM_WEIGHT        0x0002
+#define NGX_HTTP_UPSTREAM_MAX_FAILS     0x0004
+#define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT  0x0008
+#define NGX_HTTP_UPSTREAM_DOWN          0x0010
+#define NGX_HTTP_UPSTREAM_BACKUP        0x0020
+
 
+struct ngx_http_upstream_srv_conf_s {
+    ngx_http_upstream_peer_t        peer;
+    void                          **srv_conf;
+
+    ngx_array_t                    *servers;   /* ngx_http_upstream_server_t */
+
+    ngx_uint_t                      flags;
     ngx_str_t                       host;
     ngx_str_t                       file_name;
     ngx_uint_t                      line;
     in_port_t                       port;
-} ngx_http_upstream_srv_conf_t;
+};
 
 
 typedef struct {
+    ngx_http_upstream_srv_conf_t   *upstream;
+
     ngx_msec_t                      connect_timeout;
     ngx_msec_t                      send_timeout;
     ngx_msec_t                      read_timeout;
@@ -78,9 +116,6 @@ typedef struct {
     size_t                          temp_file_write_size_conf;
 
     ngx_uint_t                      next_upstream;
-    ngx_uint_t                      max_fails;
-
-    time_t                          fail_timeout;
 
     ngx_bufs_t                      bufs;
 
@@ -213,7 +248,11 @@ struct ngx_http_upstream_s {
 
 void ngx_http_upstream_init(ngx_http_request_t *r);
 ngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf,
-    ngx_url_t *u);
+    ngx_url_t *u, ngx_uint_t flags);
+
+
+#define ngx_http_conf_upstream_srv_conf(uscf, module)                         \
+    uscf->srv_conf[module.ctx_index]
 
 
 extern ngx_module_t  ngx_http_upstream_module;
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -0,0 +1,430 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+ngx_int_t
+ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
+    ngx_http_upstream_srv_conf_t *us)
+{
+    ngx_url_t                      u;
+    ngx_uint_t                     i, j, n;
+    ngx_http_upstream_server_t    *server;
+    ngx_http_upstream_rr_peers_t  *peers;
+
+    us->peer.init = ngx_http_upstream_init_round_robin_peer;
+
+    if (us->servers) {
+        n = 0;
+        server = us->servers->elts;
+
+        for (i = 0; i < us->servers->nelts; i++) {
+            n += server[i].naddrs;
+        }
+
+        peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
+                              + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
+        if (peers == NULL) {
+            return NGX_ERROR;
+        }
+
+        peers->number = n;
+        peers->name = &us->host;
+
+        n = 0;
+
+        for (i = 0; i < us->servers->nelts; i++) {
+            for (j = 0; j < server[i].naddrs; j++) {
+                peers->peer[n].sockaddr = server[i].addrs[j].sockaddr;
+                peers->peer[n].socklen = server[i].addrs[j].socklen;
+                peers->peer[n].name = server[i].addrs[j].name;
+                peers->peer[n].weight = server[i].weight;
+                peers->peer[n].current_weight = server[i].weight;
+                peers->peer[n].max_fails = server[i].max_fails;
+                peers->peer[n].fail_timeout = server[i].fail_timeout;
+                peers->peer[n].down = server[i].down;
+                n++;
+            }
+        }
+
+        us->peer.data = peers;
+
+        return NGX_OK;
+    }
+
+
+    /* an upstream implicitly defined by proxy_pass, etc. */
+
+    ngx_memzero(&u, sizeof(ngx_url_t));
+
+    u.host = us->host;
+    u.portn = us->port;
+
+    if (ngx_inet_resolve_host(cf, &u) != NGX_OK) {
+        if (u.err) {
+            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+                          "%s in upstream host \"%V\" is not found in %s:%ui",
+                          u.err, &us->host, us->file_name.data, us->line);
+        }
+
+        return NGX_ERROR;
+    }
+
+    n = u.naddrs;
+
+    peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t)
+                              + sizeof(ngx_http_upstream_rr_peer_t) * (n - 1));
+    if (peers == NULL) {
+        return NGX_ERROR;
+    }
+
+    peers->number = n;
+    peers->name = &us->host;
+
+    n = 0;
+
+    for (i = 0; i < u.naddrs; i++) {
+        peers->peer[n].sockaddr = u.addrs[i].sockaddr;
+        peers->peer[n].socklen = u.addrs[i].socklen;
+        peers->peer[n].name = u.addrs[i].name;
+        peers->peer[n].weight = 1;
+        peers->peer[n].current_weight = 1;
+        peers->peer[n].max_fails = 1;
+        peers->peer[n].fail_timeout = 10;
+        n++;
+    }
+
+    us->peer.data = peers;
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
+    ngx_http_upstream_srv_conf_t *us)
+{
+    ngx_uint_t                         n;
+    ngx_http_upstream_rr_peer_data_t  *rrp;
+
+    rrp = r->upstream->peer.data;
+
+    if (rrp == NULL) {
+        rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));
+        if (rrp == NULL) {
+            return NGX_ERROR;
+        }
+
+        r->upstream->peer.data = rrp;
+    }
+
+    rrp->peers = us->peer.data;
+    rrp->current = 0;
+
+    if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {
+        rrp->tried = &rrp->data;
+        rrp->data = 0;
+
+    } else {
+        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))
+                / (8 * sizeof(uintptr_t));
+
+        rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));
+        if (rrp->tried == NULL) {
+            return NGX_ERROR;
+        }
+    }
+
+    r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
+    r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
+    r->upstream->peer.tries = rrp->peers->number;
+#if (NGX_HTTP_SSL)
+    r->upstream->peer.save_session = ngx_http_upstream_save_round_robin_peer;
+#endif
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)
+{
+    ngx_http_upstream_rr_peer_data_t  *rrp = data;
+
+    time_t                        now;
+    uintptr_t                     m;
+    ngx_uint_t                    i, n;
+    ngx_connection_t             *c;
+    ngx_http_upstream_rr_peer_t  *peer;
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+                   "get rr peer, try: %ui", pc->tries);
+
+    now = ngx_time();
+
+    /* ngx_lock_mutex(rrp->peers->mutex); */
+
+    if (rrp->peers->last_cached) {
+
+        /* cached connection */
+
+        c = rrp->peers->cached[rrp->peers->last_cached];
+        rrp->peers->last_cached--;
+
+        /* ngx_unlock_mutex(ppr->peers->mutex); */
+
+#if (NGX_THREADS)
+        c->read->lock = c->read->own_lock;
+        c->write->lock = c->write->own_lock;
+#endif
+
+        pc->connection = c;
+        pc->cached = 1;
+
+        return NGX_OK;
+    }
+
+    pc->cached = 0;
+    pc->connection = NULL;
+
+    if (rrp->peers->number == 1) {
+        peer = &rrp->peers->peer[0];
+
+    } else {
+
+        /* there are several peers */
+
+        if (pc->tries == rrp->peers->number) {
+
+            /* it's a first try - get a current peer */
+
+            for ( ;; ) {
+                rrp->current = rrp->peers->current;
+
+                n = rrp->current / (8 * sizeof(uintptr_t));
+                m = 1 << rrp->current % (8 * sizeof(uintptr_t));
+
+                if (!(rrp->tried[n] & m)) {
+                    peer = &rrp->peers->peer[rrp->current];
+
+                    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 {
+                        rrp->tried[n] |= m;
+                    }
+
+                    pc->tries--;
+                }
+
+                rrp->peers->current++;
+
+                if (rrp->peers->current >= rrp->peers->number) {
+                    rrp->peers->current = 0;
+                }
+
+                if (pc->tries) {
+                    continue;
+                }
+
+                goto failed;
+            }
+
+            peer->current_weight--;
+
+            if (peer->current_weight == 0) {
+                peer->current_weight = peer->weight;
+
+                rrp->peers->current++;
+
+                if (rrp->peers->current >= rrp->peers->number) {
+                    rrp->peers->current = 0;
+                }
+            }
+
+        } else {
+            for ( ;; ) {
+                n = rrp->current / (8 * sizeof(uintptr_t));
+                m = 1 << rrp->current % (8 * sizeof(uintptr_t));
+
+                if (!(rrp->tried[n] & m)) {
+
+                    peer = &rrp->peers->peer[rrp->current];
+
+                    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 {
+                        rrp->tried[n] |= m;
+                    }
+
+                    pc->tries--;
+                }
+
+                rrp->current++;
+
+                if (rrp->current >= rrp->peers->number) {
+                    rrp->current = 0;
+                }
+
+                if (pc->tries) {
+                    continue;
+                }
+
+                goto failed;
+            }
+
+            peer->current_weight--;
+
+            if (peer->current_weight == 0) {
+                peer->current_weight = peer->weight;
+
+                if (rrp->current == rrp->peers->current) {
+                    rrp->peers->current++;
+
+                    if (rrp->peers->current >= rrp->peers->number) {
+                        rrp->peers->current = 0;
+                    }
+                }
+            }
+        }
+
+        rrp->tried[n] |= m;
+    }
+
+    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(rrp->peers->mutex); */
+
+    return NGX_OK;
+
+failed:
+
+    /* all peers failed, mark them as live for quick recovery */
+
+    for (i = 0; i < rrp->peers->number; i++) {
+        rrp->peers->peer[i].fails = 0;
+    }
+
+    /* ngx_unlock_mutex(rrp->peers->mutex); */
+
+    pc->name = rrp->peers->name;
+
+    return NGX_BUSY;
+}
+
+
+void
+ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,
+    ngx_uint_t state)
+{
+    ngx_http_upstream_rr_peer_data_t  *rrp = data;
+
+    time_t                       now;
+    ngx_http_upstream_rr_peer_t  *peer;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,
+                   "free rr peer %ui %ui", pc->tries, state);
+
+    if (state == 0 && pc->tries == 0) {
+        return;
+    }
+
+    /* TODO: NGX_PEER_KEEPALIVE */
+
+    if (rrp->peers->number == 1) {
+        pc->tries = 0;
+        return;
+    }
+
+    if (state & NGX_PEER_FAILED) {
+        now = ngx_time();
+
+        peer = &rrp->peers->peer[rrp->current];
+
+        /* ngx_lock_mutex(rrp->peers->mutex); */
+
+        peer->fails++;
+        peer->accessed = now;
+
+        if (peer->current_weight > 1) {
+            peer->current_weight /= 2;
+        }
+
+        /* ngx_unlock_mutex(rrp->peers->mutex); */
+    }
+
+    rrp->current++;
+
+    if (rrp->current >= rrp->peers->number) {
+        rrp->current = 0;
+    }
+
+    if (pc->tries) {
+        pc->tries--;
+    }
+
+    /* ngx_unlock_mutex(rrp->peers->mutex); */
+}
+
+
+#if (NGX_HTTP_SSL)
+
+void
+ngx_http_upstream_save_round_robin_peer(ngx_peer_connection_t *pc, void *data)
+{
+    ngx_http_upstream_rr_peer_data_t  *rrp = data;
+
+    ngx_ssl_session_t            *ssl_session;
+    ngx_http_upstream_rr_peer_t  *peer;
+
+    ssl_session = ngx_ssl_get_session(pc->connection);
+
+    if (ssl_session == NULL) {
+        return;
+    }
+
+    peer = &rrp->peers->peer[rrp->current];
+
+    /* ngx_lock_mutex(rrp->peers->mutex); */
+    peer->ssl_session = ssl_session;
+    /* ngx_unlock_mutex(rrp->peers->mutex); */
+
+    if (pc->ssl_session) {
+        /* TODO: may block */
+        ngx_ssl_free_session(pc->ssl_session);
+    }
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/src/http/ngx_http_upstream_round_robin.h
@@ -0,0 +1,77 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
+#define _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+typedef struct {
+    struct sockaddr                *sockaddr;
+    socklen_t                       socklen;
+    ngx_str_t                       name;
+
+    ngx_uint_t                      current_weight;
+    ngx_uint_t                      weight;
+
+    ngx_uint_t                      fails;
+    time_t                          accessed;
+
+    ngx_uint_t                      max_fails;
+    time_t                          fail_timeout;
+
+    ngx_uint_t                      down;          /* unsigned  down:1; */
+
+#if (NGX_SSL)
+    ngx_ssl_session_t              *ssl_session;
+#endif
+} ngx_http_upstream_rr_peer_t;
+
+
+typedef struct {
+    ngx_uint_t                      current;
+
+    ngx_uint_t                      number;
+    ngx_uint_t                      last_cached;
+
+ /* ngx_mutex_t                    *mutex; */
+    ngx_connection_t              **cached;
+
+    ngx_str_t                      *name;
+
+    ngx_http_upstream_rr_peer_t     peer[1];
+} ngx_http_upstream_rr_peers_t;
+
+
+typedef struct {
+    ngx_http_upstream_rr_peers_t   *peers;
+    ngx_uint_t                      current;
+    uintptr_t                      *tried; 
+    uintptr_t                       data;
+} ngx_http_upstream_rr_peer_data_t;
+
+
+ngx_int_t ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
+    ngx_http_upstream_srv_conf_t *us);
+ngx_int_t ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,
+    ngx_http_upstream_srv_conf_t *us);
+ngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,
+    void *data);
+void ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,
+    void *data, ngx_uint_t state);
+
+#if (NGX_HTTP_SSL)
+void ngx_http_upstream_save_round_robin_peer(ngx_peer_connection_t *pc,
+    void *data);
+#endif
+
+
+
+#endif /* _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */
--- a/src/imap/ngx_imap.h
+++ b/src/imap/ngx_imap.h
@@ -160,6 +160,7 @@ typedef struct {
     unsigned                no_sync_literal:1;
     unsigned                starttls:1;
     unsigned                auth_method:2;
+    unsigned                auth_wait:1;
 
     ngx_str_t               login;
     ngx_str_t               passwd;
@@ -265,7 +266,7 @@ ngx_int_t ngx_pop3_parse_command(ngx_ima
 
 
 /* STUB */
-void ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers);
+void ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peer_addr_t *peer);
 void ngx_imap_auth_http_init(ngx_imap_session_t *s);
 /**/
 
--- a/src/imap/ngx_imap_auth_http_module.c
+++ b/src/imap/ngx_imap_auth_http_module.c
@@ -12,7 +12,7 @@
 
 
 typedef struct {
-    ngx_peers_t                    *peers;
+    ngx_peer_addr_t                *peer;
 
     ngx_msec_t                      timeout;
 
@@ -175,7 +175,10 @@ ngx_imap_auth_http_init(ngx_imap_session
 
     ngx_imap_set_ctx(s, ctx, ngx_imap_auth_http_module);
 
-    ctx->peer.peers = ahcf->peers;
+    ctx->peer.sockaddr = ahcf->peer->sockaddr;
+    ctx->peer.socklen = ahcf->peer->socklen;
+    ctx->peer.name = &ahcf->peer->name;
+    ctx->peer.get = ngx_event_get_peer;
     ctx->peer.log = s->connection->log;
     ctx->peer.log_error = NGX_ERROR_ERR;
 
@@ -229,8 +232,7 @@ ngx_imap_auth_http_write_handler(ngx_eve
 
     if (wev->timedout) {
         ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
-                      "auth http server %V timed out",
-                      &ctx->peer.peers->peer[0].name);
+                      "auth http server %V timed out", ctx->peer.name);
         ngx_close_connection(ctx->peer.connection);
         ngx_destroy_pool(ctx->pool);
         ngx_imap_session_internal_server_error(s);
@@ -293,8 +295,7 @@ ngx_imap_auth_http_read_handler(ngx_even
 
     if (rev->timedout) {
         ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
-                      "auth http server %V timed out",
-                      &ctx->peer.peers->peer[0].name);
+                      "auth http server %V timed out", ctx->peer.name);
         ngx_close_connection(ctx->peer.connection);
         ngx_destroy_pool(ctx->pool);
         ngx_imap_session_internal_server_error(s);
@@ -413,7 +414,7 @@ ngx_imap_auth_http_ignore_status_line(ng
 
             ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                           "auth http server &V sent invalid response",
-                          &ctx->peer.peers->peer[0].name);
+                          ctx->peer.name);
             ngx_close_connection(ctx->peer.connection);
             ngx_destroy_pool(ctx->pool);
             ngx_imap_session_internal_server_error(s);
@@ -447,7 +448,7 @@ ngx_imap_auth_http_process_headers(ngx_i
     time_t               timer;
     size_t               len, size;
     ngx_int_t            rc, port, n;
-    ngx_peers_t         *peers;
+    ngx_peer_addr_t     *peer;
     struct sockaddr_in  *sin;
 
     ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
@@ -488,6 +489,16 @@ ngx_imap_auth_http_process_headers(ngx_i
                     continue;
                 }
 
+                if (len == 4
+                    && ctx->header_start[0] == 'W'
+                    && ctx->header_start[1] == 'A'
+                    && ctx->header_start[2] == 'I'
+                    && ctx->header_start[3] == 'T')
+                {
+                    s->auth_wait = 1;
+                    continue;
+                }
+
                 ctx->errmsg.len = len;
                 ctx->errmsg.data = ctx->header_start;
 
@@ -632,10 +643,27 @@ ngx_imap_auth_http_process_headers(ngx_i
                 return;
             }
 
+            if (s->auth_wait) {
+                timer = ctx->sleep;
+
+                ngx_destroy_pool(ctx->pool);
+
+                if (timer == 0) {
+                    ngx_imap_auth_http_init(s);
+                    return;
+                }
+
+                ngx_add_timer(s->connection->read, timer * 1000);
+
+                s->connection->read->handler = ngx_imap_auth_sleep_handler;
+
+                return;
+            }
+
             if (ctx->addr.len == 0 || ctx->port.len == 0) {
                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                               "auth http server %V did not send server or port",
-                              &ctx->peer.peers->peer[0].name);
+                              ctx->peer.name);
                 ngx_destroy_pool(ctx->pool);
                 ngx_imap_session_internal_server_error(s);
                 return;
@@ -644,14 +672,14 @@ ngx_imap_auth_http_process_headers(ngx_i
             if (s->passwd.data == NULL) {
                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                               "auth http server %V did not send password",
-                              &ctx->peer.peers->peer[0].name);
+                              ctx->peer.name);
                 ngx_destroy_pool(ctx->pool);
                 ngx_imap_session_internal_server_error(s);
                 return;
             }
 
-            peers = ngx_pcalloc(s->connection->pool, sizeof(ngx_peers_t));
-            if (peers == NULL) {
+            peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_peer_addr_t));
+            if (peer == NULL) {
                 ngx_destroy_pool(ctx->pool);
                 ngx_imap_session_internal_server_error(s);
                 return;
@@ -671,7 +699,7 @@ ngx_imap_auth_http_process_headers(ngx_i
                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                               "auth http server %V sent invalid server "
                               "port:\"%V\"",
-                              &ctx->peer.peers->peer[0].name, &ctx->port);
+                              ctx->peer.name, &ctx->port);
                 ngx_destroy_pool(ctx->pool);
                 ngx_imap_session_internal_server_error(s);
                 return;
@@ -685,23 +713,21 @@ ngx_imap_auth_http_process_headers(ngx_i
                 ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                               "auth http server %V sent invalid server "
                               "address:\"%V\"",
-                              &ctx->peer.peers->peer[0].name, &ctx->addr);
+                              ctx->peer.name, &ctx->addr);
                 ngx_destroy_pool(ctx->pool);
                 ngx_imap_session_internal_server_error(s);
                 return;
             }
 
-            peers->number = 1;
-
-            peers->peer[0].sockaddr = (struct sockaddr *) sin;
-            peers->peer[0].socklen = sizeof(struct sockaddr_in);
+            peer->sockaddr = (struct sockaddr *) sin;
+            peer->socklen = sizeof(struct sockaddr_in);
 
             len = ctx->addr.len + 1 + ctx->port.len;
 
-            peers->peer[0].name.len = len;
+            peer->name.len = len;
 
-            peers->peer[0].name.data = ngx_palloc(s->connection->pool, len);
-            if (peers->peer[0].name.data == NULL) {
+            peer->name.data = ngx_palloc(s->connection->pool, len);
+            if (peer->name.data == NULL) {
                 ngx_destroy_pool(ctx->pool);
                 ngx_imap_session_internal_server_error(s);
                 return;
@@ -709,17 +735,14 @@ ngx_imap_auth_http_process_headers(ngx_i
 
             len = ctx->addr.len;
 
-            ngx_memcpy(peers->peer[0].name.data, ctx->addr.data, len);
-
-            peers->peer[0].name.data[len++] = ':';
+            ngx_memcpy(peer->name.data, ctx->addr.data, len);
 
-            ngx_memcpy(peers->peer[0].name.data + len,
-                       ctx->port.data, ctx->port.len);
+            peer->name.data[len++] = ':';
 
-            peers->peer[0].uri_separator = "";
+            ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);
 
             ngx_destroy_pool(ctx->pool);
-            ngx_imap_proxy_init(s, peers);
+            ngx_imap_proxy_init(s, peer);
 
             return;
         }
@@ -732,7 +755,7 @@ ngx_imap_auth_http_process_headers(ngx_i
 
         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                       "auth http server %V sent invalid header in response",
-                      &ctx->peer.peers->peer[0].name);
+                      ctx->peer.name);
         ngx_close_connection(ctx->peer.connection);
         ngx_destroy_pool(ctx->pool);
         ngx_imap_session_internal_server_error(s);
@@ -758,6 +781,12 @@ ngx_imap_auth_sleep_handler(ngx_event_t 
 
         rev->timedout = 0;
 
+        if (s->auth_wait) {
+            s->auth_wait = 0;
+            ngx_imap_auth_http_init(s);
+            return;
+        }
+
         if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
             s->imap_state = ngx_pop3_start;
             s->connection->read->handler = ngx_pop3_auth_state;
@@ -1209,8 +1238,8 @@ ngx_imap_auth_http_merge_conf(ngx_conf_t
     ngx_uint_t        i;
     ngx_table_elt_t  *header;
 
-    if (conf->peers == NULL) {
-        conf->peers = prev->peers;
+    if (conf->peer == NULL) {
+        conf->peer = prev->peer;
         conf->host_header = prev->host_header;
         conf->uri = prev->uri;
     }
@@ -1264,6 +1293,7 @@ ngx_imap_auth_http(ngx_conf_t *cf, ngx_c
     u.url = value[1];
     u.default_portn = 80;
     u.uri_part = 1;
+    u.one_addr = 1;
 
     if (ngx_parse_url(cf, &u) != NGX_OK) {
         if (u.err) {
@@ -1272,8 +1302,7 @@ ngx_imap_auth_http(ngx_conf_t *cf, ngx_c
         }
     }
 
-    ahcf->peers = u.peers;
-    ahcf->peers->number = 1;
+    ahcf->peer = u.addrs;
 
     ahcf->host_header = u.host_header;
     ahcf->uri = u.uri;
--- a/src/imap/ngx_imap_handler.c
+++ b/src/imap/ngx_imap_handler.c
@@ -1279,8 +1279,7 @@ ngx_imap_log_error(ngx_log_t *log, u_cha
         return p;
     }
 
-    p = ngx_snprintf(buf, len, ", upstream: %V",
-                     &s->proxy->upstream.peers->peer[0].name);
+    p = ngx_snprintf(buf, len, ", upstream: %V", s->proxy->upstream.name);
 
     return p;
 }
--- a/src/imap/ngx_imap_proxy_module.c
+++ b/src/imap/ngx_imap_proxy_module.c
@@ -94,7 +94,7 @@ ngx_module_t  ngx_imap_proxy_module = {
 
 
 void
-ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers)
+ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peer_addr_t *peer)
 {
     int                        keepalive;
     ngx_int_t                  rc;
@@ -125,7 +125,10 @@ ngx_imap_proxy_init(ngx_imap_session_t *
 
     s->proxy = p;
 
-    p->upstream.peers = peers;
+    p->upstream.sockaddr = peer->sockaddr;
+    p->upstream.socklen = peer->socklen;
+    p->upstream.name = &peer->name;
+    p->upstream.get = ngx_event_get_peer;
     p->upstream.log = s->connection->log;
     p->upstream.log_error = NGX_ERROR_ERR;