nginx-0.3.20-RELEASE import

    *) Bugfix: in SSI handling.

    *) Bugfix: the ngx_http_memcached_module did not support the keys in
       the "/usr?args" form.
diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c
index 8a04807..8e2ad02 100644
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -503,7 +503,7 @@
 
     b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);
 
-    if (r->main == r) {
+    if (r == r->main) {
         b->last_buf = 1;
     }
 
diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c
index 8eee358..cf41b62 100644
--- a/src/http/modules/ngx_http_charset_filter_module.c
+++ b/src/http/modules/ngx_http_charset_filter_module.c
@@ -162,7 +162,7 @@
         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);
diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c
index 85ca934..89c4a04 100644
--- 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_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);
     }
 
diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c
index 940e082..663f7f2 100644
--- 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_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 @@
             && 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 @@
             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 @@
 
                 ctx->done = 1;
 
+                r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;
+
                 break;
             }
 
diff --git a/src/http/modules/ngx_http_headers_filter_module.c b/src/http/modules/ngx_http_headers_filter_module.c
index 86c1a80..b0bd0a5 100644
--- a/src/http/modules/ngx_http_headers_filter_module.c
+++ b/src/http/modules/ngx_http_headers_filter_module.c
@@ -97,7 +97,7 @@
 
     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);
     }
diff --git a/src/http/modules/ngx_http_memcached_module.c b/src/http/modules/ngx_http_memcached_module.c
index 6217dd1..89a568d 100644
--- a/src/http/modules/ngx_http_memcached_module.c
+++ b/src/http/modules/ngx_http_memcached_module.c
@@ -19,6 +19,7 @@
 typedef struct {
     size_t                     rest;
     ngx_http_request_t        *request;
+    ngx_str_t                  key;
 } ngx_http_memcached_ctx_t;
 
 
@@ -202,6 +203,8 @@
     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 @@
 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 @@
 
     *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 @@
         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 @@
 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 @@
 
     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 @@
             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 @@
 
     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;
 
diff --git a/src/http/modules/ngx_http_not_modified_filter_module.c b/src/http/modules/ngx_http_not_modified_filter_module.c
index 4a6bb5c..6dd8a32 100644
--- 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 @@
     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)
     {
diff --git a/src/http/modules/ngx_http_range_filter_module.c b/src/http/modules/ngx_http_range_filter_module.c
index 7b21eae..a76b67a 100644
--- a/src/http/modules/ngx_http_range_filter_module.c
+++ b/src/http/modules/ngx_http_range_filter_module.c
@@ -134,7 +134,7 @@
 
     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)
     {
diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c
index c984f7e..18e8729 100644
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -8,94 +8,31 @@
 #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_flag_t    enable;
+    ngx_flag_t    silent_errors;
+    ngx_flag_t    ignore_recycled_buffers;
 
-    ngx_array_t      *types;     /* array of ngx_str_t */
+    ngx_array_t  *types;     /* array of ngx_str_t */
 
-    size_t            min_file_chunk;
-    size_t            value_len;
-} ngx_http_ssi_conf_t;
+    size_t        min_file_chunk;
+    size_t        value_len;
+} ngx_http_ssi_loc_conf_t;
 
 
 typedef struct {
-    ngx_str_t          name;
-    ngx_str_t          value;
+    ngx_str_t     name;
+    ngx_str_t     value;
 } ngx_http_ssi_var_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;
-
-
 typedef enum {
     ssi_start_state = 0,
     ssi_tag_state,
@@ -149,7 +86,9 @@
 
 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 @@
       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_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_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_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;
+    ngx_uint_t                i;
+    ngx_str_t                *type;
+    ngx_http_ssi_ctx_t       *ctx;
+    ngx_http_ssi_loc_conf_t  *slcf;
 
-    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);
 
-    if (!conf->enable
+    if (!slcf->enable
         || r->headers_out.content_type.len == 0
         || r->headers_out.content_length_n == 0)
     {
@@ -345,8 +284,8 @@
     }
 
 
-    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 @@
     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 @@
 
     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 @@
 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 @@
         }
     }
 
-    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 @@
                     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 @@
 
             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 @@
                             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);
+                        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;
+                                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 @@
 
     ssi_error:
 
-            if (conf->silent_errors) {
+            if (slcf->silent_errors) {
                 continue;
             }
 
@@ -709,7 +661,6 @@
         }
 
         if (ctx->buf->last_buf || ctx->buf->recycled) {
-
             if (b == NULL) {
                 if (ctx->free) {
                     cl = ctx->free;
@@ -741,7 +692,7 @@
             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 @@
                 }
 
                 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 @@
 
             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 @@
 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 @@
             continue;
         }
 
-        type = ngx_array_push(scf->types);
+        type = ngx_array_push(slcf->types);
         if (type == NULL) {
             return NGX_CONF_ERROR;
         }
@@ -2087,9 +2042,12 @@
 
 
 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 @@
         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;
+    ngx_http_ssi_loc_conf_t  *slcf;
 
-    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_conf_t));
-    if (conf == NULL) {
+    slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));
+    if (slcf == NULL) {
         return NGX_CONF_ERROR;
     }
 
@@ -2121,22 +2144,22 @@
      *     conf->types = NULL;
      */
 
-    conf->enable = NGX_CONF_UNSET;
-    conf->silent_errors = NGX_CONF_UNSET;
-    conf->ignore_recycled_buffers = NGX_CONF_UNSET;
+    slcf->enable = NGX_CONF_UNSET;
+    slcf->silent_errors = NGX_CONF_UNSET;
+    slcf->ignore_recycled_buffers = NGX_CONF_UNSET;
 
-    conf->min_file_chunk = NGX_CONF_UNSET_SIZE;
-    conf->value_len = NGX_CONF_UNSET_SIZE;
+    slcf->min_file_chunk = NGX_CONF_UNSET_SIZE;
+    slcf->value_len = NGX_CONF_UNSET_SIZE;
 
-    return conf;
+    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;
 
diff --git a/src/http/modules/ngx_http_ssi_filter_module.h b/src/http/modules/ngx_http_ssi_filter_module.h
new file mode 100644
index 0000000..bd73648
--- /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_ */
diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c
index ed360fa..42cc676 100644
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -244,7 +244,7 @@
         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 @@
     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;
diff --git a/src/http/modules/ngx_http_userid_filter_module.c b/src/http/modules/ngx_http_userid_filter_module.c
index 159bd34..a7cda32 100644
--- 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_ctx_t   *ctx;
     ngx_http_userid_conf_t  *conf;
 
-    if (r->main != r) {
+    if (r != r->main) {
         return ngx_http_next_header_filter(r);
     }
 
diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h
index b9e124d..df75870 100644
--- a/src/http/ngx_http.h
+++ b/src/http/ngx_http.h
@@ -38,6 +38,9 @@
 #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
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 26d41b1..dc7d972 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -469,7 +469,7 @@
     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 @@
             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 @@
     }
 
     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;
 }
diff --git a/src/http/ngx_http_header_filter_module.c b/src/http/ngx_http_header_filter_module.c
index b14b3cf..d835c43 100644
--- a/src/http/ngx_http_header_filter_module.c
+++ b/src/http/ngx_http_header_filter_module.c
@@ -159,7 +159,7 @@
     ngx_table_elt_t           *header;
     ngx_http_core_loc_conf_t  *clcf;
 
-    if (r->main != r) {
+    if (r != r->main) {
         return NGX_OK;
     }
 
diff --git a/src/http/ngx_http_postpone_filter_module.c b/src/http/ngx_http_postpone_filter_module.c
index b90d33e..20a78a0 100644
--- a/src/http/ngx_http_postpone_filter_module.c
+++ b/src/http/ngx_http_postpone_filter_module.c
@@ -96,7 +96,9 @@
 
     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 @@
 
     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 @@
             pr = r->postponed;
         }
 
+        if (pr == NULL) {
+            return NGX_OK;
+        }
+
         out = pr->out;
 
         if (out) {
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index f182633..7fddde0 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1423,7 +1423,7 @@
             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 @@
         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 @@
         return;
     }
 
-    if (r->main != r) {
+    if (r != r->main) {
 
         pr = r->parent;
 
@@ -1461,7 +1461,7 @@
                        "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,12 +1472,22 @@
 
             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 @@
 
                 pr->write_event_handler(pr);
             }
+#endif
+
         }
 
         return;
@@ -1498,7 +1510,7 @@
         return;
     }
 
-    if (r->out) {
+    if (r->connection->buffered) {
         (void) ngx_http_set_write_handler(r);
         return;
     }
@@ -1655,7 +1667,11 @@
             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 @@
     ssize_t       size;
     ngx_event_t  *rev;
 
-    if (r->main != r) {
+    if (r != r->main) {
         return NGX_OK;
     }
 
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index ed50843..9f4ae21 100644
--- 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 @@
     unsigned                          uri_changed:1;
     unsigned                          uri_changes:4;
 
+    unsigned                          fast_subrequest:1;
+
     unsigned                          low_case_exten:1;
     unsigned                          header_timeout_set:1;
 
diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c
index f4ad1a7..63a3a34 100644
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -447,7 +447,7 @@
         cl->buf = b;
     }
 
-    if (r->main == r) {
+    if (r == r->main) {
         b->last_buf = 1;
     }
 
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index cdf9a07..3c3a9e6 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -558,7 +558,7 @@
         }
     }
 
-    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 @@
     /* 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 @@
 #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 @@
         && 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 @@
 
     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);
     }
 
diff --git a/src/http/ngx_http_write_filter_module.c b/src/http/ngx_http_write_filter_module.c
index c4d0a0e..30f1a72 100644
--- a/src/http/ngx_http_write_filter_module.c
+++ b/src/http/ngx_http_write_filter_module.c
@@ -179,20 +179,24 @@
     }
 
     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 @@
 
     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;
     }