changeset 248:acd2ec3541cb NGINX_0_4_9

nginx 0.4.9 *) Feature: the "set" parameter in the "include" SSI command. *) Feature: the ngx_http_perl_module now tests the nginx.pm module version.
author Igor Sysoev <http://sysoev.ru>
date Fri, 13 Oct 2006 00:00:00 +0400
parents fcca101509a4
children 7a34085272cb
files CHANGES CHANGES.ru README src/core/nginx.h src/core/ngx_crc.h src/http/modules/ngx_http_addition_filter_module.c src/http/modules/ngx_http_autoindex_module.c src/http/modules/ngx_http_dav_module.c src/http/modules/ngx_http_fastcgi_module.c src/http/modules/ngx_http_flv_module.c src/http/modules/ngx_http_index_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/perl/nginx.pm src/http/modules/perl/nginx.xs src/http/modules/perl/ngx_http_perl_module.c src/http/modules/perl/ngx_http_perl_module.h src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_request.c src/http/ngx_http_request.h src/http/ngx_http_upstream.c src/http/ngx_http_variables.c
diffstat 24 files changed, 386 insertions(+), 115 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,12 @@
 
+Changes with nginx 0.4.9                                         13 Oct 2006
+
+    *) Feature: the "set" parameter in the "include" SSI command.
+
+    *) Feature: the ngx_http_perl_module now tests the nginx.pm module 
+       version.
+
+
 Changes with nginx 0.4.8                                         11 Oct 2006
 
     *) Bugfix: if an "include" SSI command were before another "include" 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,12 @@
 
+Изменения в nginx 0.4.9                                           13.10.2006
+
+    *) Добавление: параметр set в команде SSI include.
+
+    *) Добавление: модуль ngx_http_perl_module теперь проверяет версию 
+       модуля nginx.pm.
+
+
 Изменения в nginx 0.4.8                                           11.10.2006
 
     *) Исправление: если до команды SSI include с параметром wait 
--- a/README
+++ b/README
@@ -1,3 +1,4 @@
 
-Documentation is available at http://sysoev.ru/nginx/ only.
+The Russian documentation is available at http://sysoev.ru/nginx/
+The English documentation is available at http://nginx.net
 
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VER          "nginx/0.4.8"
+#define NGINX_VERSION      "0.4.9"
+#define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
 #define NGX_OLDPID_EXT     ".oldbin"
--- a/src/core/ngx_crc.h
+++ b/src/core/ngx_crc.h
@@ -10,15 +10,16 @@
 
 /* 32-bit crc16 */
 
-static ngx_inline uint32_t ngx_crc(char *data, size_t len)
+static ngx_inline uint32_t
+ngx_crc(char *data, size_t len)
 {
     uint32_t  sum;
 
     for (sum = 0; len; len--) {
 
         /*
-         * gcc 2.95.2 x86 and icc 7.1.006 compile that operator
-         *                                into the single "rol" opcode.
+         * gcc 2.95.2 x86 and icc 7.1.006 compile
+         * that operator into the single "rol" opcode,
          * msvc 6.0sp2 compiles it into four opcodes.
          */
         sum = sum >> 1 | sum << 31;
--- a/src/http/modules/ngx_http_addition_filter_module.c
+++ b/src/http/modules/ngx_http_addition_filter_module.c
@@ -144,10 +144,10 @@ ngx_http_addition_body_filter(ngx_http_r
         ctx->before_body_sent = 1;
 
         if (conf->before_body.len) {
-            if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0)
-                == NGX_ERROR)
-            {
-                return NGX_ERROR;
+            rc = ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0);
+
+            if (rc == NGX_ERROR || rc == NGX_DONE) {
+                return rc;
             }
         }
     }
@@ -168,10 +168,10 @@ ngx_http_addition_body_filter(ngx_http_r
         return rc;
     }
 
-    if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0)
-        == NGX_ERROR)
-    {
-        return NGX_ERROR;
+    rc = ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0);
+
+    if (rc == NGX_ERROR || rc == NGX_DONE) {
+        return rc;
     }
 
     ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -135,7 +135,7 @@ ngx_http_autoindex_handler(ngx_http_requ
 {
     u_char                         *last, *filename, scale;
     off_t                           length;
-    size_t                          len, copy, allocated;
+    size_t                          len, copy, allocated, root;
     ngx_tm_t                        tm;
     ngx_err_t                       err;
     ngx_buf_t                      *b;
@@ -174,7 +174,8 @@ ngx_http_autoindex_handler(ngx_http_requ
 
     /* NGX_DIR_MASK_LEN is lesser than NGX_HTTP_AUTOINDEX_PREALLOCATE */
 
-    last = ngx_http_map_uri_to_path(r, &path, NGX_HTTP_AUTOINDEX_PREALLOCATE);
+    last = ngx_http_map_uri_to_path(r, &path, &root,
+                                    NGX_HTTP_AUTOINDEX_PREALLOCATE);
     if (last == NULL) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -102,6 +102,7 @@ static ngx_int_t
 ngx_http_dav_handler(ngx_http_request_t *r)
 {
     char                     *failed;
+    size_t                    root;
     ngx_int_t                 rc;
     ngx_str_t                 path;
     ngx_file_info_t           fi;
@@ -152,7 +153,7 @@ ngx_http_dav_handler(ngx_http_request_t 
             return rc;
         }
 
-        ngx_http_map_uri_to_path(r, &path, 0);
+        ngx_http_map_uri_to_path(r, &path, &root, 0);
 
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http delete filename: \"%s\"", path.data);
@@ -219,7 +220,7 @@ ngx_http_dav_handler(ngx_http_request_t 
             return rc;
         }
 
-        ngx_http_map_uri_to_path(r, &path, 0);
+        ngx_http_map_uri_to_path(r, &path, &root, 0);
 
         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                        "http mkcol path: \"%s\"", path.data);
@@ -245,6 +246,7 @@ ngx_http_dav_put_handler(ngx_http_reques
 {
     char                     *failed;
     u_char                   *name;
+    size_t                    root;
     time_t                    date;
     ngx_err_t                 err;
     ngx_str_t                *temp, path;
@@ -252,7 +254,7 @@ ngx_http_dav_put_handler(ngx_http_reques
     ngx_file_info_t           fi;
     ngx_http_dav_loc_conf_t  *dlcf;
 
-    ngx_http_map_uri_to_path(r, &path, 0);
+    ngx_http_map_uri_to_path(r, &path, &root, 0);
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http put filename: \"%s\"", path.data);
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -395,6 +395,13 @@ ngx_http_fastcgi_handler(ngx_http_reques
     ngx_http_upstream_t          *u;
     ngx_http_fastcgi_loc_conf_t  *flcf;
 
+    if (r->subrequest_in_memory) {
+        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                      "ngx_http_fastcgi_module does not support "
+                      "subrequest in memeory");
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
+    }
+
     flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);
 
     u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));
--- a/src/http/modules/ngx_http_flv_module.c
+++ b/src/http/modules/ngx_http_flv_module.c
@@ -62,6 +62,7 @@ ngx_http_flv_handler(ngx_http_request_t 
 {
     u_char                    *p;
     off_t                      start, len;
+    size_t                     root;
     ngx_fd_t                   fd;
     ngx_int_t                  rc;
     ngx_uint_t                 level, i;
@@ -94,7 +95,7 @@ ngx_http_flv_handler(ngx_http_request_t 
         return rc;
     }
 
-    if (ngx_http_map_uri_to_path(r, &path, 0) == NULL) {
+    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -28,6 +28,8 @@ typedef struct {
     ngx_str_t                path;
     ngx_str_t                index;
 
+    size_t                   root;
+
     ngx_uint_t               tested;     /* unsigned  tested:1 */
 } ngx_http_index_ctx_t;
 
@@ -200,7 +202,7 @@ ngx_http_index_handler(ngx_http_request_
 
         if (len > (size_t) (ctx->path.data + ctx->path.len - ctx->index.data)) {
 
-            last = ngx_http_map_uri_to_path(r, &ctx->path, len);
+            last = ngx_http_map_uri_to_path(r, &ctx->path, &ctx->root, len);
             if (last == NULL) {
                 return NGX_ERROR;
             }
@@ -291,7 +293,7 @@ ngx_http_index_handler(ngx_http_request_
         uri.len = r->uri.len + ctx->index.len - 1;
 
         if (!clcf->alias) {
-            uri.data = ctx->path.data + r->root_length;
+            uri.data = ctx->path.data + ctx->root;
 
         } else {
             uri.data = ngx_palloc(r->pool, uri.len);
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -78,6 +78,10 @@ static ngx_int_t ngx_http_ssi_evaluate_s
 
 static ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,
     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
+static ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data,
+    ngx_int_t rc);
+static ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data,
+    ngx_int_t rc);
 static ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,
     ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);
 static ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,
@@ -197,11 +201,14 @@ static ngx_http_output_body_filter_pt   
 static u_char ngx_http_ssi_string[] = "<!--";
 
 static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");
+static ngx_str_t ngx_http_ssi_null_string = ngx_null_string;
+
 
 #define  NGX_HTTP_SSI_INCLUDE_VIRTUAL  0
 #define  NGX_HTTP_SSI_INCLUDE_FILE     1
 #define  NGX_HTTP_SSI_INCLUDE_WAIT     2
-#define  NGX_HTTP_SSI_INCLUDE_STUB     3
+#define  NGX_HTTP_SSI_INCLUDE_SET      3
+#define  NGX_HTTP_SSI_INCLUDE_STUB     4
 
 #define  NGX_HTTP_SSI_ECHO_VAR         0
 #define  NGX_HTTP_SSI_ECHO_DEFAULT     1
@@ -221,6 +228,7 @@ static ngx_http_ssi_param_t  ngx_http_ss
     { ngx_string("virtual"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 },
     { ngx_string("file"), NGX_HTTP_SSI_INCLUDE_FILE, 0, 0 },
     { ngx_string("wait"), NGX_HTTP_SSI_INCLUDE_WAIT, 0, 0 },
+    { ngx_string("set"), NGX_HTTP_SSI_INCLUDE_SET, 0, 0 },
     { ngx_string("stub"), NGX_HTTP_SSI_INCLUDE_STUB, 0, 0 },
     { ngx_null_string, 0, 0, 0 }
 };
@@ -382,7 +390,6 @@ ngx_http_ssi_body_filter(ngx_http_reques
     ngx_uint_t                 i, index;
     ngx_chain_t               *cl, **ll;
     ngx_table_elt_t           *param;
-    ngx_connection_t          *c;
     ngx_http_request_t        *pr;
     ngx_http_ssi_ctx_t        *ctx, *mctx;
     ngx_http_ssi_block_t      *bl;
@@ -801,24 +808,14 @@ ngx_http_ssi_body_filter(ngx_http_reques
                     }
                 }
 
-                c = r->connection;
-
                 rc = cmd->handler(r, ctx, params);
 
-                if (c->destroyed) {
-                    return NGX_DONE;
-                }
-
                 if (rc == NGX_OK) {
                     continue;
                 }
 
-                if (rc == NGX_AGAIN) {
-                    return NGX_AGAIN;
-                }
-
-                if (rc == NGX_ERROR) {
-                    return NGX_ERROR;
+                if (rc == NGX_DONE || rc == NGX_AGAIN || rc == NGX_ERROR) {
+                    return rc;
                 }
             }
 
@@ -1538,6 +1535,7 @@ ngx_http_ssi_get_variable(ngx_http_reque
     ngx_uint_t key)
 {
     ngx_uint_t           i;
+    ngx_list_part_t     *part;
     ngx_http_ssi_var_t  *var;
     ngx_http_ssi_ctx_t  *ctx;
 
@@ -1547,8 +1545,21 @@ ngx_http_ssi_get_variable(ngx_http_reque
         return NULL;
     }
 
-    var = ctx->variables->elts;
-    for (i = 0; i < ctx->variables->nelts; i++) {
+    part = &ctx->variables->part;
+    var = part->elts;
+
+    for (i = 0; /* void */ ; i++) {
+
+        if (i >= part->nelts) {
+            if (part->next == NULL) {
+                break;
+            }
+  
+            part = part->next;
+            var = part->elts;
+            i = 0;
+        }
+
         if (name->len != var[i].name.len) {
             continue;
         }
@@ -1693,7 +1704,7 @@ ngx_http_ssi_evaluate_string(ngx_http_re
                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                               "the closing bracket in \"%V\" "
                               "variable is missing", &var);
-                return NGX_ERROR;
+                return NGX_HTTP_SSI_ERROR;
             }
 
             if (var.len == 0) {
@@ -1820,7 +1831,7 @@ invalid_variable:
     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                   "invalid variable name in \"%V\"", text);
 
-    return NGX_ERROR;
+    return NGX_HTTP_SSI_ERROR;
 }
 
 
@@ -1828,18 +1839,21 @@ static ngx_int_t
 ngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
     ngx_str_t **params)
 {
-    ngx_int_t              rc;
-    ngx_str_t             *uri, *file, *wait, *stub, args;
-    ngx_buf_t             *b;
-    ngx_uint_t             flags, i;
-    ngx_chain_t           *out, *cl, *tl, **ll;
-    ngx_http_request_t    *sr;
-    ngx_http_ssi_ctx_t    *mctx;
-    ngx_http_ssi_block_t  *bl;
+    ngx_int_t                    rc, key;
+    ngx_str_t                   *uri, *file, *wait, *set, *stub, args;
+    ngx_buf_t                   *b;
+    ngx_uint_t                   flags, i;
+    ngx_chain_t                 *cl, *tl, **ll;
+    ngx_http_request_t          *sr;
+    ngx_http_ssi_var_t          *var;
+    ngx_http_ssi_ctx_t          *mctx;
+    ngx_http_ssi_block_t        *bl;
+    ngx_http_post_subrequest_t  *psr;
 
     uri = params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];
     file = params[NGX_HTTP_SSI_INCLUDE_FILE];
     wait = params[NGX_HTTP_SSI_INCLUDE_WAIT];
+    set = params[NGX_HTTP_SSI_INCLUDE_SET];
     stub = params[NGX_HTTP_SSI_INCLUDE_STUB];
 
     if (uri && file) {
@@ -1855,6 +1869,13 @@ ngx_http_ssi_include(ngx_http_request_t 
         return NGX_HTTP_SSI_ERROR;
     }
 
+    if (set && stub) {
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+                      "\"set\" and \"stub\" may not be used together "
+                      "in \"include\" SSI command");
+        return NGX_HTTP_SSI_ERROR;
+    }
+
     if (wait) {
         if (uri == NULL) {
             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -1878,10 +1899,10 @@ ngx_http_ssi_include(ngx_http_request_t 
         uri = file;
     }
 
-    if (ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX)
-        != NGX_OK)
-    {
-        return NGX_HTTP_SSI_ERROR;
+    rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX);
+
+    if (rc != NGX_OK) {
+        return rc;
     }
 
     args.len = 0;
@@ -1895,11 +1916,11 @@ ngx_http_ssi_include(ngx_http_request_t 
         return NGX_HTTP_SSI_ERROR;
     }
 
-    out = NULL;
+    psr = NULL;
+
+    mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
 
     if (stub) {
-        mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
-
         if (mctx->blocks) {
             bl = mctx->blocks->elts;
             for (i = 0; i < mctx->blocks->nelts; i++) {
@@ -1917,9 +1938,16 @@ ngx_http_ssi_include(ngx_http_request_t 
 
     found:
 
+        psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
+        if (psr == NULL) {
+            return NGX_ERROR;
+        }
+
+        psr->handler = ngx_http_ssi_stub_output;
+
         if (bl[i].count++) {
 
-            ll = &out;
+            ll = (ngx_chain_t **) &psr->data;
 
             for (tl = bl[i].bufs; tl; tl = tl->next) {
 
@@ -1952,17 +1980,61 @@ ngx_http_ssi_include(ngx_http_request_t 
             }
 
         } else {
-            out = bl[i].bufs;
+            psr->data = bl[i].bufs;
         }
     }
 
-    rc = ngx_http_subrequest(r, uri, &args, &sr, out, flags);
+    if (set) {
+        key = 0;
+
+        for (i = 0; i < set->len; i++) {
+            set->data[i] = ngx_tolower(set->data[i]);
+            key = ngx_hash(key, set->data[i]);
+        }
+
+        psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
+        if (psr == NULL) {
+            return NGX_ERROR;
+        }
+
+        psr->handler = ngx_http_ssi_set_variable;
+        psr->data = ngx_http_ssi_get_variable(r, set, key);
+
+        if (psr->data == NULL) {
+
+            if (mctx->variables == NULL) {
+                mctx->variables = ngx_list_create(r->pool, 4,
+                                                  sizeof(ngx_http_ssi_var_t));
+                if (mctx->variables == NULL) {
+                    return NGX_ERROR;
+                }
+            }
+
+            var = ngx_list_push(mctx->variables);
+            if (var == NULL) {
+                return NGX_ERROR;
+            }
+
+            var->name = *set;
+            var->key = key;
+            var->value = ngx_http_ssi_null_string;
+            psr->data = &var->value;
+        }
+
+        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY;
+    }
+
+    rc = ngx_http_subrequest(r, uri, &args, &sr, psr, flags);
+
+    if (rc == NGX_DONE) {
+        return NGX_DONE;
+    }
 
     if (rc == NGX_ERROR) {
         return NGX_HTTP_SSI_ERROR;
     }
 
-    if (wait == NULL) {
+    if (wait == NULL && set == NULL) {
         return NGX_OK;
     }
 
@@ -1981,6 +2053,48 @@ ngx_http_ssi_include(ngx_http_request_t 
 
 
 static ngx_int_t
+ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data, ngx_int_t rc)
+{
+    ngx_chain_t  *out;
+
+    if (rc == NGX_ERROR || r->connection->error || r->request_output) {
+        return rc;
+    }
+
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "ssi stub output: \"%V?%V\"", &r->uri, &r->args);
+
+    out = data;
+
+    if (!r->header_sent) {
+        if (ngx_http_set_content_type(r) == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        if (ngx_http_send_header(r) == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+    }
+
+    return ngx_http_output_filter(r, out);
+}
+
+
+static ngx_int_t
+ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data, ngx_int_t rc)
+{
+    ngx_str_t  *value = data;
+
+    if (r->upstream) {
+        value->len = r->upstream->buffer.last - r->upstream->buffer.pos;
+        value->data = r->upstream->buffer.pos;
+    }
+
+    return rc;
+}
+
+
+static ngx_int_t
 ngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
     ngx_str_t **params)
 {
@@ -2090,7 +2204,7 @@ static ngx_int_t
 ngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,
     ngx_str_t **params)
 {
-    ngx_int_t            key;
+    ngx_int_t            key, rc;
     ngx_uint_t           i;
     ngx_str_t           *name, *value, *vv;
     ngx_http_ssi_var_t  *var;
@@ -2099,10 +2213,10 @@ ngx_http_ssi_set(ngx_http_request_t *r, 
     mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);
 
     if (mctx->variables == NULL) {
-        mctx->variables = ngx_array_create(r->pool, 4,
-                                           sizeof(ngx_http_ssi_var_t));
+        mctx->variables = ngx_list_create(r->pool, 4,
+                                          sizeof(ngx_http_ssi_var_t));
         if (mctx->variables == NULL) {
-            return NGX_HTTP_SSI_ERROR;
+            return NGX_ERROR;
         }
     }
 
@@ -2112,8 +2226,10 @@ ngx_http_ssi_set(ngx_http_request_t *r, 
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "ssi set \"%V\" \"%V\"", name, value);
 
-    if (ngx_http_ssi_evaluate_string(r, ctx, value, 0) != NGX_OK) {
-        return NGX_HTTP_SSI_ERROR;
+    rc = ngx_http_ssi_evaluate_string(r, ctx, value, 0);
+
+    if (rc != NGX_OK) {
+        return rc;
     }
 
     key = 0;
@@ -2130,9 +2246,9 @@ ngx_http_ssi_set(ngx_http_request_t *r, 
         return NGX_OK;
     }
 
-    var = ngx_array_push(mctx->variables);
+    var = ngx_list_push(mctx->variables);
     if (var == NULL) {
-        return NGX_HTTP_SSI_ERROR;
+        return NGX_ERROR;
     }
 
     var->name = *name;
@@ -2209,8 +2325,10 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "left: \"%V\"", &left);
 
-    if (ngx_http_ssi_evaluate_string(r, ctx, &left, flags) != NGX_OK) {
-        return NGX_HTTP_SSI_ERROR;
+    rc = ngx_http_ssi_evaluate_string(r, ctx, &left, flags);
+
+    if (rc != NGX_OK) {
+        return rc;
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -2271,8 +2389,10 @@ ngx_http_ssi_if(ngx_http_request_t *r, n
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "right: \"%V\"", &right);
 
-    if (ngx_http_ssi_evaluate_string(r, ctx, &right, flags) != NGX_OK) {
-        return NGX_HTTP_SSI_ERROR;
+    rc = ngx_http_ssi_evaluate_string(r, ctx, &right, flags);
+
+    if (rc != NGX_OK) {
+        return rc;
     }
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
--- a/src/http/modules/ngx_http_ssi_filter_module.h
+++ b/src/http/modules/ngx_http_ssi_filter_module.h
@@ -56,7 +56,7 @@ typedef struct {
 
     size_t                    value_len;
 
-    ngx_array_t              *variables;
+    ngx_list_t               *variables;
     ngx_array_t              *blocks;
 
     unsigned                  conditional:2;
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -73,6 +73,7 @@ static ngx_int_t
 ngx_http_static_handler(ngx_http_request_t *r)
 {
     u_char                    *last, *location;
+    size_t                     root;
     ngx_fd_t                   fd;
     ngx_int_t                  rc;
     ngx_uint_t                 level;
@@ -112,7 +113,7 @@ ngx_http_static_handler(ngx_http_request
      * so we do not need to reserve memory for '/' for possible redirect
      */
 
-    last = ngx_http_map_uri_to_path(r, &path, 0);
+    last = ngx_http_map_uri_to_path(r, &path, &root, 0);
     if (last == NULL) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -17,7 +17,7 @@ our @EXPORT = qw(
     HTTP_SERVER_ERROR
 );
 
-our $VERSION = '0.4.0';
+our $VERSION = '0.4.9';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -453,6 +453,7 @@ filename(r)
     CODE:
 
     dXSTARG;
+    size_t                root;
     ngx_http_request_t   *r;
     ngx_http_perl_ctx_t  *ctx;
 
@@ -463,7 +464,7 @@ filename(r)
         goto done;
     }
 
-    if (ngx_http_map_uri_to_path(r, &ctx->filename, 0) == NULL) {
+    if (ngx_http_map_uri_to_path(r, &ctx->filename, &root, 0) == NULL) {
         XSRETURN_UNDEF;
     }
 
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -565,7 +565,9 @@ ngx_http_perl_create_interpreter(ngx_htt
     ngx_log_t *log)
 {
     int                n;
-    char              *embedding[6];
+    STRLEN             len;
+    SV                *sv;
+    char              *ver, *embedding[6];
     PerlInterpreter   *perl;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "create perl interpreter");
@@ -634,6 +636,16 @@ ngx_http_perl_create_interpreter(ngx_htt
         goto fail;
     }
 
+    sv = get_sv("nginx::VERSION", FALSE);
+    ver = SvPV(sv, len);
+
+    if (ngx_strcmp(ver, NGINX_VERSION) != 0) {
+        ngx_log_error(NGX_LOG_ALERT, log, 0,
+                      "version " NGINX_VERSION " of nginx.pm is required, "
+                      "but %s was found", ver);
+        goto fail;
+    }
+
     if (ngx_http_perl_run_requires(aTHX_ &pmcf->requires, log) != NGX_OK) {
         goto fail;
     }
--- a/src/http/modules/perl/ngx_http_perl_module.h
+++ b/src/http/modules/perl/ngx_http_perl_module.h
@@ -11,6 +11,7 @@
 #include <ngx_config.h>
 #include <ngx_core.h>
 #include <ngx_http.h>
+#include <nginx.h>
 
 #include <EXTERN.h>
 #include <perl.h>
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -794,6 +794,7 @@ ngx_int_t
 ngx_http_core_content_phase(ngx_http_request_t *r,
     ngx_http_phase_handler_t *ph)
 {
+    size_t     root;
     ngx_int_t  rc;
     ngx_str_t  path;
 
@@ -830,7 +831,7 @@ ngx_http_core_content_phase(ngx_http_req
 
     if (r->uri.data[r->uri.len - 1] == '/' && !r->zero_in_uri) {
 
-        if (ngx_http_map_uri_to_path(r, &path, 0) != NULL) {
+        if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
             ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                           "directory index of \"%V\" is forbidden", &path);
         }
@@ -1157,7 +1158,7 @@ ngx_http_output_filter(ngx_http_request_
 
 u_char *
 ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *path,
-    size_t reserved)
+    size_t *root_length, size_t reserved)
 {
     u_char                    *last;
     size_t                     alias;
@@ -1178,7 +1179,7 @@ ngx_http_map_uri_to_path(ngx_http_reques
 
     if (clcf->root_lengths == NULL) {
 
-        r->root_length = clcf->root.len;
+        *root_length = clcf->root.len;
 
         path->len = clcf->root.len + reserved;
 
@@ -1201,8 +1202,8 @@ ngx_http_map_uri_to_path(ngx_http_reques
             return NULL;
         }
 
-        r->root_length = path->len - reserved;
-        last = path->data + r->root_length;
+        *root_length = path->len - reserved;
+        last = path->data + *root_length;
     }
 
     last = ngx_cpystrn(last, r->uri.data + alias, r->uri.len - alias + 1);
@@ -1284,7 +1285,7 @@ ngx_http_auth_basic_user(ngx_http_reques
 ngx_int_t
 ngx_http_subrequest(ngx_http_request_t *r,
     ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,
-    ngx_chain_t *out, ngx_uint_t flags)
+    ngx_http_post_subrequest_t *ps, ngx_uint_t flags)
 {
     ngx_connection_t              *c;
     ngx_http_request_t            *sr;
@@ -1341,7 +1342,6 @@ ngx_http_subrequest(ngx_http_request_t *
 
     sr->method = NGX_HTTP_GET;
     sr->http_version = r->http_version;
-    sr->http_major = r->http_minor;
 
     sr->request_line = r->request_line;
     sr->uri = *uri;
@@ -1353,9 +1353,8 @@ ngx_http_subrequest(ngx_http_request_t *
     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "http subrequest \"%V?%V\"", uri, &sr->args);
 
-    if (flags & NGX_HTTP_ZERO_IN_URI) {
-        sr->zero_in_uri = 1;
-    }
+    sr->zero_in_uri = (flags & NGX_HTTP_ZERO_IN_URI) != 0;
+    sr->subrequest_in_memory = (flags & NGX_HTTP_SUBREQUEST_IN_MEMORY) != 0;
 
     sr->unparsed_uri = r->unparsed_uri;
     sr->method_name = r->method_name;
@@ -1365,9 +1364,9 @@ ngx_http_subrequest(ngx_http_request_t *
         return NGX_ERROR;
     }
 
-    sr->out = out;
     sr->main = r->main;
     sr->parent = r;
+    sr->post_subrequest = ps;
     sr->read_event_handler = ngx_http_request_empty_handler;
     sr->write_event_handler = ngx_http_request_empty_handler;
 
@@ -1431,7 +1430,7 @@ ngx_http_subrequest(ngx_http_request_t *
         return NGX_AGAIN;
     }
 
-    return NGX_OK;
+    return NGX_DONE;
 }
 
 
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -301,12 +301,12 @@ ngx_int_t ngx_http_core_content_phase(ng
 ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);
 ngx_int_t ngx_http_set_exten(ngx_http_request_t *r);
 u_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name,
-    size_t reserved);
+    size_t *root_length, size_t reserved);
 ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r);
 
 ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,
     ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **sr,
-    ngx_chain_t *out, ngx_uint_t flags);
+    ngx_http_post_subrequest_t *psr, ngx_uint_t flags);
 ngx_int_t ngx_http_internal_redirect(ngx_http_request_t *r,
     ngx_str_t *uri, ngx_str_t *args);
 
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -1476,26 +1476,8 @@ ngx_http_finalize_request(ngx_http_reque
         return;
     }
 
-    if (r != r->main
-        && rc != NGX_ERROR
-        && !r->connection->error
-        && !r->request_output
-        && r->out)
-    {
-        if (!r->header_sent) {
-            rc = ngx_http_set_content_type(r);
-
-            if (rc == NGX_OK) {
-                rc = ngx_http_send_header(r);
-
-                if (rc != NGX_ERROR) {
-                    rc = ngx_http_output_filter(r, r->out);
-                }
-            }
-
-        } else {
-            rc = ngx_http_output_filter(r, r->out);
-        }
+    if (r != r->main && r->post_subrequest) {
+        rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);
     }
 
     if (rc == NGX_ERROR
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -47,7 +47,9 @@
 
 #define NGX_HTTP_PARSE_INVALID_HEADER      13
 
+
 #define NGX_HTTP_ZERO_IN_URI               1
+#define NGX_HTTP_SUBREQUEST_IN_MEMORY      2
 
 
 #define NGX_HTTP_OK                        200
@@ -287,6 +289,15 @@ struct ngx_http_cleanup_s {
 };
 
 
+typedef ngx_int_t (*ngx_http_post_subrequest_pt)(ngx_http_request_t *r,
+    void *data, ngx_int_t rc);
+
+typedef struct {
+    ngx_http_post_subrequest_pt       handler;
+    void                             *data;
+} ngx_http_post_subrequest_t;
+
+
 typedef struct ngx_http_postponed_request_s  ngx_http_postponed_request_t;
 
 struct ngx_http_postponed_request_s {
@@ -330,8 +341,6 @@ struct ngx_http_request_s {
 
     ngx_uint_t                        method;
     ngx_uint_t                        http_version;
-    ngx_uint_t                        http_major;
-    ngx_uint_t                        http_minor;
 
     ngx_str_t                         request_line;
     ngx_str_t                         uri;
@@ -346,6 +355,7 @@ struct ngx_http_request_s {
     ngx_http_request_t               *main;
     ngx_http_request_t               *parent;
     ngx_http_postponed_request_t     *postponed;
+    ngx_http_post_subrequest_t       *post_subrequest;
 
     uint32_t                          in_addr;
     ngx_uint_t                        port;
@@ -359,8 +369,6 @@ struct ngx_http_request_s {
 
     ngx_http_variable_value_t        *variables;
 
-    size_t                            root_length;
-
     size_t                            limit_rate;
 
     /* used to learn the Apache compatible response length without a header */
@@ -405,6 +413,7 @@ struct ngx_http_request_s {
     unsigned                          request_body_file_log_level:3;
 
     unsigned                          fast_subrequest:1;
+    unsigned                          subrequest_in_memory:1;
 
     unsigned                          header_timeout_set:1;
 
@@ -456,6 +465,7 @@ struct ngx_http_request_s {
     unsigned                          subrequests:8;
 
     /* used to parse HTTP headers */
+
     ngx_uint_t                        state;
     u_char                           *uri_start;
     u_char                           *uri_end;
@@ -475,6 +485,9 @@ struct ngx_http_request_s {
     u_char                           *header_start;
     u_char                           *header_end;
 
+    unsigned                          http_minor:16;
+    unsigned                          http_major:16;
+
     ngx_uint_t                        header_hash;
     ngx_uint_t                        lowcase_index;
     u_char                            lowcase_header[NGX_HTTP_LC_HEADER_LEN];
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -21,6 +21,7 @@ static void ngx_http_upstream_send_reque
     ngx_http_upstream_t *u);
 static void ngx_http_upstream_send_request_handler(ngx_event_t *wev);
 static void ngx_http_upstream_process_header(ngx_event_t *rev);
+static void ngx_http_upstream_process_body_in_memory(ngx_event_t *rev);
 static void ngx_http_upstream_send_response(ngx_http_request_t *r,
     ngx_http_upstream_t *u);
 static void
@@ -1061,6 +1062,12 @@ ngx_http_upstream_process_header(ngx_eve
 
     /* rc == NGX_OK */
 
+    if (u->headers_in.status_n >= NGX_HTTP_BAD_REQUEST
+        && r->subrequest_in_memory)
+    {
+        u->buffer.last = u->buffer.pos;
+    }
+
     if (u->headers_in.status_n == NGX_HTTP_INTERNAL_SERVER_ERROR) {
 
         if (u->peer.tries > 1
@@ -1248,7 +1255,109 @@ ngx_http_upstream_process_header(ngx_eve
         u->length = NGX_MAX_SIZE_T_VALUE;
     }
 
-    ngx_http_upstream_send_response(r, u);
+    if (!r->subrequest_in_memory) {
+        ngx_http_upstream_send_response(r, u);
+        return;
+    }
+
+    /* subrequest content in memory */
+
+    if (u->input_filter == NULL) {
+        u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;
+        u->input_filter = ngx_http_upstream_non_buffered_filter;
+        u->input_filter_ctx = r;
+    }
+
+    if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) {
+        ngx_http_upstream_finalize_request(r, u,
+                                           NGX_HTTP_INTERNAL_SERVER_ERROR);
+        return;
+    }
+
+    if (u->buffer.last - u->buffer.pos >= (ssize_t) u->length) {
+        if (u->input_filter(u->input_filter_ctx, 0) == NGX_ERROR) {
+            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+            return;
+        }
+
+        ngx_http_upstream_finalize_request(r, u, 0);
+        return;
+    }
+
+    rev->handler = ngx_http_upstream_process_body_in_memory;
+
+    ngx_http_upstream_process_body_in_memory(rev);
+}
+
+
+static void
+ngx_http_upstream_process_body_in_memory(ngx_event_t *rev)
+{
+    size_t                size;
+    ssize_t               n;
+    ngx_buf_t            *b;
+    ngx_connection_t     *c;
+    ngx_http_request_t   *r;
+    ngx_http_upstream_t  *u;
+
+    c = rev->data;
+    r = c->data;
+    u = r->upstream;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                   "http upstream process body on memory");
+
+    if (rev->timedout) {
+        ngx_connection_error(c, NGX_ETIMEDOUT, "upstream timed out");
+        ngx_http_upstream_finalize_request(r, u, NGX_ETIMEDOUT);
+        return;
+    }
+
+    b = &u->buffer;
+
+    for ( ;; ) {
+
+        size = b->end - b->last;
+
+        if (size == 0) {
+            ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                          "upstream buffer is too small to read repsonse");
+            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+            return;
+        }
+
+        n = c->recv(c, b->last, size);
+
+        if (n == NGX_AGAIN) {
+            break;
+        }
+
+        if (n == 0 || n == NGX_ERROR) {
+            ngx_http_upstream_finalize_request(r, u, n);
+            return;
+        }
+
+        if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
+            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+            return;
+        }
+
+        if (!rev->ready) {
+            break;
+        }
+    }
+
+    if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
+        return;
+    }
+
+    if (rev->active) {
+        ngx_add_timer(rev, u->conf->read_timeout);
+
+    } else if (rev->timer_set) {
+        ngx_del_timer(rev);
+    }
 }
 
 
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -852,9 +852,10 @@ static ngx_int_t
 ngx_http_variable_request_filename(ngx_http_request_t *r,
     ngx_http_variable_value_t *v, uintptr_t data)
 {
+    size_t     root;
     ngx_str_t  path;
 
-    if (ngx_http_map_uri_to_path(r, &path, 0) == NULL) {
+    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {
         return NGX_ERROR;
     }