changeset 484:ed5e10fb40fc NGINX_0_7_54

nginx 0.7.54 *) Feature: the ngx_http_image_filter_module. *) Feature: the "proxy_ignore_headers" and "fastcgi_ignore_headers" directives. *) Bugfix: a segmentation fault might occur in worker process, if an "open_file_cache_errors off" directive was used; the bug had appeared in 0.7.53. *) Bugfix: the "port_in_redirect off" directive did not work; the bug had appeared in 0.7.39. *) Bugfix: improve handling of "select" method errors. *) Bugfix: of "select() failed (10022: ...)" error in nginx/Windows. *) Bugfix: in error text descriptions in nginx/Windows; the bug had appeared in 0.7.53.
author Igor Sysoev <http://sysoev.ru>
date Fri, 01 May 2009 00:00:00 +0400
parents 0a2f4b42ddad
children 21824e8058e6
files CHANGES CHANGES.ru auto/lib/conf auto/lib/libgd/conf auto/modules auto/options auto/sources src/core/nginx.c src/core/nginx.h src/core/ngx_conf_file.c src/core/ngx_connection.c src/core/ngx_connection.h src/core/ngx_cycle.c src/core/ngx_cycle.h src/core/ngx_file.c src/core/ngx_log.c src/core/ngx_log.h src/core/ngx_open_file_cache.c src/core/ngx_resolver.c src/core/ngx_string.c src/core/ngx_string.h src/event/modules/ngx_select_module.c src/event/ngx_event.c src/event/ngx_event_openssl.c src/http/modules/ngx_http_dav_module.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_image_filter_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_xslt_filter_module.c src/http/modules/perl/nginx.pm src/http/ngx_http.c src/http/ngx_http.h src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_header_filter_module.c src/http/ngx_http_request.c src/http/ngx_http_special_response.c src/http/ngx_http_upstream.c src/http/ngx_http_upstream.h src/mail/ngx_mail.c src/os/unix/ngx_files.h
diffstat 41 files changed, 1646 insertions(+), 234 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES	Mon Apr 27 00:00:00 2009 +0400
+++ b/CHANGES	Fri May 01 00:00:00 2009 +0400
@@ -1,4 +1,26 @@
 
+Changes with nginx 0.7.54                                        01 May 2009
+
+    *) Feature: the ngx_http_image_filter_module.
+
+    *) Feature: the "proxy_ignore_headers" and "fastcgi_ignore_headers" 
+       directives.
+
+    *) Bugfix: a segmentation fault might occur in worker process, if an 
+       "open_file_cache_errors off" directive was used; the bug had 
+       appeared in 0.7.53.
+
+    *) Bugfix: the "port_in_redirect off" directive did not work; the bug 
+       had appeared in 0.7.39.
+
+    *) Bugfix: improve handling of "select" method errors.
+
+    *) Bugfix: of "select() failed (10022: ...)" error in nginx/Windows.
+
+    *) Bugfix: in error text descriptions in nginx/Windows; the bug had 
+       appeared in 0.7.53.
+
+
 Changes with nginx 0.7.53                                        27 Apr 2009
 
     *) Change: now a log set by --error-log-path is created from the very 
--- a/CHANGES.ru	Mon Apr 27 00:00:00 2009 +0400
+++ b/CHANGES.ru	Fri May 01 00:00:00 2009 +0400
@@ -1,4 +1,25 @@
 
+Изменения в nginx 0.7.54                                          01.05.2009
+
+    *) Добавление: модуль ngx_http_image_filter_module.
+
+    *) Добавление: директивы proxy_ignore_headers и fastcgi_ignore_headers.
+
+    *) Исправление: при использовании переменных "open_file_cache_errors 
+       on" в рабочем процессе мог произойти segmentation fault; ошибка 
+       появилась в 0.7.53.
+
+    *) Исправление: директива "port_in_redirect off" не работала; ошибка 
+       появилась в 0.7.39.
+
+    *) Исправление: улучшение обработки ошибок метода select.
+
+    *) Исправление: ошибки "select() failed (10022: ...)" в nginx/Windows.
+
+    *) Исправление: в текстовых сообщениях об ошибках в nginx/Windows; 
+       ошибка появилась в 0.7.53.
+
+
 Изменения в nginx 0.7.53                                          27.04.2009
 
     *) Изменение: теперь лог, указанный в --error-log-path, создаётся с 
--- a/auto/lib/conf	Mon Apr 27 00:00:00 2009 +0400
+++ b/auto/lib/conf	Fri May 01 00:00:00 2009 +0400
@@ -45,6 +45,10 @@
     . auto/lib/libxslt/conf
 fi
 
+if [ $USE_LIBGD = YES ]; then
+    . auto/lib/libgd/conf
+fi
+
 if [ $USE_PERL = YES ]; then
     . auto/lib/perl/conf
 fi
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/auto/lib/libgd/conf	Fri May 01 00:00:00 2009 +0400
@@ -0,0 +1,81 @@
+
+# Copyright (C) Igor Sysoev
+
+
+    ngx_feature="GD library"
+    ngx_feature_name=
+    ngx_feature_run=no
+    ngx_feature_incs="#include <gd.h>"
+    ngx_feature_path=
+    ngx_feature_libs="-lgd"
+    ngx_feature_test="gdImagePtr img = gdImageCreateFromGifPtr(1, NULL);"
+    . auto/feature
+
+
+if [ $ngx_found = no ]; then
+
+    # FreeBSD port
+
+    ngx_feature="GD library in /usr/local/"
+    ngx_feature_path="/usr/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/local/lib -L/usr/local/lib -lgd"
+    else
+        ngx_feature_libs="-L/usr/local/lib -lgd"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # NetBSD port
+
+    ngx_feature="GD library in /usr/pkg/"
+    ngx_feature_path="/usr/pkg/include/"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/usr/pkg/lib -L/usr/pkg/lib -lgd"
+    else
+        ngx_feature_libs="-L/usr/pkg/lib -lgd"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = no ]; then
+
+    # MacPorts
+
+    ngx_feature="GD library in /opt/local/"
+    ngx_feature_path="/opt/local/include"
+
+    if [ $NGX_RPATH = YES ]; then
+        ngx_feature_libs="-R/opt/local/lib -L/opt/local/lib -lgd"
+    else
+        ngx_feature_libs="-L/opt/local/lib -lgd"
+    fi
+
+    . auto/feature
+fi
+
+
+if [ $ngx_found = yes ]; then
+
+    CORE_LIBS="$CORE_LIBS $ngx_feature_libs"
+
+else
+
+cat << END
+
+$0: error: the HTTP image filter module requires the GD library.
+You can either do not enable the module or install the libraries.
+
+END
+
+    exit 1
+
+fi
--- a/auto/modules	Mon Apr 27 00:00:00 2009 +0400
+++ b/auto/modules	Fri May 01 00:00:00 2009 +0400
@@ -104,6 +104,7 @@
 #     ngx_http_charset_filter
 #     ngx_http_ssi_filter
 #         ngx_http_xslt_filter
+#         ngx_http_image_filter_filter
 #         ngx_http_sub_filter
 #         ngx_http_addition_filter
 #         ngx_http_userid_filter
@@ -148,6 +149,12 @@
     HTTP_SRCS="$HTTP_SRCS $HTTP_XSLT_SRCS"
 fi
 
+if [ $HTTP_IMAGE_FILTER = YES ]; then
+    USE_LIBGD=YES
+    HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_IMAGE_FILTER_MODULE"
+    HTTP_SRCS="$HTTP_SRCS $HTTP_IMAGE_SRCS"
+fi
+
 if [ $HTTP_SUB = YES ]; then
     HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SUB_FILTER_MODULE"
     HTTP_SRCS="$HTTP_SRCS $HTTP_SUB_SRCS"
--- a/auto/options	Mon Apr 27 00:00:00 2009 +0400
+++ b/auto/options	Fri May 01 00:00:00 2009 +0400
@@ -60,6 +60,7 @@
 HTTP_POSTPONE=NO
 HTTP_REALIP=NO
 HTTP_XSLT=NO
+HTTP_IMAGE_FILTER=NO
 HTTP_SUB=NO
 HTTP_ADDITION=NO
 HTTP_DAV=NO
@@ -123,6 +124,7 @@
 NGX_PERL=perl
 
 USE_LIBXSLT=NO
+USE_LIBGD=NO
 
 NGX_GOOGLE_PERFTOOLS=NO
 NGX_CPP_TEST=NO
@@ -181,6 +183,7 @@
         --with-http_realip_module)       HTTP_REALIP=YES            ;;
         --with-http_addition_module)     HTTP_ADDITION=YES          ;;
         --with-http_xslt_module)         HTTP_XSLT=YES              ;;
+        --with-http_image_filter_module) HTTP_IMAGE_FILTER=YES      ;;
         --with-http_sub_module)          HTTP_SUB=YES               ;;
         --with-http_dav_module)          HTTP_DAV=YES               ;;
         --with-http_flv_module)          HTTP_FLV=YES               ;;
--- a/auto/sources	Mon Apr 27 00:00:00 2009 +0400
+++ b/auto/sources	Fri May 01 00:00:00 2009 +0400
@@ -328,6 +328,10 @@
 HTTP_XSLT_SRCS=src/http/modules/ngx_http_xslt_filter_module.c
 
 
+HTTP_IMAGE_FILTER_MODULE=ngx_http_image_filter_module
+HTTP_IMAGE_SRCS=src/http/modules/ngx_http_image_filter_module.c
+
+
 HTTP_SUB_FILTER_MODULE=ngx_http_sub_filter_module
 HTTP_SUB_SRCS=src/http/modules/ngx_http_sub_filter_module.c
 
--- a/src/core/nginx.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/nginx.c	Fri May 01 00:00:00 2009 +0400
@@ -213,7 +213,7 @@
         if (ngx_show_help) {
             ngx_log_stderr(0,
                 "Usage: nginx [-?hvVt] [-s signal] [-c filename] "
-                             "[-g directives]" CRLF CRLF
+                             "[-p prefix] [-g directives]" CRLF CRLF
                 "Options:" CRLF
                 "  -?,-h         : this help" CRLF
                 "  -v            : show version and exit" CRLF
--- a/src/core/nginx.h	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/nginx.h	Fri May 01 00:00:00 2009 +0400
@@ -8,8 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version         7053
-#define NGINX_VERSION      "0.7.53"
+#define nginx_version         7054
+#define NGINX_VERSION      "0.7.54"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_conf_file.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_conf_file.c	Fri May 01 00:00:00 2009 +0400
@@ -61,6 +61,7 @@
 char *
 ngx_conf_param(ngx_conf_t *cf)
 {
+    char             *rv;
     ngx_str_t        *param;
     ngx_buf_t         b;
     ngx_conf_file_t   conf_file;
@@ -82,13 +83,17 @@
     b.temporary = 1;
 
     conf_file.file.fd = NGX_INVALID_FILE;
-    conf_file.file.name.data = (u_char *) "command line";
-    conf_file.line = 1;
+    conf_file.file.name.data = NULL;
+    conf_file.line = 0;
 
     cf->conf_file = &conf_file;
     cf->conf_file->buffer = &b;
 
-    return ngx_conf_parse(cf, NULL);
+    rv = ngx_conf_parse(cf, NULL);
+
+    cf->conf_file = NULL;
+
+    return rv;
 }
 
 
@@ -853,7 +858,7 @@
     full.data = NULL;
 #endif
 
-    if (name && name->len) {
+    if (name->len) {
         full = *name;
 
         if (ngx_conf_full_name(cycle, &full, 0) != NGX_OK) {
@@ -889,14 +894,13 @@
         return NULL;
     }
 
-    if (name && name->len) {
+    if (name->len) {
         file->fd = NGX_INVALID_FILE;
         file->name = full;
 
     } else {
         file->fd = ngx_stderr;
-        file->name.len = 0;
-        file->name.data = NULL;
+        file->name = *name;
     }
 
     file->buffer = NULL;
@@ -961,31 +965,11 @@
     last = errstr + NGX_MAX_CONF_ERRSTR;
 
     va_start(args, fmt);
-    p = ngx_vsnprintf(errstr, last - errstr, fmt, args);
+    p = ngx_vslprintf(errstr, last, fmt, args);
     va_end(args);
 
     if (err) {
-
-        if (p > last - 50) {
-
-            /* leave a space for an error code */
-
-            p = last - 50;
-            *p++ = '.';
-            *p++ = '.';
-            *p++ = '.';
-        }
-
-#if (NGX_WIN32)
-        p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000)
-                                           ? " (%d: " : " (%Xd: ", err);
-#else
-        p = ngx_snprintf(p, last - p, " (%d: ", err);
-#endif
-
-        p = ngx_strerror_r(err, p, last - p);
-
-        *p++ = ')';
+        p = ngx_log_errno(p, last, err);
     }
 
     if (cf->conf_file == NULL) {
@@ -993,6 +977,12 @@
         return;
     }
 
+    if (cf->conf_file->file.fd == NGX_INVALID_FILE) {
+        ngx_log_error(level, cf->log, 0, "%*s in command line",
+                      p - errstr, errstr);
+        return;
+    }
+
     ngx_log_error(level, cf->log, 0, "%*s in %s:%ui",
                   p - errstr, errstr,
                   cf->conf_file->file.name.data, cf->conf_file->line);
--- a/src/core/ngx_connection.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_connection.c	Fri May 01 00:00:00 2009 +0400
@@ -248,6 +248,8 @@
                 continue;
             }
 
+            ls[i].log = *ls[i].logp;
+
             if (ls[i].inherited) {
 
                 /* TODO: close on exit */
@@ -801,7 +803,14 @@
 {
     ngx_uint_t  level;
 
-    if (err == NGX_ECONNRESET && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) {
+    /* Winsock may return NGX_ECONNABORTED instead of NGX_ECONNRESET */
+
+    if ((err == NGX_ECONNRESET
+#if (NGX_WIN32)
+         || err == NGX_ECONNABORTED
+#endif
+        ) && c->log_error == NGX_ERROR_IGNORE_ECONNRESET)
+    {
         return 0;
     }
 
@@ -813,7 +822,9 @@
 
     if (err == 0
         || err == NGX_ECONNRESET
-#if !(NGX_WIN32)
+#if (NGX_WIN32)
+        || err == NGX_ECONNABORTED
+#else
         || err == NGX_EPIPE
 #endif
         || err == NGX_ENOTCONN
--- a/src/core/ngx_connection.h	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_connection.h	Fri May 01 00:00:00 2009 +0400
@@ -34,6 +34,7 @@
     void               *servers;  /* array of ngx_http_in_addr_t, for example */
 
     ngx_log_t           log;
+    ngx_log_t          *logp;
 
     size_t              pool_size;
     /* should be here because of the AcceptEx() preread */
--- a/src/core/ngx_cycle.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_cycle.c	Fri May 01 00:00:00 2009 +0400
@@ -82,6 +82,7 @@
 
     cycle->pool = pool;
     cycle->log = log;
+    cycle->new_log.log_level = NGX_LOG_ERR;
     cycle->old_cycle = old_cycle;
 
     cycle->conf_prefix.len = old_cycle->conf_prefix.len;
@@ -165,14 +166,6 @@
         return NULL;
     }
 
-
-    cycle->new_log = ngx_log_create_errlog(cycle, &error_log);
-    if (cycle->new_log == NULL) {
-        ngx_destroy_pool(pool);
-        return NULL;
-    }
-
-
     n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;
 
     cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t));
@@ -336,6 +329,13 @@
     }
 
 
+    if (cycle->new_log.file == NULL) {
+        cycle->new_log.file = ngx_conf_open_file(cycle, &error_log);
+        if (cycle->new_log.file == NULL) {
+            goto failed;
+        }
+    }
+
     /* open the new files */
 
     part = &cycle->open_files.part;
@@ -382,12 +382,8 @@
 #endif
     }
 
-    cycle->log = cycle->new_log;
-    pool->log = cycle->new_log;
-
-    if (cycle->log->log_level == 0) {
-        cycle->log->log_level = NGX_LOG_ERR;
-    }
+    cycle->log = &cycle->new_log;
+    pool->log = &cycle->new_log;
 
 
     /* create shared memory */
@@ -1126,7 +1122,9 @@
         if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) {
             ngx_file_info_t  fi;
 
-            if (ngx_file_info((const char *) file[i].name.data, &fi) == -1) {
+            if (ngx_file_info((const char *) file[i].name.data, &fi)
+                == NGX_FILE_ERROR)
+            {
                 ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                               ngx_file_info_n " \"%s\" failed",
                               file[i].name.data);
--- a/src/core/ngx_cycle.h	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_cycle.h	Fri May 01 00:00:00 2009 +0400
@@ -38,7 +38,7 @@
     ngx_pool_t               *pool;
 
     ngx_log_t                *log;
-    ngx_log_t                *new_log;
+    ngx_log_t                 new_log;
 
     ngx_connection_t        **files;
     ngx_connection_t         *free_connections;
--- a/src/core/ngx_file.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_file.c	Fri May 01 00:00:00 2009 +0400
@@ -489,7 +489,9 @@
         {
         ngx_file_info_t   fi;
 
-        if (ngx_file_info((const char *) path[i]->name.data, &fi) == -1) {
+        if (ngx_file_info((const char *) path[i]->name.data, &fi)
+            == NGX_FILE_ERROR)
+        {
             ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                           ngx_file_info_n " \"%s\" failed", path[i]->name.data);
             return NGX_ERROR;
--- a/src/core/ngx_log.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_log.c	Fri May 01 00:00:00 2009 +0400
@@ -8,14 +8,14 @@
 #include <ngx_core.h>
 
 
-static char *ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
 
 static ngx_command_t  ngx_errlog_commands[] = {
 
     {ngx_string("error_log"),
      NGX_MAIN_CONF|NGX_CONF_1MORE,
-     ngx_set_error_log,
+     ngx_error_log,
      0,
      0,
      NULL},
@@ -53,7 +53,7 @@
 
 
 static ngx_str_t err_levels[] = {
-    ngx_string("stderr"),
+    ngx_null_string,
     ngx_string("emerg"),
     ngx_string("alert"),
     ngx_string("crit"),
@@ -101,14 +101,14 @@
 
     p = errstr + ngx_cached_err_log_time.len;
 
-    p = ngx_snprintf(p, last - p, " [%V] ", &err_levels[level]);
+    p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]);
 
     /* pid#tid */
-    p = ngx_snprintf(p, last - p, "%P#" NGX_TID_T_FMT ": ",
+    p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ",
                     ngx_log_pid, ngx_log_tid);
 
     if (log->connection) {
-        p = ngx_snprintf(p, last - p, "*%uA ", log->connection);
+        p = ngx_slprintf(p, last, "*%uA ", log->connection);
     }
 
     msg = p;
@@ -116,39 +116,17 @@
 #if (NGX_HAVE_VARIADIC_MACROS)
 
     va_start(args, fmt);
-    p = ngx_vsnprintf(p, last - p, fmt, args);
+    p = ngx_vslprintf(p, last, fmt, args);
     va_end(args);
 
 #else
 
-    p = ngx_vsnprintf(p, last - p, fmt, args);
+    p = ngx_vslprintf(p, last, fmt, args);
 
 #endif
 
     if (err) {
-
-        if (p > last - 50) {
-
-            /* leave a space for an error code */
-
-            p = last - 50;
-            *p++ = '.';
-            *p++ = '.';
-            *p++ = '.';
-        }
-
-#if (NGX_WIN32)
-        p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000)
-                                           ? " (%d: " : " (%Xd: ", err);
-#else
-        p = ngx_snprintf(p, last - p, " (%d: ", err);
-#endif
-
-        p = ngx_strerror_r(err, p, last - p);
-
-        if (p < last) {
-            *p++ = ')';
-        }
+        p = ngx_log_errno(p, last, err);
     }
 
     if (level != NGX_LOG_DEBUG && log->handler) {
@@ -174,7 +152,7 @@
 
     (void) ngx_sprintf(msg, "[%V]: ", &err_levels[level]);
 
-    (void) ngx_write_fd(ngx_stderr, msg, p - msg);
+    (void) ngx_write_console(ngx_stderr, msg, p - msg);
 }
 
 
@@ -230,45 +208,53 @@
     va_list   args;
     u_char    errstr[NGX_MAX_ERROR_STR];
 
-    va_start(args, fmt);
-    p = ngx_vsnprintf(errstr, NGX_MAX_ERROR_STR, fmt, args);
-    va_end(args);
+    last = errstr + NGX_MAX_ERROR_STR;
 
-    if (p > errstr + NGX_MAX_ERROR_STR - NGX_LINEFEED_SIZE) {
-        p = errstr + NGX_MAX_ERROR_STR - NGX_LINEFEED_SIZE;
-    }
+    va_start(args, fmt);
+    p = ngx_vslprintf(errstr, last, fmt, args);
+    va_end(args);
 
     if (err) {
-
-        last = errstr + NGX_MAX_ERROR_STR;
-
-        if (p > last - 50) {
-
-            /* leave a space for an error code */
-
-            p = last - 50;
-            *p++ = '.';
-            *p++ = '.';
-            *p++ = '.';
-        }
+        p = ngx_log_errno(p, last, err);
+    }
 
-#if (NGX_WIN32)
-        p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000)
-                                           ? " (%d: " : " (%Xd: ", err);
-#else
-        p = ngx_snprintf(p, last - p, " (%d: ", err);
-#endif
-
-        p = ngx_strerror_r(err, p, last - p);
-
-        if (p < last) {
-            *p++ = ')';
-        }
+    if (p > last - NGX_LINEFEED_SIZE) {
+        p = last - NGX_LINEFEED_SIZE;
     }
 
     ngx_linefeed(p);
 
-    (void) ngx_write_fd(ngx_stderr, errstr, p - errstr);
+    (void) ngx_write_console(ngx_stderr, errstr, p - errstr);
+}
+
+
+u_char *
+ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err)
+{
+    if (buf > last - 50) {
+
+        /* leave a space for an error code */
+
+        buf = last - 50;
+        *buf++ = '.';
+        *buf++ = '.';
+        *buf++ = '.';
+    }
+
+#if (NGX_WIN32)
+    buf = ngx_slprintf(buf, last, ((unsigned) err < 0x80000000)
+                                       ? " (%d: " : " (%Xd: ", err);
+#else
+    buf = ngx_slprintf(buf, last, " (%d: ", err);
+#endif
+
+    buf = ngx_strerror_r(err, buf, last - buf);
+
+    if (buf < last) {
+        *buf++ = ')';
+    }
+
+    return buf;
 }
 
 
@@ -302,15 +288,16 @@
 #else
     if (name[0] != '/') {
 #endif
-        plen = 0;
 
         if (prefix) {
             plen = ngx_strlen(prefix);
 
+        } else {
 #ifdef NGX_PREFIX
-        } else {
             prefix = (u_char *) NGX_PREFIX;
             plen = ngx_strlen(prefix);
+#else
+            plen = 0;
 #endif
         }
 
@@ -358,7 +345,7 @@
 
 
 ngx_log_t *
-ngx_log_create_errlog(ngx_cycle_t *cycle, ngx_str_t *name)
+ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name)
 {
     ngx_log_t  *log;
 
@@ -377,7 +364,7 @@
 
 
 char *
-ngx_set_error_log_levels(ngx_conf_t *cf, ngx_log_t *log)
+ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log)
 {
     ngx_uint_t   i, n, d;
     ngx_str_t   *value;
@@ -422,10 +409,7 @@
         }
     }
 
-    if (log->log_level == 0) {
-        log->log_level = NGX_LOG_ERR;
-
-    } else if (log->log_level == NGX_LOG_DEBUG) {
+    if (log->log_level == NGX_LOG_DEBUG) {
         log->log_level = NGX_LOG_DEBUG_ALL;
     }
 
@@ -434,26 +418,35 @@
 
 
 static char *
-ngx_set_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    ngx_str_t  *value;
+    ngx_str_t  *value, name;
+
+    if (cf->cycle->new_log.file) {
+        return "is duplicate";
+    }
 
     value = cf->args->elts;
 
-    if (value[1].len == 6 && ngx_strcmp(value[1].data, "stderr") == 0) {
-        cf->cycle->new_log->file->fd = ngx_stderr;
-        cf->cycle->new_log->file->name.len = 0;
-        cf->cycle->new_log->file->name.data = NULL;
+    if (ngx_strcmp(value[1].data, "stderr") == 0) {
+        name.len = 0;
+        name.data = NULL;
 
     } else {
-        cf->cycle->new_log->file->name = value[1];
+        name = value[1];
+    }
 
-        if (ngx_conf_full_name(cf->cycle, &cf->cycle->new_log->file->name, 0)
-            != NGX_OK)
-        {
-            return NGX_CONF_ERROR;
-        }
+    cf->cycle->new_log.file = ngx_conf_open_file(cf->cycle, &name);
+    if (cf->cycle->new_log.file == NULL) {
+        return NULL;
     }
 
-    return ngx_set_error_log_levels(cf, cf->cycle->new_log);
+    if (cf->args->nelts == 2) {
+        cf->cycle->new_log.log_level = NGX_LOG_ERR;
+        return NGX_CONF_OK;
+    }
+
+    cf->cycle->new_log.log_level = 0;
+
+    return ngx_log_set_levels(cf, &cf->cycle->new_log);
 }
--- a/src/core/ngx_log.h	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_log.h	Fri May 01 00:00:00 2009 +0400
@@ -196,10 +196,11 @@
 /*********************************/
 
 ngx_log_t *ngx_log_init(u_char *prefix);
-ngx_log_t *ngx_log_create_errlog(ngx_cycle_t *cycle, ngx_str_t *name);
-char *ngx_set_error_log_levels(ngx_conf_t *cf, ngx_log_t *log);
+ngx_log_t *ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name);
+char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log);
 void ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...);
 void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...);
+u_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err);
 
 
 extern ngx_module_t  ngx_errlog_module;
--- a/src/core/ngx_open_file_cache.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_open_file_cache.c	Fri May 01 00:00:00 2009 +0400
@@ -143,7 +143,7 @@
 
         if (of->test_only) {
 
-            if (ngx_file_info(name->data, &fi) == -1) {
+            if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
                 of->err = ngx_errno;
                 of->failed = ngx_file_info_n;
                 return NGX_ERROR;
@@ -234,6 +234,7 @@
 
             } else {
                 of->err = file->err;
+                of->failed = ngx_open_file_n;
             }
 
             goto found;
@@ -463,7 +464,7 @@
 
     if (of->fd != NGX_INVALID_FILE) {
 
-        if (ngx_file_info(name, &fi) == -1) {
+        if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
             of->failed = ngx_file_info_n;
             goto failed;
         }
@@ -474,7 +475,7 @@
 
     } else if (of->test_dir) {
 
-        if (ngx_file_info(name, &fi) == -1) {
+        if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
             of->failed = ngx_file_info_n;
             goto failed;
         }
--- a/src/core/ngx_resolver.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_resolver.c	Fri May 01 00:00:00 2009 +0400
@@ -131,14 +131,14 @@
 
     r->event->handler = ngx_resolver_resend_handler;
     r->event->data = r;
-    r->event->log = cf->cycle->new_log;
+    r->event->log = &cf->cycle->new_log;
     r->ident = -1;
 
     r->resend_timeout = 5;
     r->expire = 30;
     r->valid = 300;
 
-    r->log = cf->cycle->new_log;
+    r->log = &cf->cycle->new_log;
     r->log_level = NGX_LOG_ALERT;
 
     if (addr) {
@@ -152,7 +152,7 @@
         uc->sockaddr = addr->sockaddr;
         uc->socklen = addr->socklen;
         uc->server = addr->name;
-        uc->log = cf->cycle->new_log;
+        uc->log = &cf->cycle->new_log;
     }
 
     return r;
--- a/src/core/ngx_string.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_string.c	Fri May 01 00:00:00 2009 +0400
@@ -99,7 +99,7 @@
     va_list   args;
 
     va_start(args, fmt);
-    p = ngx_vsnprintf(buf, /* STUB */ 65536, fmt, args);
+    p = ngx_vslprintf(buf, (void *) -1, fmt, args);
     va_end(args);
 
     return p;
@@ -113,7 +113,21 @@
     va_list   args;
 
     va_start(args, fmt);
-    p = ngx_vsnprintf(buf, max, fmt, args);
+    p = ngx_vslprintf(buf, buf + max, fmt, args);
+    va_end(args);
+
+    return p;
+}
+
+
+u_char * ngx_cdecl
+ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...)
+{
+    u_char   *p;
+    va_list   args;
+
+    va_start(args, fmt);
+    p = ngx_vslprintf(buf, last, fmt, args);
     va_end(args);
 
     return p;
@@ -121,9 +135,9 @@
 
 
 u_char *
-ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args)
+ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
 {
-    u_char                *p, zero, *last;
+    u_char                *p, zero;
     int                    d;
     float                  f, scale;
     size_t                 len, slen;
@@ -134,12 +148,6 @@
     ngx_str_t             *v;
     ngx_variable_value_t  *vv;
 
-    if (max == 0) {
-        return buf;
-    }
-
-    last = buf + max;
-
     while (*fmt && buf < last) {
 
         /*
--- a/src/core/ngx_string.h	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/core/ngx_string.h	Fri May 01 00:00:00 2009 +0400
@@ -140,7 +140,11 @@
 u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src);
 u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...);
 u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...);
-u_char *ngx_vsnprintf(u_char *buf, size_t max, const char *fmt, va_list args);
+u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt,
+    ...);
+u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args);
+#define ngx_vsnprintf(buf, max, fmt, args)                                   \
+    ngx_vslprintf(buf, buf + (max), fmt, args)
 
 ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2);
 ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);
--- a/src/event/modules/ngx_select_module.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/event/modules/ngx_select_module.c	Fri May 01 00:00:00 2009 +0400
@@ -18,6 +18,7 @@
     ngx_uint_t flags);
 static ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
     ngx_uint_t flags);
+static void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);
 static char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);
 
 
@@ -248,12 +249,12 @@
 ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
     ngx_uint_t flags)
 {
-    int                 ready, nready;
-    ngx_uint_t          i, found;
-    ngx_err_t           err;
-    ngx_event_t        *ev, **queue;
-    ngx_connection_t   *c;
-    struct timeval      tv, *tp;
+    int                ready, nready;
+    ngx_err_t          err;
+    ngx_uint_t         i, found;
+    ngx_event_t       *ev, **queue;
+    struct timeval     tv, *tp;
+    ngx_connection_t  *c;
 
 #if !(NGX_WIN32)
 
@@ -302,19 +303,23 @@
     work_read_fd_set = master_read_fd_set;
     work_write_fd_set = master_write_fd_set;
 
-#if 1
-    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
-                   /*
-                    * (void *) disables "dereferencing type-punned
-                    * pointer will break strict-aliasing rules
-                    */
-                   "select read fd_set: %08Xd",
-                   *(int *) (void *) &work_read_fd_set);
-#endif
-
 #if (NGX_WIN32)
 
-    ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp);
+    if (max_read || max_write) {
+        ready = select(0, &work_read_fd_set, &work_write_fd_set, NULL, tp);
+
+    } else {
+
+        /*
+         * Winsock select() requires that at least one descriptor set must be
+         * be non-null, and any non-null descriptor set must contain at least
+         * one handle to a socket.  Otherwise select() returns WSAEINVAL.
+         */
+
+        ngx_msleep(timer);
+
+        ready = 0;
+    }
 
 #else
 
@@ -339,6 +344,11 @@
 
     if (err) {
         ngx_log_error(NGX_LOG_ALERT, cycle->log, err, "select() failed");
+
+        if (err == WSAENOTSOCK) {
+            ngx_select_repair_fd_sets(cycle);
+        }
+
         return NGX_ERROR;
     }
 
@@ -361,6 +371,11 @@
         }
 
         ngx_log_error(level, cycle->log, err, "select() failed");
+
+        if (err == EBADF) {
+            ngx_select_repair_fd_sets(cycle);
+        }
+
         return NGX_ERROR;
     }
 
@@ -414,13 +429,101 @@
     ngx_mutex_unlock(ngx_posted_events_mutex);
 
     if (ready != nready) {
-        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "select ready != events");
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                      "select ready != events: %d:%d", ready, nready);
+
+        ngx_select_repair_fd_sets(cycle);
     }
 
     return NGX_OK;
 }
 
 
+static void
+ngx_select_repair_fd_sets(ngx_cycle_t *cycle)
+{
+    int           n;
+    socklen_t     len;
+    ngx_err_t     err;
+    ngx_socket_t  s;
+
+#if (NGX_WIN32)
+    u_int         i;
+
+    for (i = 0; i < master_read_fd_set.fd_count; i++) {
+
+        s = master_read_fd_set.fd_array[i];
+        len = sizeof(int);
+
+        if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {
+            err = ngx_socket_errno;
+
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+                          "invalid descriptor #%d in read fd_set", s);
+
+            FD_CLR(s, &master_read_fd_set);
+        }
+    }
+
+    for (i = 0; i < master_write_fd_set.fd_count; i++) {
+
+        s = master_write_fd_set.fd_array[i];
+        len = sizeof(int);
+
+        if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {
+            err = ngx_socket_errno;
+
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+                          "invalid descriptor #%d in write fd_set", s);
+
+            FD_CLR(s, &master_write_fd_set);
+        }
+    }
+
+#else
+
+    for (s = 0; s <= max_fd; s++) {
+
+        if (FD_ISSET(s, &master_read_fd_set) == 0) {
+            continue;
+        }
+
+        len = sizeof(int);
+
+        if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {
+            err = ngx_socket_errno;
+
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+                          "invalid descriptor #%d in read fd_set", s);
+
+            FD_CLR(s, &master_read_fd_set);
+        }
+    }
+
+    for (s = 0; s <= max_fd; s++) {
+
+        if (FD_ISSET(s, &master_write_fd_set) == 0) {
+            continue;
+        }
+
+        len = sizeof(int);
+
+        if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {
+            err = ngx_socket_errno;
+
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
+                          "invalid descriptor #%d in write fd_set", s);
+
+            FD_CLR(s, &master_write_fd_set);
+        }
+    }
+
+    max_fd = -1;
+
+#endif
+}
+
+
 static char *
 ngx_select_init_conf(ngx_cycle_t *cycle, void *conf)
 {
--- a/src/event/ngx_event.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/event/ngx_event.c	Fri May 01 00:00:00 2009 +0400
@@ -776,6 +776,10 @@
 
             rev->handler = ngx_event_acceptex;
 
+            if (ngx_use_accept_mutex) {
+                continue;
+            }
+
             if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
                 return NGX_ERROR;
             }
@@ -792,6 +796,10 @@
         } else {
             rev->handler = ngx_event_accept;
 
+            if (ngx_use_accept_mutex) {
+                continue;
+            }
+
             if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
                 return NGX_ERROR;
             }
--- a/src/event/ngx_event_openssl.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/event/ngx_event_openssl.c	Fri May 01 00:00:00 2009 +0400
@@ -1306,7 +1306,7 @@
     last = errstr + NGX_MAX_CONF_ERRSTR;
 
     va_start(args, fmt);
-    p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args);
+    p = ngx_vslprintf(errstr, last - 1, fmt, args);
     va_end(args);
 
     p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);
--- a/src/http/modules/ngx_http_dav_module.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/modules/ngx_http_dav_module.c	Fri May 01 00:00:00 2009 +0400
@@ -221,7 +221,7 @@
 
     temp = &r->request_body->temp_file->file.name;
 
-    if (ngx_file_info(path.data, &fi) == -1) {
+    if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
         status = NGX_HTTP_CREATED;
 
     } else {
@@ -326,7 +326,7 @@
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http delete filename: \"%s\"", path.data);
 
-    if (ngx_file_info(path.data, &fi) == -1) {
+    if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
         err = ngx_errno;
 
         rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;
@@ -678,7 +678,7 @@
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http copy to: \"%s\"", copy.path.data);
 
-    if (ngx_file_info(copy.path.data, &fi) == -1) {
+    if (ngx_file_info(copy.path.data, &fi) == NGX_FILE_ERROR) {
         err = ngx_errno;
 
         if (err != NGX_ENOENT) {
@@ -712,7 +712,7 @@
         dir = ngx_is_dir(&fi);
     }
 
-    if (ngx_file_info(path.data, &fi) == -1) {
+    if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {
         return ngx_http_dav_error(r->connection->log, ngx_errno,
                                   NGX_HTTP_NOT_FOUND, ngx_file_info_n,
                                   path.data);
--- a/src/http/modules/ngx_http_fastcgi_module.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/modules/ngx_http_fastcgi_module.c	Fri May 01 00:00:00 2009 +0400
@@ -183,6 +183,15 @@
 };
 
 
+static ngx_conf_bitmask_t  ngx_http_fastcgi_ignore_headers_masks[] = {
+    { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT },
+    { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES },
+    { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES },
+    { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL },
+    { ngx_null_string, 0 }
+};
+
+
 ngx_module_t  ngx_http_fastcgi_module;
 
 
@@ -409,6 +418,13 @@
       offsetof(ngx_http_fastcgi_loc_conf_t, upstream.hide_headers),
       NULL },
 
+    { ngx_string("fastcgi_ignore_headers"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+      ngx_conf_set_bitmask_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_headers),
+      &ngx_http_fastcgi_ignore_headers_masks },
+
     { ngx_string("fastcgi_catch_stderr"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_str_array_slot,
@@ -1817,6 +1833,7 @@
      * set by ngx_pcalloc():
      *
      *     conf->upstream.bufs.num = 0;
+     *     conf->upstream.ignore_headers = 0;
      *     conf->upstream.next_upstream = 0;
      *     conf->upstream.use_stale_cache = 0;
      *     conf->upstream.temp_path = NULL;
@@ -2012,6 +2029,11 @@
     }
 
 
+    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
+                              prev->upstream.ignore_headers,
+                              NGX_CONF_BITMASK_SET);
+
+
     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
                               prev->upstream.next_upstream,
                               (NGX_CONF_BITMASK_SET
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/http/modules/ngx_http_image_filter_module.c	Fri May 01 00:00:00 2009 +0400
@@ -0,0 +1,1026 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+#include "gd.h"
+
+
+#define NGX_HTTP_IMAGE_OFF       0
+#define NGX_HTTP_IMAGE_TEST      1
+#define NGX_HTTP_IMAGE_SIZE      2
+#define NGX_HTTP_IMAGE_RESIZE    3
+#define NGX_HTTP_IMAGE_CROP      4
+
+
+#define NGX_HTTP_IMAGE_START     0
+#define NGX_HTTP_IMAGE_READ      1
+#define NGX_HTTP_IMAGE_PROCESS   2
+#define NGX_HTTP_IMAGE_DONE      3
+
+
+#define NGX_HTTP_IMAGE_NONE      0
+#define NGX_HTTP_IMAGE_JPEG      1
+#define NGX_HTTP_IMAGE_GIF       2
+#define NGX_HTTP_IMAGE_PNG       3
+
+
+#define NGX_HTTP_IMAGE_BUFFERED  0x08
+
+
+typedef struct {
+    ngx_uint_t                   filter;
+    ngx_uint_t                   width;
+    ngx_uint_t                   height;
+
+    size_t                       buffer_size;
+} ngx_http_image_filter_conf_t;
+
+
+typedef struct {
+    u_char                      *image;
+    u_char                      *last;
+
+    size_t                       length;
+
+    ngx_uint_t                   width;
+    ngx_uint_t                   height;
+
+    ngx_uint_t                   phase;
+    ngx_uint_t                   type;
+} ngx_http_image_filter_ctx_t;
+
+
+static ngx_uint_t ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in);
+static ngx_int_t ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in);
+static ngx_buf_t *ngx_http_image_process(ngx_http_request_t *r);
+static ngx_buf_t *ngx_http_image_json(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx);
+static ngx_buf_t *ngx_http_image_asis(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx);
+static void ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b);
+static ngx_int_t ngx_http_image_size(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx);
+
+static ngx_buf_t *ngx_http_image_resize(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx);
+static gdImagePtr ngx_http_image_source(ngx_http_request_t *r,
+    ngx_http_image_filter_ctx_t *ctx);
+static gdImagePtr ngx_http_image_new(ngx_http_request_t *r, int w, int h,
+    int colors);
+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 void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);
+static char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent,
+    void *child);
+static char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+static ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);
+
+
+static ngx_command_t  ngx_http_image_filter_commands[] = {
+
+    { ngx_string("image_filter"),
+      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE13,
+      ngx_http_image_filter,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
+    { ngx_string("image_filter_buffer"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_size_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_image_filter_conf_t, buffer_size),
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_http_module_t  ngx_http_image_filter_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    ngx_http_image_filter_init,            /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_image_filter_create_conf,     /* create location configuration */
+    ngx_http_image_filter_merge_conf       /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_image_filter_module = {
+    NGX_MODULE_V1,
+    &ngx_http_image_filter_module_ctx,     /* module context */
+    ngx_http_image_filter_commands,        /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    NULL,                                  /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
+static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
+
+
+static ngx_str_t  ngx_http_image_types[] = {
+    ngx_string("image/jpeg"),
+    ngx_string("image/gif"),
+    ngx_string("image/png")
+};
+
+
+static ngx_int_t
+ngx_http_image_header_filter(ngx_http_request_t *r)
+{
+    off_t                          len;
+    ngx_http_image_filter_ctx_t   *ctx;
+    ngx_http_image_filter_conf_t  *conf;
+
+    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {
+        return ngx_http_next_header_filter(r);
+    }
+
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+    if (conf->filter == NGX_HTTP_IMAGE_OFF) {
+        return ngx_http_next_header_filter(r);
+    }
+
+    if (r->headers_out.content_type.len
+            >= sizeof("multipart/x-mixed-replace") - 1
+        && ngx_strncasecmp(r->headers_out.content_type.data,
+                           (u_char *) "multipart/x-mixed-replace",
+                           sizeof("multipart/x-mixed-replace") - 1)
+           == 0)
+    {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "image filter: multipart/x-mixed-replace response");
+
+        return NGX_ERROR;
+    }
+
+    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_image_filter_ctx_t));
+    if (ctx == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_http_set_ctx(r, ctx, ngx_http_image_filter_module);
+
+    len = r->headers_out.content_length_n;
+
+    if (len != -1 && len > conf->buffer_size) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "image filter: too big response: %O", len);
+
+        return NGX_ERROR;
+    }
+
+    if (len == -1) {
+        ctx->length = conf->buffer_size;
+
+    } else {
+        ctx->length = (size_t) len;
+    }
+
+    if (r->headers_out.refresh) {
+        r->headers_out.refresh->hash = 0;
+    }
+
+    r->main_filter_need_in_memory = 1;
+    r->allow_ranges = 0;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    ngx_int_t                      rc;
+    ngx_str_t                     *ct;
+    ngx_chain_t                    out;
+    ngx_http_image_filter_ctx_t   *ctx;
+    ngx_http_image_filter_conf_t  *conf;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "image filter");
+
+    if (in == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+    if (ctx == NULL) {
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    switch (ctx->phase) {
+
+    case NGX_HTTP_IMAGE_START:
+
+        ctx->type = ngx_http_image_test(r, in);
+
+        conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);
+
+        if (ctx->type == NGX_HTTP_IMAGE_NONE) {
+
+            if (conf->filter == NGX_HTTP_IMAGE_SIZE) {
+                out.buf = ngx_http_image_json(r, NULL);
+
+                if (out.buf) {
+                    out.next = NULL;
+                    in = &out;
+
+                    break;
+                }
+            }
+
+            return ngx_http_filter_finalize_request(r,
+                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
+        }
+
+        /* override content type */
+
+        ct = &ngx_http_image_types[ctx->type - 1];
+        r->headers_out.content_type_len = ct->len;
+        r->headers_out.content_type = *ct;
+
+        if (conf->filter == NGX_HTTP_IMAGE_TEST) {
+            break;
+        }
+
+        ctx->phase = NGX_HTTP_IMAGE_READ;
+
+        /* fall through */
+
+    case NGX_HTTP_IMAGE_READ:
+
+        rc = ngx_http_image_read(r, in);
+
+        if (rc == NGX_AGAIN) {
+            return NGX_OK;
+        }
+
+        if (rc == NGX_ERROR) {
+            return ngx_http_filter_finalize_request(r,
+                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
+        }
+
+        /* fall through */
+
+    case NGX_HTTP_IMAGE_PROCESS:
+
+        out.buf = ngx_http_image_process(r);
+
+        if (out.buf == NULL) {
+            return ngx_http_filter_finalize_request(r,
+                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);
+        }
+
+        out.next = NULL;
+        in = &out;
+
+        break;
+
+    default: /* NGX_HTTP_IMAGE_DONE */
+
+        return ngx_http_next_body_filter(r, in);
+    }
+
+    ctx->phase = NGX_HTTP_IMAGE_DONE;
+
+    rc = ngx_http_next_header_filter(r);
+
+    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
+        return rc;
+    }
+
+    return ngx_http_next_body_filter(r, in);
+}
+
+
+static ngx_uint_t
+ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    u_char  *p;
+
+    p = in->buf->pos;
+
+    if (in->buf->last - p < 16) {
+        return NGX_HTTP_IMAGE_NONE;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "image filter: \"%c%c\"", p[0], p[1]);
+
+    if (p[0] == 0xff && p[1] == 0xd8) {
+
+        /* JPEG */
+
+        return NGX_HTTP_IMAGE_JPEG;
+
+    } else if (p[0] == 'G' && p[1] == 'I' && p[2] == 'F' && p[3] == '8'
+               && p[4] == '9' && p[5] == 'a')
+    {
+        /* GIF */
+
+        return NGX_HTTP_IMAGE_GIF;
+
+    } else if (p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G'
+               && p[4] == 0x0d && p[5] == 0x0a && p[6] == 0x1a && p[7] == 0x0a)
+    {
+        /* PNG */
+
+        return NGX_HTTP_IMAGE_PNG;
+    }
+
+    return NGX_HTTP_IMAGE_NONE;
+}
+
+
+static ngx_int_t
+ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in)
+{
+    u_char                       *p;
+    size_t                        size, rest;
+    ngx_buf_t                    *b;
+    ngx_chain_t                  *cl;
+    ngx_http_image_filter_ctx_t  *ctx;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+    if (ctx->image == NULL) {
+        ctx->image = ngx_palloc(r->pool, ctx->length);
+        if (ctx->image == NULL) {
+            return NGX_ERROR;
+        }
+
+        ctx->last = ctx->image;
+    }
+
+    p = ctx->last;
+
+    for (cl = in; cl; cl = cl->next) {
+
+        b = cl->buf;
+        size = b->last - b->pos;
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "image buf: %uz", size);
+
+        rest = ctx->image + ctx->length - p;
+        size = (rest < size) ? rest : size;
+
+        p = ngx_cpymem(p, b->pos, size);
+        b->pos += size;
+
+        if (b->last_buf) {
+            ctx->last = p;
+            return NGX_OK;
+        }
+    }
+
+    ctx->last = p;
+    r->connection->buffered |= NGX_HTTP_IMAGE_BUFFERED;
+
+    return NGX_AGAIN;
+}
+
+
+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;
+
+    r->connection->buffered &= ~NGX_HTTP_IMAGE_BUFFERED;
+
+    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);
+
+    rc = ngx_http_image_size(r, ctx);
+
+    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);
+
+    } 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);
+    }
+
+    return b;
+}
+
+
+static ngx_buf_t *
+ngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+    size_t      len;
+    ngx_buf_t  *b;
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        return NULL;
+    }
+
+    b->memory = 1;
+    b->last_buf = 1;
+
+    ngx_http_clean_header(r);
+
+    r->headers_out.status = NGX_HTTP_OK;
+    r->headers_out.content_type.len = sizeof("text/plain") - 1;
+    r->headers_out.content_type.data = (u_char *) "text/plain";
+
+    if (ctx == NULL) {
+        b->pos = (u_char *) "{}" CRLF;
+        b->last = b->pos + sizeof("{}" CRLF) - 1;
+
+        ngx_http_image_length(r, b);
+
+        return b;
+    }
+
+    len = sizeof("{ \"img\" : "
+                 "{ \"width\": , \"height\": , \"type\": \"jpeg\" } }" CRLF) - 1
+          + 2 * NGX_SIZE_T_LEN;
+
+    b->pos = ngx_pnalloc(r->pool, len);
+    if (b->pos == NULL) {
+        return NULL;
+    }
+
+    b->last = ngx_sprintf(b->pos,
+                          "{ \"img\" : "
+                                       "{ \"width\": %uz,"
+                                        " \"height\": %uz,"
+                                        " \"type\": \"%s\" } }" CRLF,
+                          ctx->width, ctx->height,
+                          ngx_http_image_types[ctx->type - 1].data + 6);
+
+    ngx_http_image_length(r, b);
+
+    return b;
+}
+
+
+static ngx_buf_t *
+ngx_http_image_asis(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+    ngx_buf_t  *b;
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        return NULL;
+    }
+
+    b->pos = ctx->image;
+    b->last = ctx->last;
+    b->memory = 1;
+    b->last_buf = 1;
+
+    ngx_http_image_length(r, b);
+
+    return b;
+}
+
+
+static void
+ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b)
+{
+    r->headers_out.content_length_n = b->last - b->pos;
+
+    if (r->headers_out.content_length) {
+        r->headers_out.content_length->hash = 0;
+    }
+
+    r->headers_out.content_length = NULL;
+}
+
+
+static ngx_int_t
+ngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+    u_char      *p, *last;
+    ngx_uint_t   width, height;
+
+    p = ctx->image;
+
+    switch (ctx->type) {
+
+    case NGX_HTTP_IMAGE_JPEG:
+
+        p += 2;
+        last = ctx->image + ctx->length - 10;
+
+        while (p < last) {
+
+            if (p[0] == 0xff && p[1] != 0xff) {
+
+                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                               "JPEG: %02xd %02xd", *p, *(p + 1));
+
+                p++;
+
+                if (*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3
+                    || *p == 0xc9 || *p == 0xca || *p == 0xcb)
+                {
+                    goto found;
+                }
+
+                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                               "JPEG: %02xd %02xd", p[1], p[2]);
+
+                p += p[1] * 256 + p[2];
+
+                continue;
+            }
+
+            p++;
+        }
+
+        return NGX_DECLINED;
+
+    found:
+
+        width = p[6] * 256 + p[7];
+        height = p[4] * 256 + p[5];
+
+        break;
+
+    case NGX_HTTP_IMAGE_GIF:
+
+        if (ctx->length < 10) {
+            return NGX_DECLINED;
+        }
+
+        width = p[7] * 256 + p[6];
+        height = p[9] * 256 + p[8];
+
+        break;
+
+    case NGX_HTTP_IMAGE_PNG:
+
+        if (ctx->length < 24) {
+            return NGX_DECLINED;
+        }
+
+        width = p[18] * 256 + p[19];
+        height = p[22] * 256 + p[23];
+
+        break;
+
+    default:
+
+        return NGX_DECLINED;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "image size: %d x %d", width, height);
+
+    ctx->width = width;
+    ctx->height = height;
+
+    return NGX_OK;
+}
+
+
+static ngx_buf_t *
+ngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+    int                            sx, sy, dx, dy, ox, oy,
+                                   colors, transparent, size;
+    u_char                        *out;
+    ngx_buf_t                     *b;
+    ngx_uint_t                     resize;
+    gdImagePtr                     src, dst;
+    ngx_pool_cleanup_t            *cln;
+    ngx_http_image_filter_conf_t  *conf;
+
+    src = ngx_http_image_source(r, ctx);
+
+    if (src == NULL) {
+        return NULL;
+    }
+
+    sx = gdImageSX(src);
+    sy = gdImageSY(src);
+
+    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) {
+        gdImageDestroy(src);
+        return ngx_http_image_asis(r, ctx);
+    }
+
+    colors = gdImageColorsTotal(src);
+    transparent = gdImageGetTransparent(src);
+
+    dx = sx;
+    dy = sy;
+
+    if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {
+
+        if ((ngx_uint_t) dx > conf->width) {
+            dy = dy * conf->width / dx;
+            dy = dy ? dy : 1;
+            dx = conf->width;
+        }
+
+        if ((ngx_uint_t) dy > conf->height) {
+            dx = dx * conf->height / dy;
+            dx = dx ? dx : 1;
+            dy = conf->height;
+        }
+
+        resize = 1;
+
+    } else { /* NGX_HTTP_IMAGE_CROP */
+
+        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;
+                dy = dy ? dy : 1;
+                dx = conf->width;
+                resize = 1;
+            }
+
+        } else {
+            if ((ngx_uint_t) dy > conf->height) {
+                dx = dx * conf->height / dy;
+                dx = dx ? dx : 1;
+                dy = conf->height;
+                resize = 1;
+            }
+        }
+    }
+
+    if (resize) {
+        dst = ngx_http_image_new(r, dx, dy, colors);
+        if (dst == NULL) {
+            gdImageDestroy(src);
+            return NULL;
+        }
+
+        gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy);
+
+        gdImageDestroy(src);
+
+    } else {
+        dst = src;
+    }
+
+    if (conf->filter == NGX_HTTP_IMAGE_CROP) {
+
+        src = dst;
+
+        if ((ngx_uint_t) dx > conf->width) {
+            ox = dx - conf->width;
+
+        } else {
+            ox = 0;
+        }
+
+        if ((ngx_uint_t) dy > conf->height) {
+            oy = dy - conf->height;
+
+        } else {
+            oy = 0;
+        }
+
+        if (ox || oy) {
+
+            dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);
+
+            if (dst == NULL) {
+                gdImageDestroy(src);
+                return NULL;
+            }
+
+            ox /= 2;
+            oy /= 2;
+
+            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "image crop: %d x %d @ %d x %d",
+                           dx, dy, ox, oy);
+
+            gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy);
+
+            gdImageDestroy(src);
+        }
+    }
+
+    gdImageColorTransparent(dst, transparent);
+
+    out = ngx_http_image_out(r, ctx->type, dst, &size);
+
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "image: %d x %d %d", sx, sy, colors);
+
+    gdImageDestroy(dst);
+    ngx_pfree(r->pool, ctx->image);
+
+    if (out == NULL) {
+        return NULL;
+    }
+
+    cln = ngx_pool_cleanup_add(r->pool, 0);
+    if (cln == NULL) {
+        gdFree(out);
+        return NULL;
+    }
+
+    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
+    if (b == NULL) {
+        gdFree(out);
+        return NULL;
+    }
+
+    cln->handler = ngx_http_image_cleanup;
+    cln->data = out;
+
+    b->pos = out;
+    b->last = out + size;
+    b->memory = 1;
+    b->last_buf = 1;
+
+    ngx_http_image_length(r, b);
+
+    return b;
+}
+
+
+static gdImagePtr
+ngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)
+{
+    char        *failed;
+    gdImagePtr   img;
+
+    img = NULL;
+
+    switch (ctx->type) {
+
+    case NGX_HTTP_IMAGE_JPEG:
+        img = gdImageCreateFromJpegPtr(ctx->length, ctx->image);
+        failed = "gdImageCreateFromJpegPtr() failed";
+        break;
+
+    case NGX_HTTP_IMAGE_GIF:
+        img = gdImageCreateFromGifPtr(ctx->length, ctx->image);
+        failed = "gdImageCreateFromGifPtr() failed";
+        break;
+
+    case NGX_HTTP_IMAGE_PNG:
+        img = gdImageCreateFromPngPtr(ctx->length, ctx->image);
+        failed = "gdImageCreateFromPngPtr() failed";
+        break;
+
+    default:
+        failed = "unknown image type";
+        break;
+    }
+
+    if (img == NULL) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
+    }
+
+    return img;
+}
+
+
+static gdImagePtr
+ngx_http_image_new(ngx_http_request_t *r, int w, int h, int colors)
+{
+    gdImagePtr  img;
+
+    if (colors == 0) {
+        img = gdImageCreateTrueColor(w, h);
+
+        if (img == NULL) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "gdImageCreateTrueColor() failed");
+            return NULL;
+        }
+
+    } else {
+        img = gdImageCreate(w, h);
+
+        if (img == NULL) {
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                          "gdImageCreate() failed");
+            return NULL;
+        }
+    }
+
+    return img;
+}
+
+
+static u_char *
+ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,
+    int *size)
+{
+    char    *failed;
+    u_char  *out;
+
+    out = NULL;
+
+    switch (type) {
+
+    case NGX_HTTP_IMAGE_JPEG:
+        out = gdImageJpegPtr(img, size, /* default quality */ -1);
+        failed = "gdImageJpegPtr() failed";
+        break;
+
+    case NGX_HTTP_IMAGE_GIF:
+        out = gdImageGifPtr(img, size);
+        failed = "gdImageGifPtr() failed";
+        break;
+
+    case NGX_HTTP_IMAGE_PNG:
+        out = gdImagePngPtr(img, size);
+        failed = "gdImagePngPtr() failed";
+        break;
+
+    default:
+        failed = "unknown image type";
+        break;
+    }
+
+    if (out == NULL) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);
+    }
+
+    return out;
+}
+
+
+static void
+ngx_http_image_cleanup(void *data)
+{
+    gdFree(data);
+}
+
+
+static void *
+ngx_http_image_filter_create_conf(ngx_conf_t *cf)
+{
+    ngx_http_image_filter_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t));
+    if (conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    conf->filter = NGX_CONF_UNSET_UINT;
+    conf->buffer_size = NGX_CONF_UNSET_SIZE;
+
+    return conf;
+}
+
+
+static char *
+ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_image_filter_conf_t *prev = parent;
+    ngx_http_image_filter_conf_t *conf = child;
+
+    if (conf->filter == NGX_CONF_UNSET_UINT) {
+
+        if (prev->filter == NGX_CONF_UNSET_UINT) {
+            conf->filter = NGX_HTTP_IMAGE_OFF;
+
+        } else {
+            conf->filter = prev->filter;
+            conf->width = prev->width;
+            conf->height = prev->height;
+        }
+    }
+
+    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
+                              1 * 1024 * 1024);
+
+    return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_image_filter_conf_t *imcf = conf;
+
+    ngx_str_t   *value;
+    ngx_int_t    n;
+    ngx_uint_t   i;
+
+    value = cf->args->elts;
+
+    i = 1;
+
+    if (cf->args->nelts == 2) {
+        if (ngx_strcmp(value[i].data, "off") == 0) {
+            imcf->filter = NGX_HTTP_IMAGE_OFF;
+
+        } else if (ngx_strcmp(value[i].data, "test") == 0) {
+            imcf->filter = NGX_HTTP_IMAGE_TEST;
+
+        } else if (ngx_strcmp(value[i].data, "size") == 0) {
+            imcf->filter = NGX_HTTP_IMAGE_SIZE;
+
+        } else {
+            goto failed;
+        }
+
+        return NGX_CONF_OK;
+    }
+
+    if (ngx_strcmp(value[i].data, "resize") == 0) {
+        imcf->filter = NGX_HTTP_IMAGE_RESIZE;
+
+    } else if (ngx_strcmp(value[i].data, "crop") == 0) {
+        imcf->filter = NGX_HTTP_IMAGE_CROP;
+
+    } else {
+        goto failed;
+    }
+
+    i++;
+
+    if (value[i].len == 1 && value[i].data[0] == '-') {
+        imcf->width = (ngx_uint_t) -1;
+
+    } else {
+        n = ngx_atoi(value[i].data, value[i].len);
+        if (n == NGX_ERROR) {
+            goto failed;
+        }
+
+        imcf->width = (ngx_uint_t) n;
+    }
+
+    i++;
+
+    if (value[i].len == 1 && value[i].data[0] == '-') {
+        imcf->height = (ngx_uint_t) -1;
+
+    } else {
+        n = ngx_atoi(value[i].data, value[i].len);
+        if (n == NGX_ERROR) {
+            goto failed;
+        }
+
+        imcf->height = (ngx_uint_t) n;
+    }
+
+    return NGX_CONF_OK;
+
+failed:
+
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"",
+                       &value[i]);
+
+    return NGX_CONF_ERROR;
+}
+
+
+static ngx_int_t
+ngx_http_image_filter_init(ngx_conf_t *cf)
+{
+    ngx_http_next_header_filter = ngx_http_top_header_filter;
+    ngx_http_top_header_filter = ngx_http_image_header_filter;
+
+    ngx_http_next_body_filter = ngx_http_top_body_filter;
+    ngx_http_top_body_filter = ngx_http_image_body_filter;
+
+    return NGX_OK;
+}
--- a/src/http/modules/ngx_http_proxy_module.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/modules/ngx_http_proxy_module.c	Fri May 01 00:00:00 2009 +0400
@@ -172,6 +172,15 @@
 };
 
 
+static ngx_conf_bitmask_t  ngx_http_proxy_ignore_headers_masks[] = {
+    { ngx_string("X-Accel-Redirect"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT },
+    { ngx_string("X-Accel-Expires"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES },
+    { ngx_string("Expires"), NGX_HTTP_UPSTREAM_IGN_EXPIRES },
+    { ngx_string("Cache-Control"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL },
+    { ngx_null_string, 0 }
+};
+
+
 ngx_module_t  ngx_http_proxy_module;
 
 
@@ -426,6 +435,13 @@
       offsetof(ngx_http_proxy_loc_conf_t, upstream.hide_headers),
       NULL },
 
+    { ngx_string("proxy_ignore_headers"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+      ngx_conf_set_bitmask_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_headers),
+      &ngx_http_proxy_ignore_headers_masks },
+
 #if (NGX_HTTP_SSL)
 
     { ngx_string("proxy_ssl_session_reuse"),
@@ -1867,6 +1883,7 @@
      * set by ngx_pcalloc():
      *
      *     conf->upstream.bufs.num = 0;
+     *     conf->upstream.ignore_headers = 0;
      *     conf->upstream.next_upstream = 0;
      *     conf->upstream.use_stale_cache = 0;
      *     conf->upstream.temp_path = NULL;
@@ -2072,6 +2089,11 @@
     }
 
 
+    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
+                              prev->upstream.ignore_headers,
+                              NGX_CONF_BITMASK_SET);
+
+
     ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
                               prev->upstream.next_upstream,
                               (NGX_CONF_BITMASK_SET
@@ -2675,10 +2697,26 @@
 
     value = cf->args->elts;
 
-    if (ngx_strcmp(value[1].data, "off") == 0) {
-        plcf->redirect = 0;
-        plcf->redirects = NULL;
-        return NGX_CONF_OK;
+    if (cf->args->nelts == 2) {
+        if (ngx_strcmp(value[1].data, "off") == 0) {
+            plcf->redirect = 0;
+            plcf->redirects = NULL;
+            return NGX_CONF_OK;
+        }
+
+        if (ngx_strcmp(value[1].data, "false") == 0) {
+            ngx_conf_log_error(NGX_LOG_ERR, cf, 0,
+                           "invalid parameter \"false\", use \"off\" instead");
+            plcf->redirect = 0;
+            plcf->redirects = NULL;
+            return NGX_CONF_OK;
+        }
+
+        if (ngx_strcmp(value[1].data, "default") != 0) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid parameter \"%V\"", &value[1]);
+            return NGX_CONF_ERROR;
+        }
     }
 
     if (plcf->redirects == NULL) {
@@ -2694,7 +2732,7 @@
         return NGX_CONF_ERROR;
     }
 
-    if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "default") == 0) {
+    if (ngx_strcmp(value[1].data, "default") == 0) {
         if (plcf->url.data == NULL) {
             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                "\"proxy_rewrite_location default\" must go "
--- a/src/http/modules/ngx_http_xslt_filter_module.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/modules/ngx_http_xslt_filter_module.c	Fri May 01 00:00:00 2009 +0400
@@ -64,7 +64,6 @@
 
 static ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,
     ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
-static ngx_int_t ngx_http_xslt_filter_internal_error(ngx_http_request_t *r);
 static ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,
     ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);
 
@@ -320,14 +319,15 @@
     ctx->done = 1;
 
     if (b == NULL) {
-        return ngx_http_xslt_filter_internal_error(r);
+        return ngx_http_filter_finalize_request(r,
+                                               NGX_HTTP_INTERNAL_SERVER_ERROR);
     }
 
     cln = ngx_pool_cleanup_add(r->pool, 0);
 
     if (cln == NULL) {
         ngx_free(b->pos);
-        return ngx_http_special_response_handler(r,
+        return ngx_http_filter_finalize_request(r,
                                                NGX_HTTP_INTERNAL_SERVER_ERROR);
     }
 
@@ -360,22 +360,6 @@
 
 
 static ngx_int_t
-ngx_http_xslt_filter_internal_error(ngx_http_request_t *r)
-{
-    ngx_int_t  rc;
-
-    /* clear the modules contexts */
-    ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
-
-    rc = ngx_http_special_response_handler(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-
-    /* NGX_ERROR resets any pending data */
-
-    return (rc == NGX_OK) ? NGX_ERROR : rc;
-}
-
-
-static ngx_int_t
 ngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
     ngx_buf_t *b)
 {
--- a/src/http/modules/perl/nginx.pm	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/modules/perl/nginx.pm	Fri May 01 00:00:00 2009 +0400
@@ -47,7 +47,7 @@
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.7.53';
+our $VERSION = '0.7.54';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/ngx_http.c	Fri May 01 00:00:00 2009 +0400
@@ -1746,7 +1746,7 @@
 
     clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];
 
-    ls->log = *clcf->err_log;
+    ls->logp = clcf->error_log;
     ls->log.data = &ls->addr_text;
     ls->log.handler = ngx_accept_log_error;
 
--- a/src/http/ngx_http.h	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/ngx_http.h	Fri May 01 00:00:00 2009 +0400
@@ -103,6 +103,9 @@
 ngx_int_t ngx_http_send_header(ngx_http_request_t *r);
 ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,
     ngx_int_t error);
+ngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,
+    ngx_int_t error);
+void ngx_http_clean_header(ngx_http_request_t *r);
 
 
 time_t ngx_http_parse_time(u_char *value, size_t len);
--- a/src/http/ngx_http_core_module.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/ngx_http_core_module.c	Fri May 01 00:00:00 2009 +0400
@@ -1292,10 +1292,10 @@
     }
 
     if (r == r->main) {
-        r->connection->log->file = clcf->err_log->file;
+        r->connection->log->file = clcf->error_log->file;
 
         if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
-            r->connection->log->log_level = clcf->err_log->log_level;
+            r->connection->log->log_level = clcf->error_log->log_level;
         }
     }
 
@@ -2929,7 +2929,7 @@
      *     lcf->post_action = { 0, NULL };
      *     lcf->types = NULL;
      *     lcf->default_type = { 0, NULL };
-     *     lcf->err_log = NULL;
+     *     lcf->error_log = NULL;
      *     lcf->error_pages = NULL;
      *     lcf->try_files = NULL;
      *     lcf->client_body_path = NULL;
@@ -3109,11 +3109,11 @@
         }
     }
 
-    if (conf->err_log == NULL) {
-        if (prev->err_log) {
-            conf->err_log = prev->err_log;
+    if (conf->error_log == NULL) {
+        if (prev->error_log) {
+            conf->error_log = prev->error_log;
         } else {
-            conf->err_log = cf->cycle->new_log;
+            conf->error_log = &cf->cycle->new_log;
         }
     }
 
@@ -4104,14 +4104,23 @@
 
     ngx_str_t  *value;
 
+    if (lcf->error_log) {
+        return "is duplicate";
+    }
+
     value = cf->args->elts;
 
-    lcf->err_log = ngx_log_create_errlog(cf->cycle, &value[1]);
-    if (lcf->err_log == NULL) {
+    lcf->error_log = ngx_log_create(cf->cycle, &value[1]);
+    if (lcf->error_log == NULL) {
         return NGX_CONF_ERROR;
     }
 
-    return ngx_set_error_log_levels(cf, lcf->err_log);
+    if (cf->args->nelts == 2) {
+        lcf->error_log->log_level = NGX_LOG_ERR;
+        return NGX_CONF_OK;
+    }
+
+    return ngx_log_set_levels(cf, lcf->error_log);
 }
 
 
--- a/src/http/ngx_http_core_module.h	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/ngx_http_core_module.h	Fri May 01 00:00:00 2009 +0400
@@ -377,7 +377,7 @@
     ngx_flag_t    open_file_cache_errors;
     ngx_flag_t    open_file_cache_events;
 
-    ngx_log_t    *err_log;
+    ngx_log_t    *error_log;
 
     ngx_uint_t    types_hash_max_size;
     ngx_uint_t    types_hash_bucket_size;
--- a/src/http/ngx_http_header_filter_module.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/ngx_http_header_filter_module.c	Fri May 01 00:00:00 2009 +0400
@@ -357,6 +357,9 @@
             else
 #endif
                 port = (port == 80) ? 0 : port;
+
+        } else {
+            port = 0;
         }
 
         if (port) {
--- a/src/http/ngx_http_request.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/ngx_http_request.c	Fri May 01 00:00:00 2009 +0400
@@ -420,9 +420,9 @@
 #endif
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-    c->log->file = clcf->err_log->file;
+    c->log->file = clcf->error_log->file;
     if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
-        c->log->log_level = clcf->err_log->log_level;
+        c->log->log_level = clcf->error_log->log_level;
     }
 
     if (c->buffer == NULL) {
@@ -1704,10 +1704,10 @@
     r->loc_conf = cscf->ctx->loc_conf;
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
-    r->connection->log->file = clcf->err_log->file;
+    r->connection->log->file = clcf->error_log->file;
 
     if (!(r->connection->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {
-        r->connection->log->log_level = clcf->err_log->log_level;
+        r->connection->log->log_level = clcf->error_log->log_level;
     }
 
     return NGX_OK;
--- a/src/http/ngx_http_special_response.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/ngx_http_special_response.c	Fri May 01 00:00:00 2009 +0400
@@ -445,6 +445,40 @@
 }
 
 
+ngx_int_t
+ngx_http_filter_finalize_request(ngx_http_request_t *r, ngx_int_t error)
+{
+    ngx_int_t  rc;
+
+    ngx_http_clean_header(r);
+
+    /* clear the modules contexts */
+    ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);
+
+    rc = ngx_http_special_response_handler(r, error);
+
+    /* NGX_ERROR resets any pending data */
+
+    return (rc == NGX_OK) ? NGX_ERROR : rc;
+}
+
+
+void
+ngx_http_clean_header(ngx_http_request_t *r)
+{
+    ngx_memzero(&r->headers_out.status,
+                sizeof(ngx_http_headers_out_t)
+                    - offsetof(ngx_http_headers_out_t, status));
+
+    r->headers_out.headers.part.nelts = 0;
+    r->headers_out.headers.part.next = NULL;
+    r->headers_out.headers.last = &r->headers_out.headers.part;
+
+    r->headers_out.content_length_n = -1;
+    r->headers_out.last_modified_time = -1;
+}
+
+
 static ngx_int_t
 ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)
 {
--- a/src/http/ngx_http_upstream.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/ngx_http_upstream.c	Fri May 01 00:00:00 2009 +0400
@@ -1623,8 +1623,9 @@
 
     umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
 
-    if (u->headers_in.x_accel_redirect) {
-
+    if (u->headers_in.x_accel_redirect
+        && !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT))
+    {
         ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);
 
         part = &u->headers_in.headers.part;
@@ -2845,10 +2846,12 @@
 ngx_http_upstream_process_cache_control(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset)
 {
-    ngx_array_t       *pa;
-    ngx_table_elt_t  **ph;
-
-    pa = &r->upstream->headers_in.cache_control;
+    ngx_array_t          *pa;
+    ngx_table_elt_t     **ph;
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+    pa = &u->headers_in.cache_control;
 
     if (pa->elts == NULL) {
        if (ngx_array_init(pa, r->pool, 2, sizeof(ngx_table_elt_t *)) != NGX_OK)
@@ -2869,6 +2872,10 @@
     u_char     *p, *last;
     ngx_int_t   n;
 
+    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL) {
+        return NGX_OK;
+    }
+
     if (r->cache == NULL) {
         return NGX_OK;
     }
@@ -2882,7 +2889,7 @@
     if (ngx_strlcasestrn(h->value.data, last, (u_char *) "no-cache", 8 - 1)
         != NULL)
     {
-        r->upstream->cacheable = 0;
+        u->cacheable = 0;
         return NGX_OK;
     }
 
@@ -2904,12 +2911,12 @@
             continue;
         }
 
-        r->upstream->cacheable = 0;
+        u->cacheable = 0;
         return NGX_OK;
     }
 
     if (n == 0) {
-        r->upstream->cacheable = 0;
+        u->cacheable = 0;
         return NGX_OK;
     }
 
@@ -2925,12 +2932,19 @@
 ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
     ngx_uint_t offset)
 {
-    r->upstream->headers_in.expires = h;
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+    u->headers_in.expires = h;
 
 #if (NGX_HTTP_CACHE)
     {
     time_t  expires;
 
+    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) {
+        return NGX_OK;
+    }
+
     if (r->cache == NULL) {
         return NGX_OK;
     }
@@ -2942,7 +2956,7 @@
     expires = ngx_http_parse_time(h->value.data, h->value.len);
 
     if (expires == NGX_ERROR || expires < ngx_time()) {
-        r->upstream->cacheable = 0;
+        u->cacheable = 0;
         return NGX_OK;
     }
 
@@ -2958,7 +2972,10 @@
 ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,
     ngx_table_elt_t *h, ngx_uint_t offset)
 {
-    r->upstream->headers_in.x_accel_expires = h;
+    ngx_http_upstream_t  *u;
+
+    u = r->upstream;
+    u->headers_in.x_accel_expires = h;
 
 #if (NGX_HTTP_CACHE)
     {
@@ -2966,6 +2983,10 @@
     size_t      len;
     ngx_int_t   n;
 
+    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES) {
+        return NGX_OK;
+    }
+
     if (r->cache == NULL) {
         return NGX_OK;
     }
@@ -2978,7 +2999,7 @@
 
         switch (n) {
         case 0:
-            r->upstream->cacheable = 0;
+            u->cacheable = 0;
         case NGX_ERROR:
             return NGX_OK;
 
--- a/src/http/ngx_http_upstream.h	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/http/ngx_http_upstream.h	Fri May 01 00:00:00 2009 +0400
@@ -38,6 +38,12 @@
 #define NGX_HTTP_UPSTREAM_INVALID_HEADER     40
 
 
+#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT    0x00000002
+#define NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES     0x00000004
+#define NGX_HTTP_UPSTREAM_IGN_EXPIRES        0x00000008
+#define NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL  0x00000010
+
+
 typedef struct {
     ngx_msec_t                       bl_time;
     ngx_uint_t                       bl_state;
@@ -128,6 +134,7 @@
 
     ngx_bufs_t                       bufs;
 
+    ngx_uint_t                       ignore_headers;
     ngx_uint_t                       next_upstream;
     ngx_uint_t                       store_access;
     ngx_flag_t                       buffering;
--- a/src/mail/ngx_mail.c	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/mail/ngx_mail.c	Fri May 01 00:00:00 2009 +0400
@@ -313,11 +313,10 @@
             ls->handler = ngx_mail_init_connection;
             ls->pool_size = 256;
 
-            /* STUB */
-            ls->log = *cf->cycle->new_log;
+            /* TODO: error_log directive */
+            ls->logp = &cf->cycle->new_log;
             ls->log.data = &ls->addr_text;
             ls->log.handler = ngx_accept_log_error;
-            /**/
 
             imip = ngx_palloc(cf->pool, sizeof(ngx_mail_in_port_t));
             if (imip == NULL) {
--- a/src/os/unix/ngx_files.h	Mon Apr 27 00:00:00 2009 +0400
+++ b/src/os/unix/ngx_files.h	Fri May 01 00:00:00 2009 +0400
@@ -113,6 +113,10 @@
 
 #define ngx_write_fd_n           "write()"
 
+
+#define ngx_write_console        ngx_write_fd
+
+
 #define ngx_linefeed(p)          *p++ = LF;
 #define NGX_LINEFEED_SIZE        1