changeset 632:5b73504dd4ba NGINX_1_1_0

nginx 1.1.0 *) Feature: cache loader run time decrease. *) Feature: "loader_files", "loader_sleep", and "loader_threshold" options of the "proxy/fastcgi/scgi/uwsgi_cache_path" directives. *) Feature: loading time decrease of configuration with large number of HTTPS sites. *) Feature: now nginx supports ECDHE key exchange ciphers. Thanks to Adrian Kotelba. *) Feature: the "lingering_close" directive. Thanks to Maxim Dounin. *) Bugfix: in closing connection for pipelined requests. Thanks to Maxim Dounin. *) Bugfix: nginx did not disable gzipping if client sent "gzip;q=0" in "Accept-Encoding" request header line. *) Bugfix: in timeout in unbuffered proxied mode. Thanks to Maxim Dounin. *) Bugfix: memory leaks when a "proxy_pass" directive contains variables and proxies to an HTTPS backend. Thanks to Maxim Dounin. *) Bugfix: in parameter validaiton of a "proxy_pass" directive with variables. Thanks to Lanshun Zhou. *) Bugfix: SSL did not work on QNX. Thanks to Maxim Dounin. *) Bugfix: SSL modules could not be built by gcc 4.6 without --with-debug option.
author Igor Sysoev <http://sysoev.ru>
date Mon, 01 Aug 2011 00:00:00 +0400
parents 9b978fa3cd33
children 561a37709f6d
files CHANGES CHANGES.ru src/core/nginx.h src/core/ngx_config.h src/core/ngx_file.c src/core/ngx_file.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_proxy_module.c src/http/modules/ngx_http_scgi_module.c src/http/modules/ngx_http_ssl_module.c src/http/modules/ngx_http_ssl_module.h src/http/modules/ngx_http_uwsgi_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_cache.h src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_file_cache.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/http/ngx_http_upstream_round_robin.c src/mail/ngx_mail_ssl_module.c src/mail/ngx_mail_ssl_module.h src/os/unix/ngx_files.h
diffstat 27 files changed, 509 insertions(+), 158 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,44 @@
 
+Changes with nginx 1.1.0                                         01 Aug 2011
+
+    *) Feature: cache loader run time decrease.
+
+    *) Feature: "loader_files", "loader_sleep", and "loader_threshold" 
+       options of the "proxy/fastcgi/scgi/uwsgi_cache_path" directives.
+
+    *) Feature: loading time decrease of configuration with large number of 
+       HTTPS sites.
+
+    *) Feature: now nginx supports ECDHE key exchange ciphers.
+       Thanks to Adrian Kotelba.
+
+    *) Feature: the "lingering_close" directive.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: in closing connection for pipelined requests.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: nginx did not disable gzipping if client sent "gzip;q=0" in 
+       "Accept-Encoding" request header line.
+
+    *) Bugfix: in timeout in unbuffered proxied mode.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: memory leaks when a "proxy_pass" directive contains 
+       variables and proxies to an HTTPS backend.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: in parameter validaiton of a "proxy_pass" directive with 
+       variables.
+       Thanks to Lanshun Zhou.
+
+    *) Bugfix: SSL did not work on QNX.
+       Thanks to Maxim Dounin.
+
+    *) Bugfix: SSL modules could not be built by gcc 4.6 without 
+       --with-debug option.
+
+
 Changes with nginx 1.0.5                                         19 Jul 2011
 
     *) Change: now default SSL ciphers are "HIGH:!aNULL:!MD5".
@@ -53,9 +93,9 @@ Changes with nginx 1.0.3                
        testing IPv4 address mapped to IPv6 address, if access or deny rules 
        were defined only for IPv6; the bug had appeared in 0.8.22.
 
-    *) Bugfix: a cached response may be broken if proxy/fastcgi/scgi/ 
-       uwsgi_cache_bypass and proxy/fastcgi/scgi/uwsgi_no_cache directive 
-       values were different; the bug had appeared in 0.8.46.
+    *) Bugfix: a cached response may be broken if "proxy/fastcgi/scgi/ 
+       uwsgi_cache_bypass" and "proxy/fastcgi/scgi/uwsgi_no_cache" 
+       directive values were different; the bug had appeared in 0.8.46.
 
 
 Changes with nginx 1.0.2                                         10 May 2011
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,44 @@
 
+Изменения в nginx 1.1.0                                           01.08.2011
+
+    *) Добавление: уменьшение времени работы загрузчика кэша.
+
+    *) Добавление: параметры loader_files, loader_sleep и loader_threshold 
+       директив proxy/fastcgi/scgi/uwsgi_cache_path.
+
+    *) Добавление: уменьшение времени загрузки конфигураций с большим 
+       количеством HTTPS серверов.
+
+    *) Добавление: теперь nginx поддерживает шифры с обменом ECDHE-ключами. 
+       Спасибо Adrian Kotelba.
+
+    *) Добавление: директива lingering_close.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: закрытия соединения для pipelined-запросов.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: nginx не запрещал сжатие при получении значения 
+       "gzip;q=0" в строке "Accept-Encoding" в заголовке запроса клиента.
+
+    *) Исправление: таймаута при небуферизированном проксировании.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: утечки памяти при использовании переменных в директиве 
+       proxy_pass при работе с бэкендом по HTTPS.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: в проверке параметра директивы proxy_pass, заданного 
+       переменными.
+       Спасибо Lanshun Zhou.
+
+    *) Исправление: SSL не работал на QNX.
+       Спасибо Максиму Дунину.
+
+    *) Исправление: SSL модули не собирались gcc 4.6 без параметра 
+       --with-debug.
+
+
 Изменения в nginx 1.0.5                                           19.07.2011
 
     *) Изменение: теперь по умолчанию используются следующие шифры SSL: 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1000005
-#define NGINX_VERSION      "1.0.5"
+#define nginx_version      1001000
+#define NGINX_VERSION      "1.1.0"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_config.h
+++ b/src/core/ngx_config.h
@@ -127,5 +127,7 @@ typedef intptr_t        ngx_flag_t;
 #define NGX_MAX_UINT32_VALUE  (uint32_t) 0xffffffff
 #endif
 
+#define NGX_MAX_INT32_VALUE   (uint32_t) 0x7fffffff
+
 
 #endif /* _NGX_CONFIG_H_INCLUDED_ */
--- a/src/core/ngx_file.c
+++ b/src/core/ngx_file.c
@@ -926,6 +926,7 @@ ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_s
                            "tree file \"%s\"", file.data);
 
             ctx->size = ngx_de_size(&dir);
+            ctx->fs_size = ngx_de_fs_size(&dir);
             ctx->access = ngx_de_access(&dir);
             ctx->mtime = ngx_de_mtime(&dir);
 
--- a/src/core/ngx_file.h
+++ b/src/core/ngx_file.h
@@ -104,6 +104,7 @@ typedef ngx_int_t (*ngx_tree_handler_pt)
 
 struct ngx_tree_ctx_s {
     off_t                      size;
+    off_t                      fs_size;
     ngx_uint_t                 access;
     time_t                     mtime;
 
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -371,28 +371,18 @@ ngx_ssl_info_callback(const ngx_ssl_conn
 }
 
 
-ngx_int_t
-ngx_ssl_generate_rsa512_key(ngx_ssl_t *ssl)
+RSA *
+ngx_ssl_rsa512_key_callback(SSL *ssl, int is_export, int key_length)
 {
-    RSA  *key;
-
-    if (SSL_CTX_need_tmp_RSA(ssl->ctx) == 0) {
-        return NGX_OK;
+    static RSA  *key;
+
+    if (key_length == 512) {
+        if (key == NULL) {
+            key = RSA_generate_key(512, RSA_F4, NULL, NULL);
+        }
     }
 
-    key = RSA_generate_key(512, RSA_F4, NULL, NULL);
-
-    if (key) {
-        SSL_CTX_set_tmp_rsa(ssl->ctx, key);
-
-        RSA_free(key);
-
-        return NGX_OK;
-    }
-
-    ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "RSA_generate_key(512) failed");
-
-    return NGX_ERROR;
+    return key;
 }
 
 
@@ -478,6 +468,45 @@ ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_
     return NGX_OK;
 }
 
+ngx_int_t
+ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+#ifndef OPENSSL_NO_ECDH
+    int      nid;
+    EC_KEY  *ecdh;
+
+    /*
+     * Elliptic-Curve Diffie-Hellman parameters are either "named curves"
+     * from RFC 4492 section 5.1.1, or explicitely described curves over
+     * binary fields. OpenSSL only supports the "named curves", which provide
+     * maximum interoperability.
+     */
+
+    nid = OBJ_sn2nid((const char *) name->data);
+    if (nid == 0) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "Unknown curve name \"%s\"", name->data);
+        return NGX_ERROR;
+    }
+
+    ecdh = EC_KEY_new_by_curve_name(nid);
+    if (ecdh == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
+                      "Unable to create curve \"%s\"", name->data);
+        return NGX_ERROR;
+    }
+
+    SSL_CTX_set_tmp_ecdh(ssl->ctx, ecdh);
+
+    SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE);
+
+    EC_KEY_free(ecdh);
+#endif
+#endif
+
+    return NGX_OK;
+}
 
 ngx_int_t
 ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
@@ -957,10 +986,10 @@ ngx_ssl_send_chain(ngx_connection_t *c, 
     }
 
 
-    /* the maximum limit size is the maximum uint32_t value - the page size */
-
-    if (limit == 0 || limit > (off_t) (NGX_MAX_UINT32_VALUE - ngx_pagesize)) {
-        limit = NGX_MAX_UINT32_VALUE - ngx_pagesize;
+    /* the maximum limit size is the maximum int32_t value - the page size */
+
+    if (limit == 0 || limit > (off_t) (NGX_MAX_INT32_VALUE - ngx_pagesize)) {
+        limit = NGX_MAX_INT32_VALUE - ngx_pagesize;
     }
 
     buf = c->ssl->buf;
@@ -1687,20 +1716,24 @@ ngx_ssl_get_cached_session(ngx_ssl_conn_
     ngx_int_t                 rc;
     ngx_shm_zone_t           *shm_zone;
     ngx_slab_pool_t          *shpool;
-    ngx_connection_t         *c;
     ngx_rbtree_node_t        *node, *sentinel;
     ngx_ssl_session_t        *sess;
     ngx_ssl_sess_id_t        *sess_id;
     ngx_ssl_session_cache_t  *cache;
     u_char                    buf[NGX_SSL_MAX_SESSION_SIZE];
-
-    c = ngx_ssl_get_connection(ssl_conn);
+#if (NGX_DEBUG)
+    ngx_connection_t         *c;
+#endif
 
     hash = ngx_crc32_short(id, (size_t) len);
     *copy = 0;
 
+#if (NGX_DEBUG)
+    c = ngx_ssl_get_connection(ssl_conn);
+
     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    "ssl get session: %08XD:%d", hash, len);
+#endif
 
     shm_zone = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn),
                                    ngx_ssl_session_cache_index);
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -99,8 +99,9 @@ ngx_int_t ngx_ssl_certificate(ngx_conf_t
 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);
+RSA *ngx_ssl_rsa512_key_callback(SSL *ssl, int is_export, int key_length);
 ngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);
+ngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);
 ngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
     ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout);
 ngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -2003,6 +2003,8 @@ ngx_http_fastcgi_create_loc_conf(ngx_con
 
     conf->catch_stderr = NGX_CONF_UNSET_PTR;
 
+    ngx_str_set(&conf->upstream.module, "fastcgi");
+
     return conf;
 }
 
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -642,15 +642,17 @@ ngx_http_proxy_eval(ngx_http_request_t *
         return NGX_ERROR;
     }
 
-    if (ngx_strncasecmp(proxy.data, (u_char *) "http://", 7) == 0) {
-
+    if (proxy.len > 7
+        && ngx_strncasecmp(proxy.data, (u_char *) "http://", 7) == 0)
+    {
         add = 7;
         port = 80;
 
 #if (NGX_HTTP_SSL)
 
-    } else if (ngx_strncasecmp(proxy.data, (u_char *) "https://", 8) == 0) {
-
+    } else if (proxy.len > 8
+               && ngx_strncasecmp(proxy.data, (u_char *) "https://", 8) == 0)
+    {
         add = 8;
         port = 443;
         r->upstream->ssl = 1;
@@ -1707,6 +1709,8 @@ ngx_http_proxy_create_loc_conf(ngx_conf_
     conf->headers_hash_max_size = NGX_CONF_UNSET_UINT;
     conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT;
 
+    ngx_str_set(&conf->upstream.module, "proxy");
+
     return conf;
 }
 
--- a/src/http/modules/ngx_http_scgi_module.c
+++ b/src/http/modules/ngx_http_scgi_module.c
@@ -1030,6 +1030,8 @@ ngx_http_scgi_create_loc_conf(ngx_conf_t
     /* "scgi_cyclic_temp_file" is disabled */
     conf->upstream.cyclic_temp_file = 0;
 
+    ngx_str_set(&conf->upstream.module, "scgi");
+
     return conf;
 }
 
--- a/src/http/modules/ngx_http_ssl_module.c
+++ b/src/http/modules/ngx_http_ssl_module.c
@@ -13,7 +13,8 @@ typedef ngx_int_t (*ngx_ssl_variable_han
     ngx_pool_t *pool, ngx_str_t *s);
 
 
-#define NGX_DEFAULT_CIPHERS  "HIGH:!aNULL:!MD5"
+#define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"
+#define NGX_DEFAULT_ECDH_CURVE  "prime256v1"
 
 
 static ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,
@@ -78,6 +79,13 @@ static ngx_command_t  ngx_http_ssl_comma
       offsetof(ngx_http_ssl_srv_conf_t, dhparam),
       NULL },
 
+    { ngx_string("ssl_ecdh_curve"),
+      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, ecdh_curve),
+      NULL },
+
     { ngx_string("ssl_protocols"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,
       ngx_conf_set_bitmask_slot,
@@ -312,6 +320,7 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t 
      *     sscf->certificate = { 0, NULL };
      *     sscf->certificate_key = { 0, NULL };
      *     sscf->dhparam = { 0, NULL };
+     *     sscf->ecdh_curve = { 0, NULL };
      *     sscf->client_certificate = { 0, NULL };
      *     sscf->crl = { 0, NULL };
      *     sscf->ciphers = { 0, NULL };
@@ -360,6 +369,9 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
                          "");
     ngx_conf_merge_str_value(conf->crl, prev->crl, "");
 
+    ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
+                         NGX_DEFAULT_ECDH_CURVE);
+
     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
 
@@ -465,11 +477,13 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *
     }
 
     /* a temporary 512-bit RSA key is required for export versions of MSIE */
-    if (ngx_ssl_generate_rsa512_key(&conf->ssl) != NGX_OK) {
+    SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback);
+
+    if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
-    if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
+    if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {
         return NGX_CONF_ERROR;
     }
 
--- a/src/http/modules/ngx_http_ssl_module.h
+++ b/src/http/modules/ngx_http_ssl_module.h
@@ -32,6 +32,7 @@ typedef struct {
     ngx_str_t                       certificate;
     ngx_str_t                       certificate_key;
     ngx_str_t                       dhparam;
+    ngx_str_t                       ecdh_curve;
     ngx_str_t                       client_certificate;
     ngx_str_t                       crl;
 
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -1083,6 +1083,8 @@ ngx_http_uwsgi_create_loc_conf(ngx_conf_
     /* "uwsgi_cyclic_temp_file" is disabled */
     conf->upstream.cyclic_temp_file = 0;
 
+    ngx_str_set(&conf->upstream.module, "uwsgi");
+
     return conf;
 }
 
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -48,7 +48,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '1.0.5';
+our $VERSION = '1.1.0';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_cache.h
+++ b/src/http/ngx_http_cache.h
@@ -118,8 +118,11 @@ struct ngx_http_file_cache_s {
 
     time_t                           inactive;
 
+    ngx_uint_t                       files;
+    ngx_uint_t                       loader_files;
     ngx_msec_t                       last;
-    ngx_uint_t                       files;
+    ngx_msec_t                       loader_sleep;
+    ngx_msec_t                       loader_threshold;
 
     ngx_shm_zone_t                  *shm_zone;
 };
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -70,6 +70,7 @@ static char *ngx_http_core_internal(ngx_
 static char *ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 #if (NGX_HTTP_GZIP)
+static ngx_int_t ngx_http_gzip_accept_encoding(ngx_str_t *ae);
 static char *ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 #endif
@@ -125,6 +126,14 @@ static ngx_conf_enum_t  ngx_http_core_sa
 };
 
 
+static ngx_conf_enum_t  ngx_http_core_lingering_close[] = {
+    { ngx_string("off"), NGX_HTTP_LINGERING_OFF },
+    { ngx_string("on"), NGX_HTTP_LINGERING_ON },
+    { ngx_string("always"), NGX_HTTP_LINGERING_ALWAYS },
+    { ngx_null_string, 0 }
+};
+
+
 static ngx_conf_enum_t  ngx_http_core_if_modified_since[] = {
     { ngx_string("off"), NGX_HTTP_IMS_OFF },
     { ngx_string("exact"), NGX_HTTP_IMS_EXACT },
@@ -530,6 +539,13 @@ static ngx_command_t  ngx_http_core_comm
       0,
       NULL },
 
+    { ngx_string("lingering_close"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_enum_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, lingering_close),
+      &ngx_http_core_lingering_close },
+
     { ngx_string("lingering_time"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_msec_slot,
@@ -2016,24 +2032,35 @@ ngx_http_gzip_ok(ngx_http_request_t *r)
     time_t                     date, expires;
     ngx_uint_t                 p;
     ngx_array_t               *cc;
-    ngx_table_elt_t           *e, *d;
+    ngx_table_elt_t           *e, *d, *ae;
     ngx_http_core_loc_conf_t  *clcf;
 
     r->gzip_tested = 1;
 
-    if (r != r->main
-        || r->headers_in.accept_encoding == NULL
-        || ngx_strcasestrn(r->headers_in.accept_encoding->value.data,
-                           "gzip", 4 - 1)
-           == NULL
-
-        /*
-         * if the URL (without the "http://" prefix) is longer than 253 bytes,
-         * then MSIE 4.x can not handle the compressed stream - it waits
-         * too long, hangs up or crashes
-         */
-
-        || (r->headers_in.msie4 && r->unparsed_uri.len > 200))
+    if (r != r->main) {
+        return NGX_DECLINED;
+    }
+
+    ae = r->headers_in.accept_encoding;
+    if (ae == NULL) {
+        return NGX_DECLINED;
+    }
+
+    if (ae->value.len < sizeof("gzip") - 1) {
+        return NGX_DECLINED;
+    }
+
+    /*
+     * test first for the most common case "gzip,...":
+     *   MSIE:    "gzip, deflate"
+     *   Firefox: "gzip,deflate"
+     *   Chrome:  "gzip,deflate,sdch"
+     *   Safari:  "gzip, deflate"
+     *   Opera:   "gzip, deflate"
+     */
+
+    if (ngx_memcmp(ae->value.data, "gzip,", 5) != 0
+        && ngx_http_gzip_accept_encoding(&ae->value) != NGX_OK)
     {
         return NGX_DECLINED;
     }
@@ -2159,6 +2186,127 @@ ok:
     return NGX_OK;
 }
 
+
+/*
+ * gzip is enabled for the following quantities:
+ *     "gzip; q=0.001" ... "gzip; q=0.999", "gzip; q=1"
+ * gzip is disabled for the following quantities:
+ *     "gzip; q=0" ... "gzip; q=0.000", and for any invalid cases
+ */
+
+static ngx_int_t
+ngx_http_gzip_accept_encoding(ngx_str_t *ae)
+{
+    u_char      c, *p, *start, *last;
+    ngx_uint_t  n, q;
+
+    start = ae->data;
+    last = start + ae->len;
+
+    for ( ;; ) {
+        p = ngx_strcasestrn(start, "gzip", 4 - 1);
+        if (p == NULL) {
+            return NGX_DECLINED;
+        }
+
+        if (p == start || (*(p - 1) == ',' || *(p - 1) == ' ')) {
+            break;
+        }
+
+        start = p + 4;
+    }
+
+    p += 4;
+
+    while (p < last) {
+        switch(*p++) {
+        case ',':
+            return NGX_OK;
+        case ';':
+            goto quantity;
+        case ' ':
+            continue;
+        default:
+            return NGX_DECLINED;
+        }
+    }
+
+    return NGX_OK;
+
+quantity:
+
+    while (p < last) {
+        switch(*p++) {
+        case 'q':
+        case 'Q':
+            goto equal;
+        case ' ':
+            continue;
+        default:
+            return NGX_DECLINED;
+        }
+    }
+
+    return NGX_OK;
+
+equal:
+
+    if (p + 2 > last || *p++ != '=') {
+        return NGX_DECLINED;
+    }
+
+    c = *p++;
+
+    if (c == '1') {
+        if (p == last || *p == ',' || *p == ' ') {
+            return NGX_OK;
+        }
+        return NGX_DECLINED;
+    }
+
+    if (c != '0') {
+        return NGX_DECLINED;
+    }
+
+    if (p == last) {
+        return NGX_DECLINED;
+    }
+
+    if (*p++ != '.') {
+        return NGX_DECLINED;
+    }
+
+    n = 0;
+    q = 0;
+
+    while (p < last) {
+        c = *p++;
+
+        if (c == ',') {
+            break;
+        }
+
+        if (c >= '1' && c <= '9') {
+            n++;
+            q++;
+            continue;
+        }
+
+        if (c == '0') {
+            n++;
+            continue;
+        }
+
+        return NGX_DECLINED;
+    }
+
+    if (n < 4 && q != 0) {
+        return NGX_OK;
+    }
+
+    return NGX_DECLINED;
+}
+
 #endif
 
 
@@ -3117,6 +3265,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t
     clcf->keepalive_timeout = NGX_CONF_UNSET_MSEC;
     clcf->keepalive_header = NGX_CONF_UNSET;
     clcf->keepalive_requests = NGX_CONF_UNSET_UINT;
+    clcf->lingering_close = NGX_CONF_UNSET_UINT;
     clcf->lingering_time = NGX_CONF_UNSET_MSEC;
     clcf->lingering_timeout = NGX_CONF_UNSET_MSEC;
     clcf->resolver_timeout = NGX_CONF_UNSET_MSEC;
@@ -3333,6 +3482,8 @@ ngx_http_core_merge_loc_conf(ngx_conf_t 
                               prev->keepalive_header, 0);
     ngx_conf_merge_uint_value(conf->keepalive_requests,
                               prev->keepalive_requests, 100);
+    ngx_conf_merge_msec_value(conf->lingering_close,
+                              prev->lingering_close, NGX_HTTP_LINGERING_ON);
     ngx_conf_merge_msec_value(conf->lingering_time,
                               prev->lingering_time, 30000);
     ngx_conf_merge_msec_value(conf->lingering_timeout,
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -33,6 +33,11 @@
 #define NGX_HTTP_SATISFY_ANY            1
 
 
+#define NGX_HTTP_LINGERING_OFF          0
+#define NGX_HTTP_LINGERING_ON           1
+#define NGX_HTTP_LINGERING_ALWAYS       2
+
+
 #define NGX_HTTP_IMS_OFF                0
 #define NGX_HTTP_IMS_EXACT              1
 #define NGX_HTTP_IMS_BEFORE             2
@@ -356,6 +361,7 @@ struct ngx_http_core_loc_conf_s {
     ngx_uint_t    keepalive_requests;      /* keepalive_requests */
     ngx_uint_t    keepalive_disable;       /* keepalive_disable */
     ngx_uint_t    satisfy;                 /* satisfy */
+    ngx_uint_t    lingering_close;         /* lingering_close */
     ngx_uint_t    if_modified_since;       /* if_modified_since */
     ngx_uint_t    client_body_in_file_only; /* client_body_in_file_only */
 
--- a/src/http/ngx_http_file_cache.c
+++ b/src/http/ngx_http_file_cache.c
@@ -31,7 +31,7 @@ static time_t ngx_http_file_cache_expire
 static void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,
     ngx_queue_t *q, u_char *name);
 static ngx_int_t
-    ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache);
+    ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache);
 static ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,
     ngx_str_t *path);
 static ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,
@@ -376,7 +376,7 @@ ngx_http_file_cache_read(ngx_http_reques
     if ((size_t) n < c->header_start) {
         ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,
                       "cache file \"%s\" is too small", c->file.name.data);
-        return NGX_ERROR;
+        return NGX_DECLINED;
     }
 
     h = (ngx_http_file_cache_header_t *) c->buf->pos;
@@ -530,26 +530,19 @@ ngx_http_file_cache_exists(ngx_http_file
             goto done;
         }
 
-        if (fcn->exists) {
+        if (fcn->exists || fcn->uses >= c->min_uses) {
 
             c->exists = fcn->exists;
-            c->body_start = fcn->body_start;
+            if (fcn->body_start) {
+                c->body_start = fcn->body_start;
+            }
 
             rc = NGX_OK;
 
             goto done;
         }
 
-        if (fcn->uses >= c->min_uses) {
-
-            c->exists = fcn->exists;
-            c->body_start = fcn->body_start;
-
-            rc = NGX_OK;
-
-        } else {
-            rc = NGX_AGAIN;
-        }
+        rc = NGX_AGAIN;
 
         goto done;
     }
@@ -858,7 +851,7 @@ ngx_http_cache_send(ngx_http_request_t *
     c = r->cache;
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                  "http file cache send: %s", c->file.name.data);
+                   "http file cache send: %s", c->file.name.data);
 
     /* we need to allocate all before the header would be sent */
 
@@ -1214,7 +1207,7 @@ ngx_http_file_cache_manager(void *data)
             return wait;
         }
 
-        if (ngx_http_file_cache_manager_sleep(cache) != NGX_OK) {
+        if (ngx_quit || ngx_terminate) {
             return next;
         }
     }
@@ -1268,31 +1261,38 @@ ngx_http_file_cache_loader(void *data)
 
 
 static ngx_int_t
-ngx_http_file_cache_manager_sleep(ngx_http_file_cache_t *cache)
+ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache)
 {
     ngx_msec_t  elapsed;
 
-    if (cache->files++ > 100) {
+    if (++cache->files >= cache->loader_files) {
 
         ngx_time_update();
 
         elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));
 
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
-                       "http file cache manager time: %M", elapsed);
+                       "http file cache loader time elapsed: %M", elapsed);
+
+        if (elapsed >= cache->loader_threshold) {
 
-        if (elapsed > 200) {
+            if (cache->loader_files > 1) {
+                cache->loader_files /= 2;
+                ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+                              "cache %V loader_files decreased to %ui",
+                              &cache->path->name, cache->loader_files);
 
-            /*
-             * if processing 100 files takes more than 200ms,
-             * it seems that many operations require disk i/o,
-             * therefore sleep 200ms
-             */
+            } else {
+                cache->loader_sleep *= 2;
+                ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+                              "cache %V loader_sleep increased to %Mms",
+                              &cache->path->name, cache->loader_sleep);
+            }
+        }
 
-            ngx_msleep(200);
+        ngx_msleep(cache->loader_sleep);
 
-            ngx_time_update();
-        }
+        ngx_time_update();
 
         cache->last = ngx_current_msec;
         cache->files = 0;
@@ -1320,75 +1320,34 @@ ngx_http_file_cache_manage_file(ngx_tree
         (void) ngx_http_file_cache_delete_file(ctx, path);
     }
 
-    return ngx_http_file_cache_manager_sleep(cache);
+    return ngx_http_file_cache_loader_sleep(cache);
 }
 
 
 static ngx_int_t
 ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)
 {
-    u_char                        *p;
-    ngx_fd_t                       fd;
-    ngx_int_t                      n;
-    ngx_uint_t                     i;
-    ngx_file_info_t                fi;
-    ngx_http_cache_t               c;
-    ngx_http_file_cache_t         *cache;
-    ngx_http_file_cache_header_t   h;
+    u_char                 *p;
+    ngx_int_t               n;
+    ngx_uint_t              i;
+    ngx_http_cache_t        c;
+    ngx_http_file_cache_t  *cache;
 
     if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {
         return NGX_ERROR;
     }
 
-    ngx_memzero(&c, sizeof(ngx_http_cache_t));
-
-    fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
-
-    if (fd == NGX_INVALID_FILE) {
-        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
-                      ngx_open_file_n " \"%s\" failed", name->data);
-        return NGX_ERROR;
-    }
-
-    c.file.fd = fd;
-    c.file.name = *name;
-    c.file.log = ctx->log;
-
-    n = ngx_read_file(&c.file, (u_char *) &h,
-                      sizeof(ngx_http_file_cache_header_t), 0);
-    if (n == NGX_ERROR) {
-        return NGX_ERROR;
-    }
-
-    if ((size_t) n < sizeof(ngx_http_file_cache_header_t)) {
+    if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) {
         ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,
                       "cache file \"%s\" is too small", name->data);
         return NGX_ERROR;
     }
 
+    ngx_memzero(&c, sizeof(ngx_http_cache_t));
     cache = ctx->data;
 
-    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
-                      ngx_fd_info_n " \"%s\" failed", name->data);
-
-    } else {
-        c.uniq = ngx_file_uniq(&fi);
-        c.valid_sec = h.valid_sec;
-        c.valid_msec = h.valid_msec;
-        c.body_start = h.body_start;
-        c.length = ngx_file_size(&fi);
-        c.fs_size = (ngx_file_fs_size(&fi) + cache->bsize - 1) / cache->bsize;
-    }
-
-    if (ngx_close_file(fd) == NGX_FILE_ERROR) {
-        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,
-                      ngx_close_file_n " \"%s\" failed", name->data);
-    }
-
-    if (c.body_start == 0) {
-        return NGX_ERROR;
-    }
+    c.length = ctx->size;
+    c.fs_size = (ctx->fs_size + cache->bsize - 1) / cache->bsize;
 
     p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];
 
@@ -1435,14 +1394,14 @@ ngx_http_file_cache_add(ngx_http_file_ca
 
         fcn->uses = 1;
         fcn->count = 0;
-        fcn->valid_msec = c->valid_msec;
+        fcn->valid_msec = 0;
         fcn->error = 0;
         fcn->exists = 1;
         fcn->updating = 0;
         fcn->deleting = 0;
-        fcn->uniq = c->uniq;
-        fcn->valid_sec = c->valid_sec;
-        fcn->body_start = c->body_start;
+        fcn->uniq = 0;
+        fcn->valid_sec = 0;
+        fcn->body_start = 0;
         fcn->fs_size = c->fs_size;
 
         cache->sh->size += c->fs_size;
@@ -1510,6 +1469,7 @@ ngx_http_file_cache_set_slot(ngx_conf_t 
     time_t                  inactive;
     ssize_t                 size;
     ngx_str_t               s, name, *value;
+    ngx_int_t               loader_files, loader_sleep, loader_threshold;
     ngx_uint_t              i, n;
     ngx_http_file_cache_t  *cache;
 
@@ -1524,6 +1484,9 @@ ngx_http_file_cache_set_slot(ngx_conf_t 
     }
 
     inactive = 600;
+    loader_files = 100;
+    loader_sleep = 50;
+    loader_threshold = 200;
 
     name.len = 0;
     size = 0;
@@ -1637,6 +1600,48 @@ ngx_http_file_cache_set_slot(ngx_conf_t 
             continue;
         }
 
+        if (ngx_strncmp(value[i].data, "loader_files=", 13) == 0) {
+
+            loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);
+            if (loader_files == NGX_ERROR) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid loader_files value \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "loader_sleep=", 13) == 0) {
+
+            s.len = value[i].len - 13;
+            s.data = value[i].data + 13;
+
+            loader_sleep = ngx_parse_time(&s, 0);
+            if (loader_sleep < 0) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid loader_sleep value \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
+        if (ngx_strncmp(value[i].data, "loader_threshold=", 17) == 0) {
+
+            s.len = value[i].len - 17;
+            s.data = value[i].data + 17;
+
+            loader_threshold = ngx_parse_time(&s, 0);
+            if (loader_threshold < 0) {
+                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                           "invalid loader_threshold value \"%V\"", &value[i]);
+                return NGX_CONF_ERROR;
+            }
+
+            continue;
+        }
+
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "invalid parameter \"%V\"", &value[i]);
         return NGX_CONF_ERROR;
@@ -1652,6 +1657,11 @@ ngx_http_file_cache_set_slot(ngx_conf_t 
     cache->path->manager = ngx_http_file_cache_manager;
     cache->path->loader = ngx_http_file_cache_loader;
     cache->path->data = cache;
+    cache->path->conf_file = cf->conf_file->file.name.data;
+    cache->path->line = cf->conf_file->line;
+    cache->loader_files = loader_files;
+    cache->loader_sleep = (ngx_msec_t) loader_sleep;
+    cache->loader_threshold = (ngx_msec_t) loader_threshold;
 
     if (ngx_add_path(cf, &cache->path) != NGX_OK) {
         return NGX_CONF_ERROR;
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1439,8 +1439,6 @@ ngx_http_process_user_agent(ngx_http_req
 
             switch (msie[5]) {
             case '4':
-                r->headers_in.msie4 = 1;
-                /* fall through */
             case '5':
                 r->headers_in.msie6 = 1;
                 break;
@@ -1463,7 +1461,6 @@ ngx_http_process_user_agent(ngx_http_req
     if (ngx_strstrn(user_agent, "Opera", 5 - 1)) {
         r->headers_in.opera = 1;
         r->headers_in.msie = 0;
-        r->headers_in.msie4 = 0;
         r->headers_in.msie6 = 0;
     }
 
@@ -2126,11 +2123,11 @@ ngx_http_finalize_connection(ngx_http_re
 
         if (r->discard_body) {
             r->read_event_handler = ngx_http_discarded_request_body_handler;
+            ngx_add_timer(r->connection->read, clcf->lingering_timeout);
 
             if (r->lingering_time == 0) {
                 r->lingering_time = ngx_time()
                                       + (time_t) (clcf->lingering_time / 1000);
-                ngx_add_timer(r->connection->read, clcf->lingering_timeout);
             }
         }
 
@@ -2145,8 +2142,14 @@ ngx_http_finalize_connection(ngx_http_re
     {
         ngx_http_set_keepalive(r);
         return;
-
-    } else if (r->lingering_close && clcf->lingering_timeout > 0) {
+    }
+
+    if (clcf->lingering_close == NGX_HTTP_LINGERING_ALWAYS
+        || (clcf->lingering_close == NGX_HTTP_LINGERING_ON
+            && (r->lingering_close
+                || r->header_in->pos < r->header_in->last
+                || r->connection->read->ready)))
+    {
         ngx_http_set_lingering_close(r);
         return;
     }
@@ -2772,7 +2775,6 @@ ngx_http_lingering_close_handler(ngx_eve
                    "http lingering close handler");
 
     if (rev->timedout) {
-        c->timedout = 1;
         ngx_http_close_request(r, 0);
         return;
     }
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -221,7 +221,6 @@ typedef struct {
 
     unsigned                          connection_type:2;
     unsigned                          msie:1;
-    unsigned                          msie4:1;
     unsigned                          msie6:1;
     unsigned                          opera:1;
     unsigned                          gecko:1;
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -661,10 +661,12 @@ ngx_http_upstream_cache(ngx_http_request
 
         ngx_http_file_cache_create_key(r);
 
-        if (r->cache->header_start >= u->conf->buffer_size) {
+        if (r->cache->header_start + 256 >= u->conf->buffer_size) {
             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                "cache key too large, increase upstream buffer size %uz",
-                u->conf->buffer_size);
+                          "%V_buffer_size %uz is not enough for cache key, "
+                          "it should increased at least to %uz",
+                          &u->conf->module, u->conf->buffer_size,
+                          ngx_align(r->cache->header_start + 256, 1024));
 
             r->cache = NULL;
             return NGX_DECLINED;
@@ -2317,7 +2319,7 @@ ngx_http_upstream_process_non_buffered_d
     if (wev->timedout) {
         c->timedout = 1;
         ngx_connection_error(c, NGX_ETIMEDOUT, "client timed out");
-        ngx_http_upstream_finalize_request(r, u, 0);
+        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);
         return;
     }
 
@@ -3013,6 +3015,7 @@ ngx_http_upstream_finalize_request(ngx_h
 #endif
 
     if (u->header_sent
+        && rc != NGX_HTTP_REQUEST_TIME_OUT
         && (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE))
     {
         rc = 0;
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -179,6 +179,7 @@ typedef struct {
     ngx_flag_t                       ssl_session_reuse;
 #endif
 
+    ngx_str_t                        module;
 } ngx_http_upstream_conf_t;
 
 
--- a/src/http/ngx_http_upstream_round_robin.c
+++ b/src/http/ngx_http_upstream_round_robin.c
@@ -14,6 +14,15 @@ static ngx_int_t ngx_http_upstream_cmp_s
 static ngx_uint_t
 ngx_http_upstream_get_peer(ngx_http_upstream_rr_peers_t *peers);
 
+#if (NGX_HTTP_SSL)
+
+static ngx_int_t ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc,
+    void *data);
+static void ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc,
+    void *data);
+
+#endif
+
 
 ngx_int_t
 ngx_http_upstream_init_round_robin(ngx_conf_t *cf,
@@ -343,10 +352,8 @@ ngx_http_upstream_create_round_robin_pee
     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.set_session =
-                               ngx_http_upstream_set_round_robin_peer_session;
-    r->upstream->peer.save_session =
-                               ngx_http_upstream_save_round_robin_peer_session;
+    r->upstream->peer.set_session = ngx_http_upstream_empty_set_session;
+    r->upstream->peer.save_session = ngx_http_upstream_empty_save_session;
 #endif
 
     return NGX_OK;
@@ -757,4 +764,18 @@ ngx_http_upstream_save_round_robin_peer_
     }
 }
 
+
+static ngx_int_t
+ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data)
+{
+    return NGX_OK;
+}
+
+
+static void
+ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data)
+{
+    return;
+}
+
 #endif
--- a/src/mail/ngx_mail_ssl_module.c
+++ b/src/mail/ngx_mail_ssl_module.c
@@ -9,7 +9,8 @@
 #include <ngx_mail.h>
 
 
-#define NGX_DEFAULT_CIPHERS  "HIGH:!aNULL:!MD5"
+#define NGX_DEFAULT_CIPHERS     "HIGH:!aNULL:!MD5"
+#define NGX_DEFAULT_ECDH_CURVE  "prime256v1"
 
 
 static void *ngx_mail_ssl_create_conf(ngx_conf_t *cf);
@@ -77,6 +78,13 @@ static ngx_command_t  ngx_mail_ssl_comma
       offsetof(ngx_mail_ssl_conf_t, dhparam),
       NULL },
 
+    { ngx_string("ssl_ecdh_curve"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_ssl_conf_t, ecdh_curve),
+      NULL },
+
     { ngx_string("ssl_protocols"),
       NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,
       ngx_conf_set_bitmask_slot,
@@ -163,6 +171,7 @@ ngx_mail_ssl_create_conf(ngx_conf_t *cf)
      *     scf->certificate = { 0, NULL };
      *     scf->certificate_key = { 0, NULL };
      *     scf->dhparam = { 0, NULL };
+     *     scf->ecdh_curve = { 0, NULL };
      *     scf->ciphers = { 0, NULL };
      *     scf->shm_zone = NULL;
      */
@@ -204,6 +213,9 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
 
     ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, "");
 
+    ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,
+                         NGX_DEFAULT_ECDH_CURVE);
+
     ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);
 
 
@@ -286,9 +298,7 @@ ngx_mail_ssl_merge_conf(ngx_conf_t *cf, 
         SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
     }
 
-    if (ngx_ssl_generate_rsa512_key(&conf->ssl) != NGX_OK) {
-        return NGX_CONF_ERROR;
-    }
+    SSL_CTX_set_tmp_rsa_callback(conf->ssl.ctx, ngx_ssl_rsa512_key_callback);
 
     if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {
         return NGX_CONF_ERROR;
--- a/src/mail/ngx_mail_ssl_module.h
+++ b/src/mail/ngx_mail_ssl_module.h
@@ -34,6 +34,7 @@ typedef struct {
     ngx_str_t        certificate;
     ngx_str_t        certificate_key;
     ngx_str_t        dhparam;
+    ngx_str_t        ecdh_curve;
 
     ngx_str_t        ciphers;
 
--- a/src/os/unix/ngx_files.h
+++ b/src/os/unix/ngx_files.h
@@ -253,6 +253,7 @@ ngx_de_info(u_char *name, ngx_dir_t *dir
 
 #define ngx_de_access(dir)       (((dir)->info.st_mode) & 0777)
 #define ngx_de_size(dir)         (dir)->info.st_size
+#define ngx_de_fs_size(dir)      ((dir)->info.st_blocks * 512)
 #define ngx_de_mtime(dir)        (dir)->info.st_mtime