changeset 510:24b676623d4f NGINX_0_8_7

nginx 0.8.7 *) Change: minimum supported OpenSSL version is 0.9.7. *) Change: the "ask" parameter of the "ssl_verify_client" directive was changed to the "optional" parameter and now it checks a client certificate if it was offered. Thanks to Brice Figureau. *) Feature: the $ssl_client_verify variable. Thanks to Brice Figureau. *) Feature: the "ssl_crl" directive. Thanks to Brice Figureau. *) Feature: the "proxy" parameter of the "geo" directive. *) Feature: the "image_filter" directive supports variables for setting size. *) Bugfix: the $ssl_client_cert variable usage corrupted memory; the bug had appeared in 0.7.7. Thanks to Sergey Zhuravlev. *) Bugfix: "proxy_pass_header" and "fastcgi_pass_header" directives did not pass to a client the "X-Accel-Redirect", "X-Accel-Limit-Rate", "X-Accel-Buffering", and "X-Accel-Charset" lines from backend response header. Thanks to Maxim Dounin. *) Bugfix: in handling "Last-Modified" and "Accept-Ranges" backend response header lines; the bug had appeared in 0.7.44. Thanks to Maxim Dounin. *) Bugfix: the "[alert] zero size buf" error if subrequest returns an empty response; the bug had appeared in 0.8.5.
author Igor Sysoev <http://sysoev.ru>
date Mon, 27 Jul 2009 00:00:00 +0400
parents 41f4e459ace8
children e8f9a06185f3
files CHANGES CHANGES.ru auto/options src/core/nginx.h src/event/ngx_event_openssl.c src/event/ngx_event_openssl.h src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_geo_module.c src/http/modules/ngx_http_image_filter_module.c src/http/modules/ngx_http_memcached_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_rewrite_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/modules/ngx_http_ssl_module.c src/http/modules/ngx_http_ssl_module.h src/http/modules/ngx_http_xslt_filter_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_parse.c src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h src/mail/ngx_mail_ssl_module.c
diffstat 23 files changed, 526 insertions(+), 310 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,42 @@
 
+Changes with nginx 0.8.7                                         27 Jul 2009
+
+    *) Change: minimum supported OpenSSL version is 0.9.7.
+
+    *) Change: the "ask" parameter of the "ssl_verify_client" directive was 
+       changed to the "optional" parameter and now it checks a client 
+       certificate if it was offered.
+       Thanks to Brice Figureau.
+
+    *) Feature: the $ssl_client_verify variable.
+       Thanks to Brice Figureau.
+
+    *) Feature: the "ssl_crl" directive.
+       Thanks to Brice Figureau.
+
+    *) Feature: the "proxy" parameter of the "geo" directive.
+
+    *) Feature: the "image_filter" directive supports variables for setting 
+       size.
+
+    *) Bugfix: the $ssl_client_cert variable usage corrupted memory; the 
+       bug had appeared in 0.7.7.
+       Thanks to Sergey Zhuravlev.
+
+    *) Bugfix: "proxy_pass_header" and "fastcgi_pass_header" directives did 
+       not pass to a client the "X-Accel-Redirect", "X-Accel-Limit-Rate", 
+       "X-Accel-Buffering", and "X-Accel-Charset" lines from backend 
+       response header.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: in handling "Last-Modified" and "Accept-Ranges" backend 
+       response header lines; the bug had appeared in 0.7.44.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: the "[alert] zero size buf" error if subrequest returns an 
+       empty response; the bug had appeared in 0.8.5.
+
+
 Changes with nginx 0.8.6                                         20 Jul 2009
 
     *) Feature: the ngx_http_geoip_module.
@@ -8,7 +46,7 @@ Changes with nginx 0.8.6                
        Thanks to Kuramoto Eiji.
 
     *) Bugfix: now in MacOSX, Cygwin, and nginx/Windows locations given by 
-       a regular expression are always tested in case insensitive mode;
+       a regular expression are always tested in case insensitive mode.
 
     *) Bugfix: now nginx/Windows ignores trailing dots in URI.
        Thanks to Hugo Leisink.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,42 @@
 
+Изменения в nginx 0.8.7                                           27.07.2009
+
+    *) Изменение: минимальная поддерживаемая версия OpenSSL - 0.9.7.
+
+    *) Изменение: параметр ask директивы ssl_verify_client изменён на 
+       параметр optional и теперь он проверяет клиентский сертификат, если 
+       он был предложен.
+       Спасибо Brice Figureau.
+
+    *) Добавление: переменная $ssl_client_verify.
+       Спасибо Brice Figureau.
+
+    *) Добавление: директива ssl_crl.
+       Спасибо Brice Figureau.
+
+    *) Добавление: параметр proxy директивы geo.
+
+    *) Добавление: директива image_filter поддерживает переменные для 
+       задания размеров.
+
+    *) Исправление: использование переменной $ssl_client_cert портило 
+       память; ошибка появилась в 0.7.7.
+       Спасибо Сергею Журавлёву.
+
+    *) Исправление: директивы proxy_pass_header и fastcgi_pass_header" не 
+       передавали клиенту строки "X-Accel-Redirect", "X-Accel-Limit-Rate", 
+       "X-Accel-Buffering" и "X-Accel-Charset" из заголовка ответа 
+       бэкенда.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: в обработке строк "Last-Modified" и "Accept-Ranges" в 
+       заголовке ответа бэкенда; ошибка появилась в 0.7.44
+       Спасибо Максиму Дунину.
+
+    *) Исправление: ошибки "[alert] zero size buf" при получении пустых 
+       ответы в подзапросах; ошибка появилась в 0.8.5.
+
+
 Изменения в nginx 0.8.6                                           20.07.2009
 
     *) Добавление: модуль ngx_http_geoip_module.
--- a/auto/options
+++ b/auto/options
@@ -312,7 +312,7 @@ cat << END
   --with-http_addition_module        enable ngx_http_addition_module
   --with-http_xslt_module            enable ngx_http_xslt_module
   --with-http_image_filter_module    enable ngx_http_image_filter_module
-  --with-http_geoip_module           enable ngx_http_geoip_link_module
+  --with-http_geoip_module           enable ngx_http_geoip_module
   --with-http_sub_module             enable ngx_http_sub_module
   --with-http_dav_module             enable ngx_http_dav_module
   --with-http_flv_module             enable ngx_http_flv_module
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version         8006
-#define NGINX_VERSION      "0.8.6"
+#define nginx_version         8007
+#define NGINX_VERSION      "0.8.7"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -97,16 +97,12 @@ int  ngx_ssl_session_cache_index;
 ngx_int_t
 ngx_ssl_init(ngx_log_t *log)
 {
-#if OPENSSL_VERSION_NUMBER >= 0x00907000
     OPENSSL_config(NULL);
-#endif
 
     SSL_library_init();
     SSL_load_error_strings();
 
-#if (NGX_SSL_ENGINE)
     ENGINE_load_builtin_engines();
-#endif
 
     ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
 
@@ -169,9 +165,7 @@ ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_
     SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG);
     SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG);
 
-#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
     SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
-#endif
 
     SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);
 
@@ -267,6 +261,51 @@ ngx_ssl_client_certificate(ngx_conf_t *c
 }
 
 
+ngx_int_t
+ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
+{
+    X509_STORE   *store;
+    X509_LOOKUP  *lookup;
+
+    if (crl->len == 0) {
+        return NGX_OK;
+    }
+
+    if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    store = SSL_CTX_get_cert_store(ssl->ctx);
+
+    if (store == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "SSL_CTX_get_cert_store() failed");
+        return NGX_ERROR;
+    }
+
+    lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
+
+    if (lookup == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "X509_STORE_add_lookup() failed");
+        return NGX_ERROR;
+    }
+
+    if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM)
+        == 0)
+    {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "X509_LOOKUP_load_file(\"%s\") failed", crl->data);
+        return NGX_ERROR;
+    }
+
+    X509_STORE_set_flags(store,
+                         X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
+
+    return NGX_OK;
+}
+
+
 static int
 ngx_http_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)
 {
@@ -1201,9 +1240,7 @@ ngx_ssl_connection_error(ngx_connection_
         if (err == NGX_ECONNRESET
             || err == NGX_EPIPE
             || err == NGX_ENOTCONN
-#if !(NGX_CRIT_ETIMEDOUT)
             || err == NGX_ETIMEDOUT
-#endif
             || err == NGX_ECONNREFUSED
             || err == NGX_ENETDOWN
             || err == NGX_ENETUNREACH
@@ -1974,7 +2011,7 @@ ngx_ssl_get_certificate(ngx_connection_t
 
     p = s->data;
 
-    for (i = 0; i < len; i++) {
+    for (i = 0; i < cert.len - 1; i++) {
         *p++ = cert.data[i];
         if (cert.data[i] == LF) {
             *p++ = '\t';
@@ -2108,6 +2145,35 @@ ngx_ssl_get_serial_number(ngx_connection
 }
 
 
+ngx_int_t
+ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
+{
+    X509  *cert;
+
+    if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {
+        s->len = sizeof("FAILED") - 1;
+        s->data = (u_char *) "FAILED";
+
+        return NGX_OK;
+    }
+
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+
+    if (cert) {
+        s->len = sizeof("SUCCESS") - 1;
+        s->data = (u_char *) "SUCCESS";
+
+    } else {
+        s->len = sizeof("NONE") - 1;
+        s->data = (u_char *) "NONE";
+    }
+
+    X509_free(cert);
+
+    return NGX_OK;
+}
+
+
 static void *
 ngx_openssl_create_conf(ngx_cycle_t *cycle)
 {
@@ -2131,7 +2197,6 @@ ngx_openssl_create_conf(ngx_cycle_t *cyc
 static char *
 ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-#if (NGX_SSL_ENGINE)
     ngx_openssl_conf_t *oscf = conf;
 
     ENGINE     *engine;
@@ -2166,23 +2231,11 @@ ngx_openssl_engine(ngx_conf_t *cf, ngx_c
     ENGINE_free(engine);
 
     return NGX_CONF_OK;
-
-#else
-
-    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                       "\"ssl_engine\" directive is available only in "
-                       "OpenSSL 0.9.7 and higher,");
-
-    return NGX_CONF_ERROR;
-
-#endif
 }
 
 
 static void
 ngx_openssl_exit(ngx_cycle_t *cycle)
 {
-#if (NGX_SSL_ENGINE)
     ENGINE_cleanup();
-#endif
 }
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -13,12 +13,8 @@
 
 #include <openssl/ssl.h>
 #include <openssl/err.h>
-
-#if OPENSSL_VERSION_NUMBER >= 0x00907000
 #include <openssl/conf.h>
 #include <openssl/engine.h>
-#define NGX_SSL_ENGINE   1
-#endif
 
 #define NGX_SSL_NAME     "OpenSSL"
 
@@ -100,6 +96,7 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t
     ngx_str_t *cert, ngx_str_t *key);
 ngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,
     ngx_str_t *cert, ngx_int_t depth);
+ngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl);
 ngx_int_t ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl);
 ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
 ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
@@ -131,6 +128,8 @@ ngx_int_t ngx_ssl_get_issuer_dn(ngx_conn
     ngx_str_t *s);
 ngx_int_t ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool,
     ngx_str_t *s);
+ngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool,
+    ngx_str_t *s);
 
 
 ngx_int_t ngx_ssl_handshake(ngx_connection_t *c);
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -543,20 +543,17 @@ ngx_http_fastcgi_handler(ngx_http_reques
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
+    if (ngx_http_upstream_create(r) != NGX_OK) {
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));
     if (f == NULL) {
-        return NGX_ERROR;
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
     ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);
 
-    u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
-    if (u == NULL) {
-        return NGX_HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    r->upstream = u;
-
     flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
 
     if (flcf->fastcgi_lengths) {
@@ -565,15 +562,11 @@ ngx_http_fastcgi_handler(ngx_http_reques
         }
     }
 
+    u = r->upstream;
+
     u->schema.len = sizeof("fastcgi://") - 1;
     u->schema.data = (u_char *) "fastcgi://";
 
-    u->peer.log = r->connection->log;
-    u->peer.log_error = NGX_ERROR_ERR;
-#if (NGX_THREADS)
-    u->peer.lock = &r->connection->lock;
-#endif
-
     u->output.tag = (ngx_buf_tag_t) &ngx_http_fastcgi_module;
 
     u->conf = &flcf->upstream;
@@ -2438,8 +2431,13 @@ ngx_http_fastcgi_pass(ngx_conf_t *cf, ng
     }
 
     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
+
     clcf->handler = ngx_http_fastcgi_handler;
 
+    if (clcf->name.data[clcf->name.len - 1] == '/') {
+        clcf->auto_redirect = 1;
+    }
+
     value = cf->args->elts;
 
     url = &value[1];
@@ -2475,10 +2473,6 @@ ngx_http_fastcgi_pass(ngx_conf_t *cf, ng
         return NGX_CONF_ERROR;
     }
 
-    if (clcf->name.data[clcf->name.len - 1] == '/') {
-        clcf->auto_redirect = 1;
-    }
-
     return NGX_CONF_OK;
 }
 
--- a/src/http/modules/ngx_http_geo_module.c
+++ b/src/http/modules/ngx_http_geo_module.c
@@ -35,6 +35,7 @@ typedef struct {
     ngx_radix_tree_t                *tree;
     ngx_rbtree_t                     rbtree;
     ngx_rbtree_node_t                sentinel;
+    ngx_array_t                     *proxies;
     ngx_pool_t                      *pool;
     ngx_pool_t                      *temp_pool;
 } ngx_http_geo_conf_ctx_t;
@@ -46,12 +47,16 @@ typedef struct {
         ngx_http_geo_high_ranges_t  *high;
     } u;
 
+    ngx_array_t                     *proxies;
+
     ngx_int_t                        index;
 } ngx_http_geo_ctx_t;
 
 
 static in_addr_t ngx_http_geo_addr(ngx_http_request_t *r,
     ngx_http_geo_ctx_t *ctx);
+static in_addr_t ngx_http_geo_real_addr(ngx_http_request_t *r,
+    ngx_http_geo_ctx_t *ctx);
 static char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 static char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);
 static char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
@@ -64,6 +69,10 @@ static char *ngx_http_geo_cidr(ngx_conf_
     ngx_str_t *value);
 static ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,
     ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);
+static char *ngx_http_geo_add_proxy(ngx_conf_t *cf,
+    ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);
+static ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,
+    ngx_cidr_t *cidr);
 
 
 static ngx_command_t  ngx_http_geo_commands[] = {
@@ -168,6 +177,50 @@ ngx_http_geo_range_variable(ngx_http_req
 static in_addr_t
 ngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx)
 {
+    u_char           *p, *ip;
+    size_t            len;
+    in_addr_t         addr;
+    ngx_uint_t        i, n;
+    ngx_in_cidr_t    *proxies;
+    ngx_table_elt_t  *xfwd;
+
+    addr = ngx_http_geo_real_addr(r, ctx);
+
+    xfwd = r->headers_in.x_forwarded_for;
+
+    if (xfwd == NULL || ctx->proxies == NULL) {
+        return addr;
+    }
+
+    proxies = ctx->proxies->elts;
+    n = ctx->proxies->nelts;
+
+    for (i = 0; i < n; i++) {
+        if ((addr & proxies[i].mask) == proxies[i].addr) {
+
+            len = xfwd->value.len;
+            ip = xfwd->value.data;
+
+            for (p = ip + len - 1; p > ip; p--) {
+                if (*p == ' ' || *p == ',') {
+                    p++;
+                    len -= p - ip;
+                    ip = p;
+                    break;
+                }
+            }
+
+            return ntohl(ngx_inet_addr(ip, len));
+        }
+    }
+
+    return addr;
+}
+
+
+static in_addr_t
+ngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx)
+{
     struct sockaddr_in         *sin;
     ngx_http_variable_value_t  *v;
 
@@ -259,6 +312,7 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
 
     ctx.high = NULL;
     ctx.tree = NULL;
+    ctx.proxies = NULL;
     ctx.pool = cf->pool;
 
     save = *cf;
@@ -271,6 +325,8 @@ ngx_http_geo_block(ngx_conf_t *cf, ngx_c
 
     *cf = save;
 
+    geo->proxies = ctx.proxies;
+
     if (ctx.high) {
 
         for (i = 0; i < 0x10000; i++) {
@@ -341,6 +397,7 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
 {
     char                     *rv;
     ngx_str_t                *value, file;
+    ngx_cidr_t                cidr;
     ngx_http_geo_conf_ctx_t  *ctx;
 
     ctx = cf->ctx;
@@ -394,6 +451,16 @@ ngx_http_geo(ngx_conf_t *cf, ngx_command
         rv = ngx_conf_parse(cf, &file);
 
         goto done;
+
+    } else if (ngx_strcmp(value[0].data, "proxy") == 0) {
+
+        if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {
+            goto failed;
+        }
+
+        rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);
+
+        goto done;
     }
 
     if (ctx->high) {
@@ -803,33 +870,8 @@ ngx_http_geo_cidr(ngx_conf_t *cf, ngx_ht
             del = 0;
         }
 
-        if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
-            cidr.u.in.addr = 0xffffffff;
-            cidr.u.in.mask = 0xffffffff;
-
-        } else {
-            rc = ngx_ptocidr(net, &cidr);
-
-            if (rc == NGX_ERROR) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                                   "invalid network \"%V\"", net);
-                return NGX_CONF_ERROR;
-            }
-
-            if (cidr.family != AF_INET) {
-                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                                   "\"geo\" supports IPv4 only");
-                return NGX_CONF_ERROR;
-            }
-
-            if (rc == NGX_DONE) {
-                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
-                                   "low address bits of %V are meaningless",
-                                   net);
-            }
-
-            cidr.u.in.addr = ntohl(cidr.u.in.addr);
-            cidr.u.in.mask = ntohl(cidr.u.in.mask);
+        if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {
+            return NGX_CONF_ERROR;
         }
 
         if (del) {
@@ -927,3 +969,64 @@ ngx_http_geo_value(ngx_conf_t *cf, ngx_h
 
     return val;
 }
+
+
+static char *
+ngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,
+    ngx_cidr_t *cidr)
+{
+    ngx_in_cidr_t  *c;
+
+    if (ctx->proxies == NULL) {
+        ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_in_cidr_t));
+        if (ctx->proxies == NULL) {
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    c = ngx_array_push(ctx->proxies);
+    if (c == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    c->addr = cidr->u.in.addr;
+    c->mask = cidr->u.in.mask;
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)
+{
+    ngx_int_t  rc;
+
+    if (ngx_strcmp(net->data, "255.255.255.255") == 0) {
+        cidr->u.in.addr = 0xffffffff;
+        cidr->u.in.mask = 0xffffffff;
+
+        return NGX_OK;
+    }
+
+    rc = ngx_ptocidr(net, cidr);
+
+    if (rc == NGX_ERROR) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid network \"%V\"", net);
+        return NGX_ERROR;
+    }
+
+    if (cidr->family != AF_INET) {
+        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"geo\" supports IPv4 only");
+        return NGX_ERROR;
+    }
+
+    if (rc == NGX_DONE) {
+        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+                           "low address bits of %V are meaningless", net);
+    }
+
+    cidr->u.in.addr = ntohl(cidr->u.in.addr);
+    cidr->u.in.mask = ntohl(cidr->u.in.mask);
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_image_filter_module.c
+++ b/src/http/modules/ngx_http_image_filter_module.c
@@ -40,6 +40,9 @@ typedef struct {
     ngx_uint_t                   height;
     ngx_int_t                    jpeg_quality;
 
+    ngx_http_complex_value_t    *wcv;
+    ngx_http_complex_value_t    *hcv;
+
     size_t                       buffer_size;
 } ngx_http_image_filter_conf_t;
 
@@ -53,6 +56,9 @@ typedef struct {
     ngx_uint_t                   width;
     ngx_uint_t                   height;
 
+    ngx_uint_t                   max_width;
+    ngx_uint_t                   max_height;
+
     ngx_uint_t                   phase;
     ngx_uint_t                   type;
 } ngx_http_image_filter_ctx_t;
@@ -80,6 +86,9 @@ static gdImagePtr ngx_http_image_new(ngx
 static u_char *ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type,
     gdImagePtr img, int *size);
 static void ngx_http_image_cleanup(void *data);
+static ngx_uint_t ngx_http_image_filter_get_value(ngx_http_request_t *r,
+    ngx_http_complex_value_t *cv, ngx_uint_t v);
+static ngx_uint_t ngx_http_image_filter_value(ngx_str_t *value);
 
 
 static void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);
@@ -454,7 +463,6 @@ ngx_http_image_read(ngx_http_request_t *
 static ngx_buf_t *
 ngx_http_image_process(ngx_http_request_t *r)
 {
-    ngx_buf_t                     *b;
     ngx_int_t                      rc;
     ngx_http_image_filter_ctx_t   *ctx;
     ngx_http_image_filter_conf_t  *conf;
@@ -468,20 +476,28 @@ ngx_http_image_process(ngx_http_request_
     conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
 
     if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
-
-        b = ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);
+        return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);
+    }
 
-    } else if (rc == NGX_OK
-               && ctx->width <= conf->width
-               && ctx->height <= conf->height)
-    {
-        b = ngx_http_image_asis(r, ctx);
-
-    } else {
-        b = ngx_http_image_resize(r, ctx);
+    ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width);
+    if (ctx->max_width == 0) {
+        return NULL;
     }
 
-    return b;
+    ctx->max_height = ngx_http_image_filter_get_value(r, conf->hcv,
+                                                      conf->height);
+    if (ctx->max_height == 0) {
+        return NULL;
+    }
+
+    if (rc == NGX_OK
+        && ctx->width <= ctx->max_width
+        && ctx->height <= ctx->max_height)
+    {
+        return ngx_http_image_asis(r, ctx);
+    }
+
+    return ngx_http_image_resize(r, ctx);
 }
 
 
@@ -682,7 +698,9 @@ ngx_http_image_resize(ngx_http_request_t
 
     conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
 
-    if ((ngx_uint_t) sx <= conf->width && (ngx_uint_t) sy <= conf->height) {
+    if ((ngx_uint_t) sx <= ctx->max_width
+        && (ngx_uint_t) sy <= ctx->max_height)
+    {
         gdImageDestroy(src);
         return ngx_http_image_asis(r, ctx);
     }
@@ -695,16 +713,16 @@ ngx_http_image_resize(ngx_http_request_t
 
     if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {
 
-        if ((ngx_uint_t) dx > conf->width) {
-            dy = dy * conf->width / dx;
+        if ((ngx_uint_t) dx > ctx->max_width) {
+            dy = dy * ctx->max_width / dx;
             dy = dy ? dy : 1;
-            dx = conf->width;
+            dx = ctx->max_width;
         }
 
-        if ((ngx_uint_t) dy > conf->height) {
-            dx = dx * conf->height / dy;
+        if ((ngx_uint_t) dy > ctx->max_height) {
+            dx = dx * ctx->max_height / dy;
             dx = dx ? dx : 1;
-            dy = conf->height;
+            dy = ctx->max_height;
         }
 
         resize = 1;
@@ -713,20 +731,21 @@ ngx_http_image_resize(ngx_http_request_t
 
         resize = 0;
 
-        if ((ngx_uint_t) (dx * 100 / dy) < conf->width * 100 / conf->height) {
-
-            if ((ngx_uint_t) dx > conf->width) {
-                dy = dy * conf->width / dx;
+        if ((ngx_uint_t) (dx * 100 / dy)
+            < ctx->max_width * 100 / ctx->max_height)
+        {
+            if ((ngx_uint_t) dx > ctx->max_width) {
+                dy = dy * ctx->max_width / dx;
                 dy = dy ? dy : 1;
-                dx = conf->width;
+                dx = ctx->max_width;
                 resize = 1;
             }
 
         } else {
-            if ((ngx_uint_t) dy > conf->height) {
-                dx = dx * conf->height / dy;
+            if ((ngx_uint_t) dy > ctx->max_height) {
+                dx = dx * ctx->max_height / dy;
                 dx = dx ? dx : 1;
-                dy = conf->height;
+                dy = ctx->max_height;
                 resize = 1;
             }
         }
@@ -751,15 +770,15 @@ ngx_http_image_resize(ngx_http_request_t
 
         src = dst;
 
-        if ((ngx_uint_t) dx > conf->width) {
-            ox = dx - conf->width;
+        if ((ngx_uint_t) dx > ctx->max_width) {
+            ox = dx - ctx->max_width;
 
         } else {
             ox = 0;
         }
 
-        if ((ngx_uint_t) dy > conf->height) {
-            oy = dy - conf->height;
+        if ((ngx_uint_t) dy > ctx->max_height) {
+            oy = dy - ctx->max_height;
 
         } else {
             oy = 0;
@@ -941,6 +960,43 @@ ngx_http_image_cleanup(void *data)
 }
 
 
+static ngx_uint_t
+ngx_http_image_filter_get_value(ngx_http_request_t *r,
+    ngx_http_complex_value_t *cv, ngx_uint_t v)
+{
+    ngx_str_t  val;
+
+    if (cv == NULL) {
+        return v;
+    }
+
+    if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {
+        return 0;
+    }
+
+    return ngx_http_image_filter_value(&val);
+}
+
+
+static ngx_uint_t
+ngx_http_image_filter_value(ngx_str_t *value)
+{
+    ngx_int_t  n;
+
+    if (value->len == 1 && value->data[0] == '-') {
+        return (ngx_uint_t) -1;
+    }
+
+    n = ngx_atoi(value->data, value->len);
+
+    if (n > 0) {
+        return (ngx_uint_t) n;
+    }
+
+    return 0;
+}
+
+
 static void *
 ngx_http_image_filter_create_conf(ngx_conf_t *cf)
 {
@@ -974,6 +1030,8 @@ ngx_http_image_filter_merge_conf(ngx_con
             conf->filter = prev->filter;
             conf->width = prev->width;
             conf->height = prev->height;
+            conf->wcv = prev->wcv;
+            conf->hcv = prev->hcv;
         }
     }
 
@@ -992,9 +1050,11 @@ ngx_http_image_filter(ngx_conf_t *cf, ng
 {
     ngx_http_image_filter_conf_t *imcf = conf;
 
-    ngx_str_t   *value;
-    ngx_int_t    n;
-    ngx_uint_t   i;
+    ngx_str_t                         *value;
+    ngx_int_t                          n;
+    ngx_uint_t                         i;
+    ngx_http_complex_value_t           cv;
+    ngx_http_compile_complex_value_t   ccv;
 
     value = cf->args->elts;
 
@@ -1027,32 +1087,60 @@ ngx_http_image_filter(ngx_conf_t *cf, ng
         goto failed;
     }
 
-    i++;
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[++i];
+    ccv.complex_value = &cv;
 
-    if (value[i].len == 1 && value[i].data[0] == '-') {
-        imcf->width = (ngx_uint_t) -1;
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
 
-    } else {
-        n = ngx_atoi(value[i].data, value[i].len);
-        if (n == NGX_ERROR) {
+    if (cv.lengths == NULL) {
+        n = ngx_http_image_filter_value(&value[i]);
+
+        if (n == 0) {
             goto failed;
         }
 
         imcf->width = (ngx_uint_t) n;
+
+    } else {
+        imcf->wcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+        if (imcf->wcv == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        *imcf->wcv = cv;
     }
 
-    i++;
+    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
+
+    ccv.cf = cf;
+    ccv.value = &value[++i];
+    ccv.complex_value = &cv;
 
-    if (value[i].len == 1 && value[i].data[0] == '-') {
-        imcf->height = (ngx_uint_t) -1;
+    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
 
-    } else {
-        n = ngx_atoi(value[i].data, value[i].len);
-        if (n == NGX_ERROR) {
+    if (cv.lengths == NULL) {
+        n = ngx_http_image_filter_value(&value[i]);
+
+        if (n == 0) {
             goto failed;
         }
 
         imcf->height = (ngx_uint_t) n;
+
+    } else {
+        imcf->hcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
+        if (imcf->hcv == NULL) {
+            return NGX_CONF_ERROR;
+        }
+
+        *imcf->hcv = cv;
     }
 
     return NGX_CONF_OK;
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -176,23 +176,18 @@ ngx_http_memcached_handler(ngx_http_requ
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
-
-    u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
-    if (u == NULL) {
+    if (ngx_http_upstream_create(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
+    u = r->upstream;
+
     u->schema.len = sizeof("memcached://") - 1;
     u->schema.data = (u_char *) "memcached://";
 
-    u->peer.log = r->connection->log;
-    u->peer.log_error = NGX_ERROR_ERR;
-#if (NGX_THREADS)
-    u->peer.lock = &r->connection->lock;
-#endif
+    u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;
 
-    u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;
+    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
 
     u->conf = &mlcf->upstream;
 
@@ -202,8 +197,6 @@ ngx_http_memcached_handler(ngx_http_requ
     u->abort_request = ngx_http_memcached_abort_request;
     u->finalize_request = ngx_http_memcached_finalize_request;
 
-    r->upstream = u;
-
     ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));
     if (ctx == NULL) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -590,13 +590,10 @@ ngx_http_proxy_handler(ngx_http_request_
     ngx_http_proxy_ctx_t       *ctx;
     ngx_http_proxy_loc_conf_t  *plcf;
 
-    u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
-    if (u == NULL) {
+    if (ngx_http_upstream_create(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    r->upstream = u;
-
     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t));
     if (ctx == NULL) {
         return NGX_ERROR;
@@ -606,6 +603,8 @@ ngx_http_proxy_handler(ngx_http_request_
 
     plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);
 
+    u = r->upstream;
+
     if (plcf->proxy_lengths == 0) {
         ctx->vars = plcf->vars;
         u->schema = plcf->vars.schema;
@@ -619,12 +618,6 @@ ngx_http_proxy_handler(ngx_http_request_
         }
     }
 
-    u->peer.log = r->connection->log;
-    u->peer.log_error = NGX_ERROR_ERR;
-#if (NGX_THREADS)
-    u->peer.lock = &r->connection->lock;
-#endif
-
     u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module;
 
     u->conf = &plcf->upstream;
@@ -2592,6 +2585,12 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
 
     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
 
+    clcf->handler = ngx_http_proxy_handler;
+
+    if (clcf->name.data[clcf->name.len - 1] == '/') {
+        clcf->auto_redirect = 1;
+    }
+
     value = cf->args->elts;
 
     url = &value[1];
@@ -2620,8 +2619,6 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
         }
 #endif
 
-        clcf->handler = ngx_http_proxy_handler;
-
         return NGX_CONF_OK;
     }
 
@@ -2668,8 +2665,6 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
 
     ngx_http_proxy_set_vars(&u, &plcf->vars);
 
-    clcf->handler = ngx_http_proxy_handler;
-
     plcf->location = clcf->name;
 
     if (clcf->named
@@ -2693,10 +2688,6 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_
 
     plcf->url = *url;
 
-    if (clcf->name.data[clcf->name.len - 1] == '/') {
-        clcf->auto_redirect = 1;
-    }
-
     return NGX_CONF_OK;
 }
 
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -568,7 +568,7 @@ ngx_http_rewrite_if(ngx_conf_t *cf, ngx_
 
     if_code = ngx_array_push_n(lcf->codes, sizeof(ngx_http_script_if_code_t));
     if (if_code == NULL) {
-        return NULL;
+        return NGX_CONF_ERROR;
     }
 
     if_code->code = ngx_http_script_if_code;
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -360,6 +360,7 @@ ngx_http_ssi_header_filter(ngx_http_requ
     if (r == r->main) {
         ngx_http_clear_content_length(r);
         ngx_http_clear_last_modified(r);
+        ngx_http_clear_accept_ranges(r);
     }
 
     return ngx_http_next_header_filter(r);
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -31,15 +31,6 @@ static char *ngx_http_ssl_enable(ngx_con
 static char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 
-#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
-
-static char *ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd,
-    void *conf);
-
-static char  ngx_http_ssl_openssl097[] = "OpenSSL 0.9.7 and higher";
-
-#endif
-
 
 static ngx_conf_bitmask_t  ngx_http_ssl_protocols[] = {
     { ngx_string("SSLv2"), NGX_SSL_SSLv2 },
@@ -52,7 +43,7 @@ static ngx_conf_bitmask_t  ngx_http_ssl_
 static ngx_conf_enum_t  ngx_http_ssl_verify[] = {
     { ngx_string("off"), 0 },
     { ngx_string("on"), 1 },
-    { ngx_string("ask"), 2 },
+    { ngx_string("optional"), 2 },
     { ngx_null_string, 0 }
 };
 
@@ -124,14 +115,10 @@ static ngx_command_t  ngx_http_ssl_comma
 
     { ngx_string("ssl_prefer_server_ciphers"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
-#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
       ngx_conf_set_flag_slot,
       NGX_HTTP_SRV_CONF_OFFSET,
       offsetof(ngx_http_ssl_srv_conf_t, prefer_server_ciphers),
       NULL },
-#else
-      ngx_http_ssl_nosupported, 0, 0, ngx_http_ssl_openssl097 },
-#endif
 
     { ngx_string("ssl_session_cache"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,
@@ -147,6 +134,13 @@ static ngx_command_t  ngx_http_ssl_comma
       offsetof(ngx_http_ssl_srv_conf_t, session_timeout),
       NULL },
 
+    { ngx_string("ssl_crl"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_HTTP_SRV_CONF_OFFSET,
+      offsetof(ngx_http_ssl_srv_conf_t, crl),
+      NULL },
+
       ngx_null_command
 };
 
@@ -206,6 +200,9 @@ static ngx_http_variable_t  ngx_http_ssl
     { ngx_string("ssl_client_serial"), NULL, ngx_http_ssl_variable,
       (uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0 },
 
+    { ngx_string("ssl_client_verify"), NULL, ngx_http_ssl_variable,
+      (uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 },
+
     { ngx_null_string, NULL, NULL, 0, 0, 0 }
 };
 
@@ -313,6 +310,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
      *     sscf->certificate_key = { 0, NULL };
      *     sscf->dhparam = { 0, NULL };
      *     sscf->client_certificate = { 0, NULL };
+     *     sscf->crl = { 0, NULL };
      *     sscf->ciphers.len = 0;
      *     sscf->ciphers.data = NULL;
      *     sscf->shm_zone = NULL;
@@ -359,6 +357,7 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
 
     ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,
                          "");
+    ngx_conf_merge_str_value(conf->crl, prev->crl, "");
 
     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
@@ -453,16 +452,16 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
         {
             return NGX_CONF_ERROR;
         }
+
+        if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {
+            return NGX_CONF_ERROR;
+        }
     }
 
-#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
-
     if (conf->prefer_server_ciphers) {
         SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
     }
 
-#endif
-
     /* a temporary 512-bit RSA key is required for export versions of MSIE */
     if (ngx_ssl_generate_rsa512_key(&conf->ssl) != NGX_OK) {
         return NGX_CONF_ERROR;
@@ -620,18 +619,3 @@ invalid:
 
     return NGX_CONF_ERROR;
 }
-
-
-#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
-
-static char *
-ngx_http_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
-{
-    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                       "\"%V\" directive is available only in %s,",
-                       &cmd->name, cmd->post);
-
-    return NGX_CONF_ERROR;
-}
-
-#endif
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -33,6 +33,7 @@ typedef struct {
     ngx_str_t                       certificate_key;
     ngx_str_t                       dhparam;
     ngx_str_t                       client_certificate;
+    ngx_str_t                       crl;
 
     ngx_str_t                       ciphers;
 
--- a/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -720,7 +720,7 @@ ngx_http_xslt_sax_error(void *data, cons
     while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }
 
     ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,
-                  "libxml2 error: \"%*s\"", n, buf);
+                  "libxml2 error: \"%*s\"", n + 1, buf);
 }
 
 
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.8.6';
+our $VERSION = '0.8.7';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -950,9 +950,6 @@ ngx_http_parse_complex_uri(ngx_http_requ
         sw_slash,
         sw_dot,
         sw_dot_dot,
-#if (NGX_WIN32)
-        sw_dot_dot_dot,
-#endif
         sw_quoted,
         sw_quoted_second
     } state, quoted_state;
@@ -1154,12 +1151,6 @@ ngx_http_parse_complex_uri(ngx_http_requ
                 goto args;
             case '#':
                 goto done;
-#if (NGX_WIN32)
-            case '.':
-                state = sw_dot_dot_dot;
-                *u++ = ch;
-                break;
-#endif
             case '+':
                 r->plus_in_uri = 1;
             default:
@@ -1171,55 +1162,6 @@ ngx_http_parse_complex_uri(ngx_http_requ
             ch = *p++;
             break;
 
-#if (NGX_WIN32)
-        case sw_dot_dot_dot:
-
-            if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
-                state = sw_usual;
-                *u++ = ch;
-                ch = *p++;
-                break;
-            }
-
-            switch(ch) {
-            case '\\':
-            case '/':
-                state = sw_slash;
-                u -= 5;
-                if (u < r->uri.data) {
-                    return NGX_HTTP_PARSE_INVALID_REQUEST;
-                }
-                while (*u != '/') {
-                    u--;
-                }
-                if (u < r->uri.data) {
-                    return NGX_HTTP_PARSE_INVALID_REQUEST;
-                }
-                while (*(u - 1) != '/') {
-                    u--;
-                }
-                break;
-            case '%':
-                quoted_state = state;
-                state = sw_quoted;
-                break;
-            case '?':
-                r->args_start = p;
-                goto args;
-            case '#':
-                goto done;
-            case '+':
-                r->plus_in_uri = 1;
-            default:
-                state = sw_usual;
-                *u++ = ch;
-                break;
-            }
-
-            ch = *p++;
-            break;
-#endif
-
         case sw_quoted:
             r->quoted_uri = 1;
 
@@ -1369,20 +1311,6 @@ ngx_http_parse_unsafe_uri(ngx_http_reque
             if (p[0] == '.' && p[1] == '.' && ngx_path_separator(p[2])) {
                 goto unsafe;
             }
-
-#if (NGX_WIN32)
-
-            if (len > 3) {
-
-                /* detect "/.../" */
-
-                if (p[0] == '.' && p[1] == '.' && p[2] == '.'
-                    && ngx_path_separator(p[3]))
-                {
-                    goto unsafe;
-                }
-            }
-#endif
         }
     }
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -129,7 +129,7 @@ ngx_http_header_t  ngx_http_headers_in[]
     { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive),
                  ngx_http_process_header_line },
 
-#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP)
+#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)
     { ngx_string("X-Forwarded-For"),
                  offsetof(ngx_http_headers_in_t, x_forwarded_for),
                  ngx_http_process_header_line },
@@ -384,6 +384,7 @@ ngx_http_init_request(ngx_event_t *rev)
     r->loc_conf = cscf->ctx->loc_conf;
 
     rev->handler = ngx_http_process_request_line;
+    r->read_event_handler = ngx_http_block_reading;
 
 #if (NGX_HTTP_SSL)
 
@@ -1524,7 +1525,7 @@ ngx_http_process_request(ngx_http_reques
 
         sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);
 
-        if (sscf->verify == 1) {
+        if (sscf->verify) {
             rc = SSL_get_verify_result(c->ssl->connection);
 
             if (rc != X509_V_OK) {
@@ -1539,20 +1540,22 @@ ngx_http_process_request(ngx_http_reques
                 return;
             }
 
-            cert = SSL_get_peer_certificate(c->ssl->connection);
-
-            if (cert == NULL) {
-                ngx_log_error(NGX_LOG_INFO, c->log, 0,
-                              "client sent no required SSL certificate");
-
-                ngx_ssl_remove_cached_session(sscf->ssl.ctx,
+            if (sscf->verify == 1) {
+                cert = SSL_get_peer_certificate(c->ssl->connection);
+
+                if (cert == NULL) {
+                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
+                                  "client sent no required SSL certificate");
+
+                    ngx_ssl_remove_cached_session(sscf->ssl.ctx,
                                        (SSL_get0_session(c->ssl->connection)));
 
-                ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);
-                return;
+                    ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);
+                    return;
+                }
+
+                X509_free(cert);
             }
-
-            X509_free(cert);
         }
     }
 
@@ -2715,6 +2718,7 @@ ngx_http_send_special(ngx_http_request_t
             b->last_buf = 1;
 
         } else {
+            b->sync = 1;
             b->last_in_chain = 1;
         }
     }
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -184,7 +184,7 @@ typedef struct {
 
     ngx_table_elt_t                  *keep_alive;
 
-#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP)
+#if (NGX_HTTP_PROXY || NGX_HTTP_REALIP || NGX_HTTP_GEO)
     ngx_table_elt_t                  *x_forwarded_for;
 #endif
 
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -224,19 +224,19 @@ ngx_http_upstream_header_t  ngx_http_ups
     { ngx_string("X-Accel-Redirect"),
                  ngx_http_upstream_process_header_line,
                  offsetof(ngx_http_upstream_headers_in_t, x_accel_redirect),
-                 ngx_http_upstream_ignore_header_line, 0, 0 },
+                 ngx_http_upstream_copy_header_line, 0, 0 },
 
     { ngx_string("X-Accel-Limit-Rate"),
                  ngx_http_upstream_process_limit_rate, 0,
-                 ngx_http_upstream_ignore_header_line, 0, 0 },
+                 ngx_http_upstream_copy_header_line, 0, 0 },
 
     { ngx_string("X-Accel-Buffering"),
                  ngx_http_upstream_process_buffering, 0,
-                 ngx_http_upstream_ignore_header_line, 0, 0 },
+                 ngx_http_upstream_copy_header_line, 0, 0 },
 
     { ngx_string("X-Accel-Charset"),
                  ngx_http_upstream_process_charset, 0,
-                 ngx_http_upstream_ignore_header_line, 0, 0 },
+                 ngx_http_upstream_copy_header_line, 0, 0 },
 
 #if (NGX_HTTP_GZIP)
     { ngx_string("Content-Encoding"),
@@ -348,6 +348,35 @@ ngx_conf_bitmask_t  ngx_http_upstream_ca
 };
 
 
+ngx_int_t
+ngx_http_upstream_create(ngx_http_request_t *r)
+{
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+
+    if (u && u->cleanup) {
+        ngx_http_upstream_cleanup(r);
+        *u->cleanup = NULL;
+    }
+
+    u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
+    if (u == NULL) {
+        return NGX_ERROR;
+    }
+
+    r->upstream = u;
+
+    u->peer.log = r->connection->log;
+    u->peer.log_error = NGX_ERROR_ERR;
+#if (NGX_THREADS)
+    u->peer.lock = &r->connection->lock;
+#endif
+
+    return NGX_OK;
+}
+
+
 void
 ngx_http_upstream_init(ngx_http_request_t *r)
 {
@@ -3301,10 +3330,11 @@ ngx_http_upstream_copy_last_modified(ngx
 
     *ho = *h;
 
+    r->headers_out.last_modified = ho;
+
 #if (NGX_HTTP_CACHE)
 
     if (r->upstream->cacheable) {
-        r->headers_out.last_modified = ho;
         r->headers_out.last_modified_time = ngx_http_parse_time(h->value.data,
                                                                 h->value.len);
     }
@@ -3428,6 +3458,8 @@ ngx_http_upstream_copy_allow_ranges(ngx_
 
     *ho = *h;
 
+    r->headers_out.accept_ranges = ho;
+
     return NGX_OK;
 }
 
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -317,6 +317,7 @@ typedef struct {
 ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data);
 
+ngx_int_t ngx_http_upstream_create(ngx_http_request_t *r);
 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_uint_t flags);
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -22,15 +22,6 @@ static char *ngx_mail_ssl_starttls(ngx_c
 static char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 
-#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
-
-static char *ngx_mail_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd,
-    void *conf);
-
-static char  ngx_mail_ssl_openssl097[] = "OpenSSL 0.9.7 and higher";
-
-#endif
-
 
 static ngx_conf_enum_t  ngx_http_starttls_state[] = {
     { ngx_string("off"), NGX_MAIL_STARTTLS_OFF },
@@ -102,14 +93,10 @@ static ngx_command_t  ngx_mail_ssl_comma
 
     { ngx_string("ssl_prefer_server_ciphers"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
-#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
       ngx_conf_set_flag_slot,
       NGX_MAIL_SRV_CONF_OFFSET,
       offsetof(ngx_mail_ssl_conf_t, prefer_server_ciphers),
       NULL },
-#else
-      ngx_mail_ssl_nosupported, 0, 0, ngx_mail_ssl_openssl097 },
-#endif
 
     { ngx_string("ssl_session_cache"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12,
@@ -297,14 +284,10 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
         }
     }
 
-#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
-
     if (conf->prefer_server_ciphers) {
         SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
     }
 
-#endif
-
     if (ngx_ssl_generate_rsa512_key(&conf->ssl) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
@@ -492,18 +475,3 @@ invalid:
 
     return NGX_CONF_ERROR;
 }
-
-
-#if !defined (SSL_OP_CIPHER_SERVER_PREFERENCE)
-
-static char *
-ngx_mail_ssl_nosupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
-{
-    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
-                       "\"%V\" directive is available only in %s,",
-                       &cmd->name, cmd->post);
-
-    return NGX_CONF_ERROR;
-}
-
-#endif