changeset 146:36af50a5582d NGINX_0_3_20

nginx 0.3.20 *) Bugfix: in SSI handling. *) Bugfix: the ngx_http_memcached_module did not support the keys in the "/usr?args" form.
author Igor Sysoev <http://sysoev.ru>
date Wed, 11 Jan 2006 00:00:00 +0300
parents 85a84f8da62b
children d1b9f90d95f6
files CHANGES CHANGES.ru LICENSE auto/modules auto/sources src/core/nginx.h src/core/ngx_connection.h src/core/ngx_hash.c src/core/ngx_hash.h src/core/ngx_output_chain.c src/event/ngx_event_openssl.c src/http/modules/ngx_http_autoindex_module.c src/http/modules/ngx_http_charset_filter_module.c src/http/modules/ngx_http_chunked_filter_module.c src/http/modules/ngx_http_gzip_filter_module.c src/http/modules/ngx_http_headers_filter_module.c src/http/modules/ngx_http_memcached_module.c src/http/modules/ngx_http_not_modified_filter_module.c src/http/modules/ngx_http_range_filter_module.c src/http/modules/ngx_http_ssi_filter_module.c src/http/modules/ngx_http_ssi_filter_module.h src/http/modules/ngx_http_static_module.c src/http/modules/ngx_http_userid_filter_module.c src/http/ngx_http.h src/http/ngx_http_core_module.c src/http/ngx_http_header_filter_module.c src/http/ngx_http_postpone_filter_module.c src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_special_response.c src/http/ngx_http_upstream.c src/http/ngx_http_write_filter_module.c src/os/unix/ngx_freebsd_sendfile_chain.c src/os/unix/ngx_linux_sendfile_chain.c src/os/unix/ngx_solaris_sendfilev_chain.c src/os/unix/ngx_writev_chain.c
diffstat 36 files changed, 488 insertions(+), 261 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,11 @@
+
+Changes with nginx 0.3.20                                        11 Jan 2006
+
+    *) Bugfix: in SSI handling.
+
+    *) Bugfix: the ngx_http_memcached_module did not support the keys in 
+       the "/usr?args" form.
+
 
 Changes with nginx 0.3.19                                        28 Dec 2005
 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,3 +1,11 @@
+
+Изменения в nginx 0.3.20                                          11.01.2006
+
+    *) Исправление: ошибки в обработке SSI.
+
+    *) Исправление: модуль ngx_http_memcached_module не поддерживал ключи в 
+       виде /uri?args.
+
 
 Изменения в nginx 0.3.19                                          28.12.2005
 
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2002-2005 Igor Sysoev
+ * Copyright (C) 2002-2006 Igor Sysoev
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
--- a/auto/modules
+++ b/auto/modules
@@ -110,6 +110,7 @@ fi
 if [ $HTTP_SSI = YES ]; then
     have=NGX_HTTP_SSI . auto/have
     HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES $HTTP_SSI_FILTER_MODULE"
+    HTTP_DEPS="$HTTP_DEPS $HTTP_SSI_DEPS"
     HTTP_SRCS="$HTTP_SRCS $HTTP_SSI_SRCS"
 fi
 
--- a/auto/sources
+++ b/auto/sources
@@ -303,6 +303,7 @@ HTTP_GZIP_SRCS=src/http/modules/ngx_http
 
 
 HTTP_SSI_FILTER_MODULE=ngx_http_ssi_filter_module
+HTTP_SSI_DEPS=src/http/modules/ngx_http_ssi_filter_module.h
 HTTP_SSI_SRCS=src/http/modules/ngx_http_ssi_filter_module.c
 
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VER          "nginx/0.3.19"
+#define NGINX_VER          "nginx/0.3.20"
 
 #define NGINX_VAR          "NGINX"
 #define NGX_OLDPID_EXT     ".oldbin"
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -93,6 +93,10 @@ typedef enum {
 } ngx_connection_tcp_nopush_e;
 
 
+#define NGX_LOWLEVEL_BUFFERED  0x0000000f
+#define NGX_SSL_BUFFERED       0x00000001
+
+
 struct ngx_connection_s {
     void               *data;
     ngx_event_t        *read;
@@ -134,9 +138,10 @@ struct ngx_connection_s {
 
     ngx_atomic_uint_t   number;
 
+    ngx_uint_t          buffered;
+
     unsigned            log_error:2;     /* ngx_connection_log_error_e */
 
-    unsigned            buffered:1;
     unsigned            single_connection:1;
     unsigned            unexpected_eof:1;
     unsigned            timedout:1;
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -753,7 +753,9 @@ ngx_hash_add_key(ngx_hash_keys_arrays_t 
         k = 0;
 
         for (i = 0; i < key->len; i++) {
-            key->data[i] = ngx_tolower(key->data[i]);
+            if (!(flags & NGX_HASH_READONLY_KEY)) {
+                key->data[i] = ngx_tolower(key->data[i]);
+            }
             k = ngx_hash(k, key->data[i]);
         }
 
--- a/src/core/ngx_hash.h
+++ b/src/core/ngx_hash.h
@@ -61,6 +61,7 @@ typedef struct {
 #define NGX_HASH_LARGE_HSIZE      10007
 
 #define NGX_HASH_WILDCARD_KEY     1
+#define NGX_HASH_READONLY_KEY     2
 
 
 typedef struct {
@@ -114,6 +115,7 @@ ngx_int_t ngx_hash_add_key(ngx_hash_keys
     void *value, ngx_uint_t flags);
 
 
+#define ngx_hash0(key, c)   key + c
 ngx_int_t ngx_hash0_init(ngx_hash0_t *hash, ngx_pool_t *pool, void *names,
     ngx_uint_t nelts);
 
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -28,9 +28,10 @@ static ngx_int_t ngx_output_chain_copy_b
 ngx_int_t
 ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
 {
-    int           rc, last;
     off_t         bsize;
     size_t        size;
+    ngx_int_t     rc, last;
+    ngx_uint_t    recycled;
     ngx_chain_t  *cl, *out, **last_out;
 
     if (ctx->in == NULL && ctx->busy == NULL) {
@@ -131,6 +132,7 @@ ngx_output_chain(ngx_output_chain_ctx_t 
                 } else {
 
                     size = ctx->bufs.size;
+                    recycled = 1;
 
                     if (ctx->in->buf->last_in_chain) {
 
@@ -142,6 +144,7 @@ ngx_output_chain(ngx_output_chain_ctx_t 
                             */
 
                             size = (size_t) bsize;
+                            recycled = 0;
 
                         } else if (ctx->bufs.num == 1
                                    && (bsize < (off_t) (ctx->bufs.size
@@ -154,6 +157,7 @@ ngx_output_chain(ngx_output_chain_ctx_t 
                              */
 
                             size = (size_t) bsize;
+                            recycled = 0;
                         }
                     }
 
@@ -163,7 +167,7 @@ ngx_output_chain(ngx_output_chain_ctx_t 
                     }
 
                     ctx->buf->tag = ctx->tag;
-                    ctx->buf->recycled = 1;
+                    ctx->buf->recycled = recycled;
                     ctx->allocated++;
                 }
             }
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -592,7 +592,9 @@ ngx_ssl_send_chain(ngx_connection_t *c, 
     ssize_t      send, size;
     ngx_buf_t   *buf;
 
-    if (!c->ssl->buffer || (in && in->next == NULL && !c->buffered)) {
+    if (!c->ssl->buffer
+        || (in && in->next == NULL && !(c->buffered & NGX_SSL_BUFFERED)))
+    {
 
         /*
          * we avoid a buffer copy if
@@ -613,7 +615,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, 
             }
 
             if (n == NGX_AGAIN) {
-                c->buffered = 1;
+                c->buffered |= NGX_SSL_BUFFERED;
                 return in;
             }
 
@@ -689,7 +691,7 @@ ngx_ssl_send_chain(ngx_connection_t *c, 
         }
 
         if (n == NGX_AGAIN) {
-            c->buffered = 1;
+            c->buffered |= NGX_SSL_BUFFERED;
             return in;
         }
 
@@ -711,7 +713,12 @@ ngx_ssl_send_chain(ngx_connection_t *c, 
         }
     }
 
-    c->buffered = (buf->pos < buf->last) ? 1 : 0;
+    if (buf->pos < buf->last) {
+        c->buffered |= NGX_SSL_BUFFERED;
+
+    } else {
+        c->buffered &= ~NGX_SSL_BUFFERED;
+    }
 
     return in;
 }
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -503,7 +503,7 @@ ngx_http_autoindex_handler(ngx_http_requ
 
     b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);
 
-    if (r->main == r) {
+    if (r == r->main) {
         b->last_buf = 1;
     }
 
--- a/src/http/modules/ngx_http_charset_filter_module.c
+++ b/src/http/modules/ngx_http_charset_filter_module.c
@@ -162,7 +162,7 @@ ngx_http_charset_header_filter(ngx_http_
         return ngx_http_next_header_filter(r);
     }
 
-    if (r->main == r
+    if (r == r->main
         && ngx_strstr(r->headers_out.content_type.data, "charset") != NULL)
     {
         return ngx_http_next_header_filter(r);
--- a/src/http/modules/ngx_http_chunked_filter_module.c
+++ b/src/http/modules/ngx_http_chunked_filter_module.c
@@ -50,7 +50,7 @@ static ngx_http_output_body_filter_pt   
 static ngx_int_t
 ngx_http_chunked_header_filter(ngx_http_request_t *r)
 {
-    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED || r->main != r) {
+    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED || r != r->main) {
         return ngx_http_next_header_filter(r);
     }
 
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -101,7 +101,6 @@ static ngx_conf_post_handler_pt  ngx_htt
 static ngx_conf_post_handler_pt  ngx_http_gzip_hash_p = ngx_http_gzip_hash;
 
 
-
 static ngx_conf_enum_t  ngx_http_gzip_http_version[] = {
     { ngx_string("1.0"), NGX_HTTP_VERSION_10 },
     { ngx_string("1.1"), NGX_HTTP_VERSION_11 },
@@ -283,7 +282,7 @@ ngx_http_gzip_header_filter(ngx_http_req
             && r->headers_out.status != NGX_HTTP_FORBIDDEN
             && r->headers_out.status != NGX_HTTP_NOT_FOUND)
         || r->header_only
-        || r->main != r
+        || r != r->main
         || r->http_version < conf->http_version
         || r->headers_out.content_type.len == 0
         || (r->headers_out.content_encoding
@@ -544,6 +543,8 @@ ngx_http_gzip_body_filter(ngx_http_reque
             return NGX_ERROR;
         }
 
+        r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;
+
         ctx->last_out = &ctx->out;
 
         ctx->crc32 = crc32(0L, Z_NULL, 0);
@@ -799,6 +800,8 @@ ngx_http_gzip_body_filter(ngx_http_reque
 
                 ctx->done = 1;
 
+                r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
+
                 break;
             }
 
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -97,7 +97,7 @@ ngx_http_headers_filter(ngx_http_request
 
     if ((r->headers_out.status != NGX_HTTP_OK
          && r->headers_out.status != NGX_HTTP_NOT_MODIFIED)
-        || r->main != r)
+        || r != r->main)
     {
         return ngx_http_next_header_filter(r);
     }
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -19,6 +19,7 @@ typedef struct {
 typedef struct {
     size_t                     rest;
     ngx_http_request_t        *request;
+    ngx_str_t                  key;
 } ngx_http_memcached_ctx_t;
 
 
@@ -202,6 +203,8 @@ ngx_http_memcached_handler(ngx_http_requ
     ctx->rest = NGX_HTTP_MEMCACHED_END;
     ctx->request = r;
 
+    ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);
+
     u->input_filter_init = ngx_http_memcached_filter_init;
     u->input_filter = ngx_http_memcached_filter;
     u->input_filter_ctx = ctx;
@@ -215,13 +218,14 @@ ngx_http_memcached_handler(ngx_http_requ
 static ngx_int_t
 ngx_http_memcached_create_request(ngx_http_request_t *r)
 {
-    size_t        len;
-    ngx_buf_t    *b;
-    ngx_chain_t  *cl;
+    size_t                     len;
+    ngx_buf_t                 *b;
+    ngx_chain_t               *cl;
+    ngx_http_memcached_ctx_t  *ctx;
 
     len = sizeof("get ") - 1 + r->uri.len + sizeof(" " CRLF) - 1;
     if (r->args.len) {
-        len += 1+ r->args.len;
+        len += 1 + r->args.len;
     }
 
     b = ngx_create_temp_buf(r->pool, len);
@@ -241,6 +245,10 @@ ngx_http_memcached_create_request(ngx_ht
 
     *b->last++ = 'g'; *b->last++ = 'e'; *b->last++ = 't'; *b->last++ = ' ';
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
+
+    ctx->key.data = b->last;
+
     b->last = ngx_copy(b->last, r->uri.data, r->uri.len);
 
     if (r->args.len) {
@@ -248,16 +256,10 @@ ngx_http_memcached_create_request(ngx_ht
         b->last = ngx_copy(b->last, r->args.data, r->args.len);
     }
 
-#if (NGX_DEBUG)
-    {
-    ngx_str_t  s;
+    ctx->key.len = b->last - ctx->key.data;
 
-    s.len = b->last - b->pos;
-    s.data = b->pos;
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                   "http memcached request: \"%V\"", &s);
-    }
-#endif
+                   "http memcached request: \"%V\"", &ctx->key);
 
     *b->last++ = ' '; *b->last++ = CR; *b->last++ = LF;
 
@@ -275,9 +277,10 @@ ngx_http_memcached_reinit_request(ngx_ht
 static ngx_int_t
 ngx_http_memcached_process_header(ngx_http_request_t *r)
 {
-    u_char               *p, *len;
-    ngx_str_t             line;
-    ngx_http_upstream_t  *u;
+    u_char                    *p, *len;
+    ngx_str_t                  line;
+    ngx_http_upstream_t       *u;
+    ngx_http_memcached_ctx_t  *ctx;
 
     u = r->upstream;
 
@@ -301,20 +304,22 @@ found:
 
     p = u->buffer.pos;
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);
+
     if (ngx_strncmp(p, "VALUE ", sizeof("VALUE ") - 1) == 0) {
 
         p += sizeof("VALUE ") - 1;
 
-        if (ngx_strncmp(p, r->uri.data, r->uri.len) != 0) {
+        if (ngx_strncmp(p, ctx->key.data, ctx->key.len) != 0) {
             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                           "memcached sent invalid key in response \"%V\" "
                           "for key \"%V\"",
-                          &line, &r->uri);
+                          &line, &ctx->key);
 
             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
         }
 
-        p += r->uri.len;
+        p += ctx->key.len;
 
         if (*p++ != ' ') {
             goto no_valid;
@@ -341,7 +346,7 @@ found:
             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                           "memcached sent invalid length in response \"%V\" "
                           "for key \"%V\"",
-                          &line, &r->uri);
+                          &line, &ctx->key);
             return NGX_HTTP_UPSTREAM_INVALID_HEADER;
         }
 
@@ -353,7 +358,7 @@ found:
 
     if (ngx_strcmp(p, "END\x0d") == 0) {
         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
-                      "key: \"%V\" was not found by memcached", &r->uri);
+                      "key: \"%V\" was not found by memcached", &ctx->key);
 
         u->headers_in.status_n = 404;
 
--- a/src/http/modules/ngx_http_not_modified_filter_module.c
+++ b/src/http/modules/ngx_http_not_modified_filter_module.c
@@ -52,7 +52,7 @@ static ngx_int_t ngx_http_not_modified_h
     time_t  ims;
 
     if (r->headers_out.status != NGX_HTTP_OK
-        || r->main != r
+        || r != r->main
         || r->headers_in.if_modified_since == NULL
         || r->headers_out.last_modified_time == -1)
     {
--- a/src/http/modules/ngx_http_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -134,7 +134,7 @@ ngx_http_range_header_filter(ngx_http_re
 
     if (r->http_version < NGX_HTTP_VERSION_10
         || r->headers_out.status != NGX_HTTP_OK
-        || r->main != r
+        || r != r->main
         || r->headers_out.content_length_n == -1
         || !r->allow_ranges)
     {
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -8,92 +8,29 @@
 #include <ngx_core.h>
 #include <ngx_http.h>
 
-#define NGX_HTTP_SSI_MAX_PARAMS     16
-
-#define NGX_HTTP_SSI_COMMAND_LEN    31
-#define NGX_HTTP_SSI_PARAM_LEN      31
-#define NGX_HTTP_SSI_PARAMS_N       4
-
 #define NGX_HTTP_SSI_ERROR          1
 
 #define NGX_HTTP_SSI_DATE_LEN       2048
 
-
 #define NGX_HTTP_SSI_ADD_PREFIX     1
 
 
 typedef struct {
-    ngx_flag_t        enable;
-    ngx_flag_t        silent_errors;
-    ngx_flag_t        ignore_recycled_buffers;
-
-    ngx_array_t      *types;     /* array of ngx_str_t */
-
-    size_t            min_file_chunk;
-    size_t            value_len;
-} ngx_http_ssi_conf_t;
-
-
-typedef struct {
-    ngx_str_t          name;
-    ngx_str_t          value;
-} ngx_http_ssi_var_t;
+    ngx_flag_t    enable;
+    ngx_flag_t    silent_errors;
+    ngx_flag_t    ignore_recycled_buffers;
+
+    ngx_array_t  *types;     /* array of ngx_str_t */
+
+    size_t        min_file_chunk;
+    size_t        value_len;
+} ngx_http_ssi_loc_conf_t;
 
 
 typedef struct {
-    ngx_buf_t         *buf;
-
-    u_char            *pos;
-    u_char            *copy_start;
-    u_char            *copy_end;
-
-    ngx_str_t          command;
-    ngx_array_t        params;
-    ngx_table_elt_t   *param;
-    ngx_table_elt_t    params_array[NGX_HTTP_SSI_PARAMS_N];
-
-    ngx_chain_t       *in;
-    ngx_chain_t       *out;
-    ngx_chain_t      **last_out;
-    ngx_chain_t       *busy;
-    ngx_chain_t       *free;
-
-    ngx_uint_t         state;
-    ngx_uint_t         saved_state;
-    size_t             saved;
-    size_t             looked;
-
-    size_t             value_len;
-
-    ngx_array_t        variables;
-
-    ngx_uint_t         output;        /* unsigned  output:1; */
-
-    ngx_str_t          timefmt;
-    ngx_str_t          errmsg;
-} ngx_http_ssi_ctx_t;
-
-
-typedef ngx_int_t (*ngx_http_ssi_command_pt) (ngx_http_request_t *r,
-    ngx_http_ssi_ctx_t *ctx, ngx_str_t **);
-
-
-typedef struct {
-    ngx_str_t                 name;
-    ngx_uint_t                index;
-
-    ngx_uint_t                mandatory;
-} ngx_http_ssi_param_t;
-
-
-typedef struct {
-    ngx_str_t                 name;
-    ngx_http_ssi_command_pt   handler;
-    ngx_http_ssi_param_t     *params;
-
-    unsigned                  conditional:1;
-    unsigned                  flush:1;
-} ngx_http_ssi_command_t;
+    ngx_str_t     name;
+    ngx_str_t     value;
+} ngx_http_ssi_var_t;
 
 
 typedef enum {
@@ -149,7 +86,9 @@ static ngx_int_t ngx_http_ssi_date_gmt_l
 
 static char *ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 
-static ngx_int_t ngx_http_ssi_add_variables(ngx_conf_t *cf);
+static ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf);
+static void *ngx_http_ssi_create_main_conf(ngx_conf_t *cf);
+static char *ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf);
 static void *ngx_http_ssi_create_conf(ngx_conf_t *cf);
 static char *ngx_http_ssi_merge_conf(ngx_conf_t *cf,
     void *parent, void *child);
@@ -162,35 +101,35 @@ static ngx_command_t  ngx_http_ssi_filte
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_ssi_conf_t, enable),
+      offsetof(ngx_http_ssi_loc_conf_t, enable),
       NULL },
 
     { ngx_string("ssi_silent_errors"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_ssi_conf_t, silent_errors),
+      offsetof(ngx_http_ssi_loc_conf_t, silent_errors),
       NULL },
 
     { ngx_string("ssi_ignore_recycled_buffers"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_flag_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_ssi_conf_t, ignore_recycled_buffers),
+      offsetof(ngx_http_ssi_loc_conf_t, ignore_recycled_buffers),
       NULL },
 
     { ngx_string("ssi_min_file_chunk"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_size_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_ssi_conf_t, min_file_chunk),
+      offsetof(ngx_http_ssi_loc_conf_t, min_file_chunk),
       NULL },
 
     { ngx_string("ssi_value_length"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
       ngx_conf_set_size_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
-      offsetof(ngx_http_ssi_conf_t, value_len),
+      offsetof(ngx_http_ssi_loc_conf_t, value_len),
       NULL },
 
     { ngx_string("ssi_types"),
@@ -206,11 +145,11 @@ static ngx_command_t  ngx_http_ssi_filte
 
 
 static ngx_http_module_t  ngx_http_ssi_filter_module_ctx = {
-    ngx_http_ssi_add_variables,            /* preconfiguration */
+    ngx_http_ssi_preconfiguration,         /* preconfiguration */
     NULL,                                  /* postconfiguration */
 
-    NULL,                                  /* create main configuration */
-    NULL,                                  /* init main configuration */
+    ngx_http_ssi_create_main_conf,         /* create main configuration */
+    ngx_http_ssi_init_main_conf,           /* init main configuration */
 
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
@@ -260,41 +199,41 @@ static ngx_str_t ngx_http_ssi_none = ngx
 
 
 static ngx_http_ssi_param_t  ngx_http_ssi_include_params[] = {
-    { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0 },
-    { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0 },
-    { ngx_null_string, 0, 0 }
+    { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 },
+    { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0, 0 },
+    { ngx_null_string, 0, 0, 0 }
 };
 
 
 static ngx_http_ssi_param_t  ngx_http_ssi_echo_params[] = {
-    { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1 },
-    { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0 },
-    { ngx_null_string, 0, 0 }
+    { ngx_string("var"), NGX_HTTP_SSI_ECHO_VAR, 1, 0 },
+    { ngx_string("default"), NGX_HTTP_SSI_ECHO_DEFAULT, 0, 0 },
+    { ngx_null_string, 0, 0, 0 }
 };
 
 
 static ngx_http_ssi_param_t  ngx_http_ssi_config_params[] = {
-    { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG, 0 },
-    { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT, 0 },
-    { ngx_null_string, 0, 0 }
+    { ngx_string("errmsg"), NGX_HTTP_SSI_CONFIG_ERRMSG, 0, 0 },
+    { ngx_string("timefmt"), NGX_HTTP_SSI_CONFIG_TIMEFMT, 0, 0 },
+    { ngx_null_string, 0, 0, 0 }
 };
 
 
 static ngx_http_ssi_param_t  ngx_http_ssi_set_params[] = {
-    { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 1 },
-    { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE, 1 },
-    { ngx_null_string, 0, 0 }
+    { ngx_string("var"), NGX_HTTP_SSI_SET_VAR, 1, 0 },
+    { ngx_string("value"), NGX_HTTP_SSI_SET_VALUE, 1, 0 },
+    { ngx_null_string, 0, 0, 0 }
 };
 
 
 static ngx_http_ssi_param_t  ngx_http_ssi_if_params[] = {
-    { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 1 },
-    { ngx_null_string, 0, 0 }
+    { ngx_string("expr"), NGX_HTTP_SSI_IF_EXPR, 1, 0 },
+    { ngx_null_string, 0, 0, 0 }
 };
 
 
 static ngx_http_ssi_param_t  ngx_http_ssi_no_params[] = {
-    { ngx_null_string, 0, 0 }
+    { ngx_null_string, 0, 0, 0 }
 };
 
 
@@ -330,14 +269,14 @@ static ngx_http_variable_t  ngx_http_ssi
 static ngx_int_t
 ngx_http_ssi_header_filter(ngx_http_request_t *r)
 {
-    ngx_uint_t            i;
-    ngx_str_t            *type;
-    ngx_http_ssi_ctx_t   *ctx;
-    ngx_http_ssi_conf_t  *conf;
-
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
-
-    if (!conf->enable
+    ngx_uint_t                i;
+    ngx_str_t                *type;
+    ngx_http_ssi_ctx_t       *ctx;
+    ngx_http_ssi_loc_conf_t  *slcf;
+
+    slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
+
+    if (!slcf->enable
         || r->headers_out.content_type.len == 0
         || r->headers_out.content_length_n == 0)
     {
@@ -345,8 +284,8 @@ ngx_http_ssi_header_filter(ngx_http_requ
     }
 
 
-    type = conf->types->elts;
-    for (i = 0; i < conf->types->nelts; i++) {
+    type = slcf->types->elts;
+    for (i = 0; i < slcf->types->nelts; i++) {
         if (r->headers_out.content_type.len >= type[i].len
             && ngx_strncasecmp(r->headers_out.content_type.data,
                                type[i].data, type[i].len) == 0)
@@ -368,7 +307,7 @@ found:
     ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);
 
 
-    ctx->value_len = conf->value_len;
+    ctx->value_len = slcf->value_len;
     ctx->last_out = &ctx->out;
 
     ctx->output = 1;
@@ -388,7 +327,7 @@ found:
 
     r->filter_need_in_memory = 1;
 
-    if (r->main == r) {
+    if (r == r->main) {
         ngx_http_clear_content_length(r);
         ngx_http_clear_last_modified(r);
     }
@@ -400,17 +339,18 @@ found:
 static ngx_int_t
 ngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
 {
-    ngx_int_t                rc;
-    ngx_uint_t               i;
-    ngx_buf_t               *b;
-    ngx_chain_t             *cl;
-    ngx_table_elt_t         *param;
-    ngx_connection_t        *c;
-    ngx_http_ssi_ctx_t      *ctx;
-    ngx_http_ssi_conf_t     *conf;
-    ngx_http_ssi_param_t    *prm;
-    ngx_http_ssi_command_t  *cmd;
-    ngx_str_t               *params[NGX_HTTP_SSI_MAX_PARAMS];
+    ngx_int_t                  rc;
+    ngx_buf_t                 *b;
+    ngx_uint_t                 i, index;
+    ngx_chain_t               *cl;
+    ngx_table_elt_t           *param;
+    ngx_connection_t          *c;
+    ngx_http_ssi_ctx_t        *ctx;
+    ngx_http_ssi_param_t      *prm;
+    ngx_http_ssi_command_t    *cmd;
+    ngx_http_ssi_loc_conf_t   *slcf;
+    ngx_http_ssi_main_conf_t  *smcf;
+    ngx_str_t                 *params[NGX_HTTP_SSI_MAX_PARAMS];
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);
 
@@ -426,7 +366,7 @@ ngx_http_ssi_body_filter(ngx_http_reques
         }
     }
 
-    conf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
+    slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http ssi filter \"%V\"", &r->uri);
@@ -528,7 +468,7 @@ ngx_http_ssi_body_filter(ngx_http_reques
                     b->recycled = 0;
 
                     if (b->in_file) {
-                        if (conf->min_file_chunk < (size_t) (b->last - b->pos))
+                        if (slcf->min_file_chunk < (size_t) (b->last - b->pos))
                         {
                             b->file_last = b->file_pos + (b->last - b->start);
                             b->file_pos += b->pos - b->start;
@@ -565,37 +505,39 @@ ngx_http_ssi_body_filter(ngx_http_reques
 
             if (rc == NGX_OK) {
 
-                for (cmd = ngx_http_ssi_commands; cmd->handler; cmd++) {
-                    if (cmd->name.len == 0) {
-                        cmd = (ngx_http_ssi_command_t *) cmd->handler;
+                smcf = ngx_http_get_module_main_conf(r,
+                                                   ngx_http_ssi_filter_module);
+
+                cmd = ngx_hash_find(&smcf->hash, ctx->key, ctx->command.data,
+                                    ctx->command.len);
+
+                if (cmd == NULL) {
+                    if (ctx->output) {
+                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                      "invalid SSI command: \"%V\"",
+                                      &ctx->command);
+                        goto ssi_error;
                     }
 
-                    if (cmd->name.len != ctx->command.len
-                        || ngx_strncmp(cmd->name.data, ctx->command.data,
-                                       ctx->command.len) != 0)
-                    {
-                        continue;
-                    }
-
-                    break;
-                }
-
-                if (cmd->name.len == 0 && ctx->output) {
-                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                                  "invalid SSI command: \"%V\"", &ctx->command);
-                    goto ssi_error;
+                    continue;
                 }
 
                 if (!ctx->output && !cmd->conditional) {
                     continue;
                 }
 
+                if (ctx->params.nelts > NGX_HTTP_SSI_MAX_PARAMS) {
+                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                                  "too many SSI command paramters: \"%V\"",
+                                  &ctx->command);
+                    goto ssi_error;
+                }
+
                 ngx_memzero(params,
                             NGX_HTTP_SSI_MAX_PARAMS * sizeof(ngx_str_t *));
 
                 param = ctx->params.elts;
 
-
                 for (i = 0; i < ctx->params.nelts; i++) {
 
                     for (prm = cmd->params; prm->name.len; prm++) {
@@ -607,17 +549,27 @@ ngx_http_ssi_body_filter(ngx_http_reques
                             continue;
                         }
 
-                        if (params[prm->index]) {
-                            ngx_log_error(NGX_LOG_ERR,
-                                          r->connection->log, 0,
-                                          "duplicate \"%V\" parameter "
-                                          "in \"%V\" SSI command",
-                                          &param[i].key, &ctx->command);
-
-                            goto ssi_error;
+                        if (!prm->multiple) {
+                            if (params[prm->index]) {
+                                ngx_log_error(NGX_LOG_ERR,
+                                              r->connection->log, 0,
+                                              "duplicate \"%V\" parameter "
+                                              "in \"%V\" SSI command",
+                                              &param[i].key, &ctx->command);
+
+                                goto ssi_error;
+                            }
+
+                            params[prm->index] = &param[i].value;
+
+                            break;
                         }
 
-                        params[prm->index] = &param[i].value;
+                        for (index = prm->index; params[index]; index++) {
+                            /* void */
+                        }
+
+                        params[index] = &param[i].value;
 
                         break;
                     }
@@ -673,7 +625,7 @@ ngx_http_ssi_body_filter(ngx_http_reques
 
     ssi_error:
 
-            if (conf->silent_errors) {
+            if (slcf->silent_errors) {
                 continue;
             }
 
@@ -709,7 +661,6 @@ ngx_http_ssi_body_filter(ngx_http_reques
         }
 
         if (ctx->buf->last_buf || ctx->buf->recycled) {
-
             if (b == NULL) {
                 if (ctx->free) {
                     cl = ctx->free;
@@ -741,7 +692,7 @@ ngx_http_ssi_body_filter(ngx_http_reques
             b->last_buf = ctx->buf->last_buf;
             b->shadow = ctx->buf;
 
-            if (conf->ignore_recycled_buffers == 0)  {
+            if (slcf->ignore_recycled_buffers == 0)  {
                 b->recycled = ctx->buf->recycled;
             }
         }
@@ -969,6 +920,9 @@ ngx_http_ssi_parse(ngx_http_request_t *r
                 }
 
                 ctx->command.data[0] = ch;
+                ctx->key = 0;
+                ctx->key = ngx_hash(ctx->key, ch);
+
                 ctx->params.nelts = 0;
                 state = ssi_command_state;
                 break;
@@ -991,6 +945,7 @@ ngx_http_ssi_parse(ngx_http_request_t *r
 
             default:
                 ctx->command.data[ctx->command.len++] = ch;
+                ctx->key = ngx_hash(ctx->key, ch);
 
                 if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) {
                     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -2039,18 +1994,18 @@ ngx_http_ssi_date_gmt_local_variable(ngx
 static char *
 ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    ngx_http_ssi_conf_t *scf = conf;
+    ngx_http_ssi_loc_conf_t *slcf = conf;
 
     ngx_str_t   *value, *type;
     ngx_uint_t   i;
 
-    if (scf->types == NULL) {
-        scf->types = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
-        if (scf->types == NULL) {
+    if (slcf->types == NULL) {
+        slcf->types = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
+        if (slcf->types == NULL) {
             return NGX_CONF_ERROR;
         }
 
-        type = ngx_array_push(scf->types);
+        type = ngx_array_push(slcf->types);
         if (type == NULL) {
             return NGX_CONF_ERROR;
         }
@@ -2067,7 +2022,7 @@ ngx_http_ssi_types(ngx_conf_t *cf, ngx_c
             continue;
         }
 
-        type = ngx_array_push(scf->types);
+        type = ngx_array_push(slcf->types);
         if (type == NULL) {
             return NGX_CONF_ERROR;
         }
@@ -2087,9 +2042,12 @@ ngx_http_ssi_types(ngx_conf_t *cf, ngx_c
 
 
 static ngx_int_t
-ngx_http_ssi_add_variables(ngx_conf_t *cf)
+ngx_http_ssi_preconfiguration(ngx_conf_t *cf)
 {
-    ngx_http_variable_t  *var, *v;
+    ngx_int_t                  rc;
+    ngx_http_variable_t       *var, *v;
+    ngx_http_ssi_command_t    *cmd;
+    ngx_http_ssi_main_conf_t  *smcf;
 
     for (v = ngx_http_ssi_vars; v->name.len; v++) {
         var = ngx_http_add_variable(cf, &v->name, v->flags);
@@ -2101,17 +2059,82 @@ ngx_http_ssi_add_variables(ngx_conf_t *c
         var->data = v->data;
     }
 
+    smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);
+
+    for (cmd = ngx_http_ssi_commands; cmd->name.len; cmd++) {
+        rc = ngx_hash_add_key(&smcf->commands, &cmd->name, cmd,
+                              NGX_HASH_READONLY_KEY);
+
+        if (rc == NGX_OK) {
+            continue;
+        }
+
+        if (rc == NGX_BUSY) {
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "conflicting SSI command \"%V\"", &cmd->name);
+        }
+
+        return NGX_ERROR;
+    }
+
     return NGX_OK;
 }
 
 
 static void *
+ngx_http_ssi_create_main_conf(ngx_conf_t *cf)
+{
+    ngx_http_ssi_main_conf_t  *smcf;
+
+    smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t));
+    if (smcf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    smcf->commands.pool = cf->pool;
+    smcf->commands.temp_pool = cf->temp_pool;
+
+    if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) {
+        return NGX_CONF_ERROR;
+    }
+
+    return smcf;
+}
+
+
+static char *
+ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf)
+{
+    ngx_http_ssi_main_conf_t *smcf = conf;
+
+    ngx_hash_init_t  hash;
+
+    hash.hash = &smcf->hash;
+    hash.key = ngx_hash_key;
+    hash.max_size = 1024;
+    hash.bucket_size = ngx_cacheline_size;
+    hash.name = "ssi_command_hash";
+    hash.pool = cf->pool;
+    hash.temp_pool = NULL;
+
+    if (ngx_hash_init(&hash, smcf->commands.keys.elts,
+                      smcf->commands.keys.nelts)
+        != NGX_OK)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
+
+
+static void *
 ngx_http_ssi_create_conf(ngx_conf_t *cf)
 {
-    ngx_http_ssi_conf_t  *conf;
-
-    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_conf_t));
-    if (conf == NULL) {
+    ngx_http_ssi_loc_conf_t  *slcf;
+
+    slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));
+    if (slcf == NULL) {
         return NGX_CONF_ERROR;
     }
 
@@ -2121,22 +2144,22 @@ ngx_http_ssi_create_conf(ngx_conf_t *cf)
      *     conf->types = NULL;
      */
 
-    conf->enable = NGX_CONF_UNSET;
-    conf->silent_errors = NGX_CONF_UNSET;
-    conf->ignore_recycled_buffers = NGX_CONF_UNSET;
-
-    conf->min_file_chunk = NGX_CONF_UNSET_SIZE;
-    conf->value_len = NGX_CONF_UNSET_SIZE;
-
-    return conf;
+    slcf->enable = NGX_CONF_UNSET;
+    slcf->silent_errors = NGX_CONF_UNSET;
+    slcf->ignore_recycled_buffers = NGX_CONF_UNSET;
+
+    slcf->min_file_chunk = NGX_CONF_UNSET_SIZE;
+    slcf->value_len = NGX_CONF_UNSET_SIZE;
+
+    return slcf;
 }
 
 
 static char *
 ngx_http_ssi_merge_conf(ngx_conf_t *cf, void *parent, void *child)
 {
-    ngx_http_ssi_conf_t *prev = parent;
-    ngx_http_ssi_conf_t *conf = child;
+    ngx_http_ssi_loc_conf_t *prev = parent;
+    ngx_http_ssi_loc_conf_t *conf = child;
 
     ngx_str_t  *type;
 
new file mode 100644
--- /dev/null
+++ b/src/http/modules/ngx_http_ssi_filter_module.h
@@ -0,0 +1,90 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_HTTP_SSI_FILTER_H_INCLUDED_
+#define _NGX_HTTP_SSI_FILTER_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_SSI_MAX_PARAMS     16
+
+#define NGX_HTTP_SSI_COMMAND_LEN    31
+#define NGX_HTTP_SSI_PARAM_LEN      31
+#define NGX_HTTP_SSI_PARAMS_N       4
+
+
+typedef struct {
+    ngx_hash_t                hash;
+    ngx_hash_keys_arrays_t    commands;
+} ngx_http_ssi_main_conf_t;
+
+
+typedef struct {
+    ngx_buf_t                *buf;
+
+    u_char                   *pos;
+    u_char                   *copy_start;
+    u_char                   *copy_end;
+
+    ngx_uint_t                key;
+    ngx_str_t                 command;
+    ngx_array_t               params;
+    ngx_table_elt_t          *param;
+    ngx_table_elt_t           params_array[NGX_HTTP_SSI_PARAMS_N];
+
+    ngx_chain_t              *in;
+    ngx_chain_t              *out;
+    ngx_chain_t             **last_out;
+    ngx_chain_t              *busy;
+    ngx_chain_t              *free;
+
+    ngx_uint_t                state;
+    ngx_uint_t                saved_state;
+    size_t                    saved;
+    size_t                    looked;
+
+    size_t                    value_len;
+
+    ngx_array_t               variables;
+
+    ngx_uint_t                output;        /* unsigned  output:1; */
+
+    ngx_str_t                 timefmt;
+    ngx_str_t                 errmsg;
+} ngx_http_ssi_ctx_t;
+
+
+typedef ngx_int_t (*ngx_http_ssi_command_pt) (ngx_http_request_t *r,
+    ngx_http_ssi_ctx_t *ctx, ngx_str_t **);
+
+
+typedef struct {
+    ngx_str_t                 name;
+    ngx_uint_t                index;
+
+    unsigned                  mandatory:1;
+    unsigned                  multiple:1;
+} ngx_http_ssi_param_t;
+
+
+typedef struct {
+    ngx_str_t                 name;
+    ngx_http_ssi_command_pt   handler;
+    ngx_http_ssi_param_t     *params;
+
+    unsigned                  conditional:1;
+    unsigned                  flush:1;
+} ngx_http_ssi_command_t;
+
+
+extern ngx_module_t  ngx_http_ssi_filter_module;
+
+
+#endif /* _NGX_HTTP_SSI_FILTER_H_INCLUDED_ */
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -244,7 +244,7 @@ ngx_http_static_handler(ngx_http_request
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (r->main != r && ngx_file_size(&fi) == 0) {
+    if (r != r->main && ngx_file_size(&fi) == 0) {
         return ngx_http_send_header(r);
     }
 
@@ -272,7 +272,7 @@ ngx_http_static_handler(ngx_http_request
     b->file_last = ngx_file_size(&fi);
 
     b->in_file = b->file_last ? 1: 0;
-    b->last_buf = (r->main == r) ? 1: 0;
+    b->last_buf = (r == r->main) ? 1: 0;
     b->last_in_chain = 1;
 
     b->file->fd = fd;
--- a/src/http/modules/ngx_http_userid_filter_module.c
+++ b/src/http/modules/ngx_http_userid_filter_module.c
@@ -213,7 +213,7 @@ ngx_http_userid_filter(ngx_http_request_
     ngx_http_userid_ctx_t   *ctx;
     ngx_http_userid_conf_t  *conf;
 
-    if (r->main != r) {
+    if (r != r->main) {
         return ngx_http_next_header_filter(r);
     }
 
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -38,6 +38,9 @@ typedef u_char *(*ngx_http_log_handler_p
 #include <ngx_http_core_module.h>
 #include <ngx_http_script.h>
 
+#if (NGX_HTTP_SSI)
+#include <ngx_http_ssi_filter_module.h>
+#endif
 #if (NGX_HTTP_SSL)
 #include <ngx_http_ssl_module.h>
 #endif
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -469,7 +469,7 @@ ngx_http_handler(ngx_http_request_t *r)
     r->uri_changed = 1;
     r->uri_changes = NGX_HTTP_MAX_REWRITE_CYCLES + 1;
 
-    r->phase = (r->main == r) ? NGX_HTTP_POST_READ_PHASE:
+    r->phase = (r == r->main) ? NGX_HTTP_POST_READ_PHASE:
                                 NGX_HTTP_SERVER_REWRITE_PHASE;
     r->phase_handler = 0;
 
@@ -516,7 +516,7 @@ ngx_http_core_run_phases(ngx_http_reques
             r->phase = NGX_HTTP_FIND_CONFIG_PHASE;
         }
 
-        if (r->phase == NGX_HTTP_ACCESS_PHASE && r->main != r) {
+        if (r->phase == NGX_HTTP_ACCESS_PHASE && r != r->main) {
             continue;
         }
 
@@ -1229,18 +1229,19 @@ ngx_http_subrequest(ngx_http_request_t *
     }
 
     sr->internal = 1;
+    sr->fast_subrequest = 1;
 
     sr->discard_body = r->discard_body;
     sr->main_filter_need_in_memory = r->main_filter_need_in_memory;
 
     ngx_http_handler(sr);
 
-#if (NGX_LOG_DEBUG)
     if (!c->destroyed) {
+        sr->fast_subrequest = 0;
+
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                        "http subrequest done \"%V?%V\"", uri, &sr->args);
     }
-#endif
 
     return NGX_OK;
 }
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -159,7 +159,7 @@ ngx_http_header_filter(ngx_http_request_
     ngx_table_elt_t           *header;
     ngx_http_core_loc_conf_t  *clcf;
 
-    if (r->main != r) {
+    if (r != r->main) {
         return NGX_OK;
     }
 
--- a/src/http/ngx_http_postpone_filter_module.c
+++ b/src/http/ngx_http_postpone_filter_module.c
@@ -96,7 +96,9 @@ ngx_http_postpone_filter(ngx_http_reques
 
     if (r->postponed) {
         out = r->postponed->out;
-        r->postponed = r->postponed->next;
+        if (out) {
+            r->postponed = r->postponed->next;
+        }
 
     } else {
         out = in;
@@ -104,7 +106,10 @@ ngx_http_postpone_filter(ngx_http_reques
 
     rc = NGX_OK;
 
-    if (out || r->main->out || r->main->connection->buffered) {
+    if (out
+        || (r->connection->buffered
+            & (NGX_HTTP_LOWLEVEL_BUFFERED|NGX_LOWLEVEL_BUFFERED)))
+    {
 
         ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http postpone filter out \"%V?%V\"", &r->uri, &r->args);
@@ -170,6 +175,10 @@ ngx_http_postpone_filter_output_postpone
             pr = r->postponed;
         }
 
+        if (pr == NULL) {
+            return NGX_OK;
+        }
+
         out = pr->out;
 
         if (out) {
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1423,7 +1423,7 @@ ngx_http_finalize_request(ngx_http_reque
             return;
         }
 
-        if (r->main == r) {
+        if (r == r->main) {
             if (r->connection->read->timer_set) {
                 ngx_del_timer(r->connection->read);
             }
@@ -1438,7 +1438,7 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
-    if (r->main != r || rc == NGX_AGAIN) {
+    if (r != r->main || rc == NGX_AGAIN) {
         if (ngx_http_set_write_handler(r) != NGX_OK) {
             return;
         }
@@ -1453,7 +1453,7 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
-    if (r->main != r) {
+    if (r != r->main) {
 
         pr = r->parent;
 
@@ -1461,7 +1461,7 @@ ngx_http_finalize_request(ngx_http_reque
                        "http parent request: \"%V?%V\"", &pr->uri, &pr->args);
 
         if (rc != NGX_AGAIN) {
-            pr->connection->data = pr;
+            r->connection->data = pr;
         }
 
         if (pr->postponed) {
@@ -1472,13 +1472,23 @@ ngx_http_finalize_request(ngx_http_reque
 
             if (rc != NGX_AGAIN && pr->postponed->request == r) {
                 pr->postponed = pr->postponed->next;
-
-                if (pr->postponed == NULL) {
-                    return;
-                }
+            }
+
+            if (r->fast_subrequest) {
+                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http fast subrequest: \"%V?%V\" done",
+                           &r->uri, &r->args);
+                return;
             }
 
             ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                           "http wake parent request: \"%V?%V\"",
+                           &pr->uri, &pr->args);
+
+            pr->write_event_handler(pr);
+
+#if 0
+            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "http request: \"%V?%V\" still has postponed",
                            &pr->uri, &pr->args);
 
@@ -1489,6 +1499,8 @@ ngx_http_finalize_request(ngx_http_reque
 
                 pr->write_event_handler(pr);
             }
+#endif
+
         }
 
         return;
@@ -1498,7 +1510,7 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
-    if (r->out) {
+    if (r->connection->buffered) {
         (void) ngx_http_set_write_handler(r);
         return;
     }
@@ -1655,7 +1667,11 @@ ngx_http_writer(ngx_http_request_t *r)
             ngx_http_close_request(r, 0);
         }
 
-        return;
+        if (r == r->main) {
+            return;
+        }
+
+        rc = NGX_OK;
     }
 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,
@@ -1691,7 +1707,7 @@ ngx_http_discard_body(ngx_http_request_t
     ssize_t       size;
     ngx_event_t  *rev;
 
-    if (r->main != r) {
+    if (r != r->main) {
         return NGX_OK;
     }
 
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -99,6 +99,13 @@
 #define NGX_HTTP_GATEWAY_TIME_OUT          504
 
 
+#define NGX_HTTP_LOWLEVEL_BUFFERED         0x000000f0
+#define NGX_HTTP_WRITE_BUFFERED            0x00000010
+#define NGX_HTTP_GZIP_BUFFERED             0x00000020
+#define NGX_HTTP_SSI_BUFFERED              0x00000100
+#define NGX_HTTP_COPY_BUFFERED             0x00000200
+
+
 typedef enum {
     NGX_HTTP_RESTRICT_HOST_OFF = 0,
     NGX_HTTP_RESTRICT_HOST_ON,
@@ -379,6 +386,8 @@ struct ngx_http_request_s {
     unsigned                          uri_changed:1;
     unsigned                          uri_changes:4;
 
+    unsigned                          fast_subrequest:1;
+
     unsigned                          low_case_exten:1;
     unsigned                          header_timeout_set:1;
 
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -447,7 +447,7 @@ ngx_http_special_response_handler(ngx_ht
         cl->buf = b;
     }
 
-    if (r->main == r) {
+    if (r == r->main) {
         b->last_buf = 1;
     }
 
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -558,7 +558,7 @@ ngx_http_upstream_connect(ngx_http_reque
         }
     }
 
-    if (r->request_body && r->request_body->temp_file && r->main == r) {
+    if (r->request_body && r->request_body->temp_file && r == r->main) {
 
         /*
          * the r->request_body->buf can be reused for one request only,
@@ -695,7 +695,7 @@ ngx_http_upstream_reinit(ngx_http_reques
     /* reinit the subrequest's ngx_output_chain() context */
 
     if (r->request_body && r->request_body->temp_file
-        && r->main != r && u->output.buf)
+        && r != r->main && u->output.buf)
     {
         u->output.free = ngx_alloc_chain_link(r->pool);
         if (u->output.free == NULL) {
@@ -1031,12 +1031,20 @@ ngx_http_upstream_process_header(ngx_eve
 #endif
     }
 
-    if (u->headers_in.status_n == NGX_HTTP_NOT_FOUND
-        && u->peer.tries > 1
-        && u->conf->next_upstream & NGX_HTTP_UPSTREAM_FT_HTTP_404)
-    {
-        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_HTTP_404);
-        return;
+    if (u->headers_in.status_n == NGX_HTTP_NOT_FOUND) {
+
+        if (u->peer.tries > 1
+            && u->conf->next_upstream & NGX_HTTP_UPSTREAM_FT_HTTP_404)
+        {
+            ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_HTTP_404);
+            return;
+        }
+
+        if (u->conf->redirect_404) {
+            rc = (r->err_ctx == NULL) ? 404 : 204;
+            ngx_http_upstream_finalize_request(r, u, rc);
+            return;
+        }
     }
 
 
@@ -1044,13 +1052,6 @@ ngx_http_upstream_process_header(ngx_eve
         && u->conf->redirect_errors
         && r->err_ctx == NULL)
     {
-        if (u->headers_in.status_n == NGX_HTTP_NOT_FOUND
-            && u->conf->redirect_404)
-        {
-            ngx_http_upstream_finalize_request(r, u, 404);
-            return;
-        }
-
         clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
         if (clcf->error_pages) {
@@ -1947,7 +1948,7 @@ ngx_http_upstream_finalize_request(ngx_h
 
     r->connection->log->action = "sending to client";
 
-    if (rc == 0 && r->main == r) {
+    if (rc == 0 && r == r->main) {
         rc = ngx_http_send_special(r, NGX_HTTP_LAST);
     }
 
--- a/src/http/ngx_http_write_filter_module.c
+++ b/src/http/ngx_http_write_filter_module.c
@@ -179,20 +179,24 @@ ngx_http_write_filter(ngx_http_request_t
     }
 
     if (c->write->delayed) {
+        c->buffered |= NGX_HTTP_WRITE_BUFFERED;
         return NGX_AGAIN;
     }
 
-    if (size == 0 && !c->buffered) {
+    if (size == 0 && !(c->buffered & NGX_LOWLEVEL_BUFFERED)) {
         if (last) {
             r->out = NULL;
+            c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
+
             return NGX_OK;
         }
 
         if (flush) {
             do {
                 r->out = r->out->next;
-            }
-            while (r->out);
+            } while (r->out);
+
+            c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
 
             return NGX_OK;
         }
@@ -232,7 +236,14 @@ ngx_http_write_filter(ngx_http_request_t
 
     r->out = chain;
 
-    if (chain || (c->buffered && r->postponed == NULL)) {
+    if (chain) {
+        c->buffered |= NGX_HTTP_WRITE_BUFFERED;
+        return NGX_AGAIN;
+    }
+
+    c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
+
+    if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
         return NGX_AGAIN;
     }
 
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -28,8 +28,13 @@
  */
 
 
-#define NGX_HEADERS   8
-#define NGX_TRAILERS  8
+#if (IOV_MAX > 64)
+#define NGX_HEADERS   64
+#define NGX_TRAILERS  64
+#else
+#define NGX_HEADERS   IOV_MAX
+#define NGX_TRAILERS  IOV_MAX
+#endif
 
 
 ngx_chain_t *
--- a/src/os/unix/ngx_linux_sendfile_chain.c
+++ b/src/os/unix/ngx_linux_sendfile_chain.c
@@ -20,7 +20,11 @@
  */
 
 
-#define NGX_HEADERS   8
+#if (IOV_MAX > 64)
+#define NGX_HEADERS  64
+#else
+#define NGX_HEADERS  IOV_MAX
+#endif
 
 
 ngx_chain_t *
--- a/src/os/unix/ngx_solaris_sendfilev_chain.c
+++ b/src/os/unix/ngx_solaris_sendfilev_chain.c
@@ -31,7 +31,12 @@ static ssize_t sendfilev(int fd, const s
 #endif
 
 
-#define NGX_SENDFILEVECS   16
+#if (IOV_MAX > 64)
+#define NGX_SENDFILEVECS  64
+#else
+#define NGX_SENDFILEVECS  IOV_MAX
+#endif
+
 
 
 ngx_chain_t *
--- a/src/os/unix/ngx_writev_chain.c
+++ b/src/os/unix/ngx_writev_chain.c
@@ -9,7 +9,11 @@
 #include <ngx_event.h>
 
 
-#define NGX_IOVS  16
+#if (IOV_MAX > 64)
+#define NGX_IOVS  64
+#else
+#define NGX_IOVS  IOV_MAX
+#endif
 
 
 ngx_chain_t *