nginx-0.3.38-RELEASE import

    *) Feature: the ngx_http_dav_module.

    *) Change: the ngx_http_perl_module optimizations.
       Thanks to Sergey Skvortsov.

    *) Feature: the ngx_http_perl_module supports the $r->request_body_file
       method.

    *) Feature: the "client_body_in_file_only" directive.

    *) Workaround: now on disk overflow nginx tries to write access logs
       once a second only.
       Thanks to Anton Yuzhaninov and Maxim Dounin.

    *) Bugfix: now the "limit_rate" directive more precisely limits rate if
       rate is more than 100 Kbyte/s.
       Thanks to ForJest.

    *) Bugfix: now the IMAP/POP3 proxy escapes the "\r" and "\n" symbols in
       login and password to pass authorization server.
       Thanks to Maxim Dounin.
diff --git a/src/http/modules/ngx_http_chunked_filter_module.c b/src/http/modules/ngx_http_chunked_filter_module.c
index 45110c5..6581e59 100644
--- a/src/http/modules/ngx_http_chunked_filter_module.c
+++ b/src/http/modules/ngx_http_chunked_filter_module.c
@@ -52,6 +52,7 @@
 {
     if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED
         || r->headers_out.status == NGX_HTTP_NO_CONTENT
+        || r->headers_out.status == NGX_HTTP_CREATED
         || r != r->main)
     {
         return ngx_http_next_header_filter(r);
diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c
new file mode 100644
index 0000000..1b98204
--- /dev/null
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -0,0 +1,286 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_http.h>
+
+
+#define NGX_HTTP_DAV_OFF   2
+
+typedef struct {
+    ngx_uint_t  methods;
+} ngx_http_dav_loc_conf_t;
+
+
+static ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r);
+static void ngx_http_dav_put_handler(ngx_http_request_t *r);
+static void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf);
+static char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf,
+    void *parent, void *child);
+static ngx_int_t ngx_http_dav_init(ngx_cycle_t *cycle);
+
+
+static ngx_conf_bitmask_t  ngx_http_dav_methods_mask[] = {
+    { ngx_string("off"), NGX_HTTP_DAV_OFF },
+    { ngx_string("put"), NGX_HTTP_PUT },
+    { ngx_string("delete"), NGX_HTTP_DELETE },
+    { ngx_null_string, 0 }
+};
+
+
+static ngx_command_t  ngx_http_dav_commands[] = {
+
+    { ngx_string("dav_methods"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+      ngx_conf_set_bitmask_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_dav_loc_conf_t, methods),
+      &ngx_http_dav_methods_mask },
+
+      ngx_null_command
+};
+
+
+ngx_http_module_t  ngx_http_dav_module_ctx = {
+    NULL,                                  /* preconfiguration */
+    NULL,                                  /* postconfiguration */
+
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    NULL,                                  /* create server configuration */
+    NULL,                                  /* merge server configuration */
+
+    ngx_http_dav_create_loc_conf,          /* create location configuration */
+    ngx_http_dav_merge_loc_conf            /* merge location configuration */
+};
+
+
+ngx_module_t  ngx_http_dav_module = {
+    NGX_MODULE_V1,
+    &ngx_http_dav_module_ctx,              /* module context */
+    ngx_http_dav_commands,                 /* module directives */
+    NGX_HTTP_MODULE,                       /* module type */
+    NULL,                                  /* init master */
+    ngx_http_dav_init,                     /* init module */
+    NULL,                                  /* init process */
+    NULL,                                  /* init thread */
+    NULL,                                  /* exit thread */
+    NULL,                                  /* exit process */
+    NULL,                                  /* exit master */
+    NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_int_t
+ngx_http_dav_handler(ngx_http_request_t *r)
+{
+    ngx_int_t                 rc;
+    ngx_str_t                 path;
+    ngx_http_dav_loc_conf_t  *dlcf;
+
+    /* TODO: Win32 */
+    if (r->zero_in_uri) {
+        return NGX_DECLINED;
+    }
+
+    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);
+
+    if (!(r->method & dlcf->methods)) {
+        return NGX_DECLINED;
+    }
+
+    switch (r->method) {
+
+    case NGX_HTTP_PUT:
+
+        if (r->uri.data[r->uri.len - 1] == '/') {
+            return NGX_DECLINED;
+        }
+
+        r->request_body_in_file_only = 1;
+        r->request_body_in_persistent_file = 1;
+        r->request_body_delete_incomplete_file = 1;
+        r->request_body_file_group_access = 1;
+
+        rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);
+
+        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+            return rc;
+        }
+
+        return NGX_DONE;
+
+    case NGX_HTTP_DELETE:
+
+        if (r->uri.data[r->uri.len - 1] == '/') {
+            return NGX_DECLINED;
+        }
+
+        ngx_http_map_uri_to_path(r, &path, 0);
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                       "http delete filename: \"%s\"", path.data);
+
+        if (ngx_delete_file(path.data) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
+                          ngx_delete_file_n " \"%s\" failed", path.data);
+
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
+        }
+
+        return NGX_HTTP_NO_CONTENT;
+    }
+
+    return NGX_DECLINED;
+}
+
+
+static void
+ngx_http_dav_put_handler(ngx_http_request_t *r)
+{
+    u_char                    *location;
+    ngx_err_t                  err;
+    ngx_str_t                 *temp, path;
+    ngx_uint_t                 status;
+    ngx_file_info_t            fi;
+    ngx_http_core_loc_conf_t  *clcf;
+
+    ngx_http_map_uri_to_path(r, &path, 0);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "http put filename: \"%s\"", path.data);
+
+    temp = &r->request_body->temp_file->file.name;
+
+    if (ngx_file_info(path.data, &fi) == -1) {
+        status = NGX_HTTP_CREATED;
+
+    } else {
+        status = NGX_HTTP_NO_CONTENT;
+    }
+
+    if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) {
+        goto ok;
+    }
+
+    err = ngx_errno;
+
+#if (NGX_WIN32)
+
+    if (err == NGX_EEXIST) {
+        if (ngx_win32_rename_file(temp, &path, r->pool) != NGX_ERROR) {
+
+            if (ngx_rename_file(temp->data, path.data) != NGX_FILE_ERROR) {
+                goto ok;
+            }
+        }
+
+        err = ngx_errno;
+    }
+
+#endif
+
+    ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
+                  ngx_rename_file_n " \"%s\" failed", path.data);
+
+    ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+    return;
+
+ok:
+
+    if (status == NGX_HTTP_CREATED) {
+
+        r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));
+        if (r->headers_out.location == NULL) {
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return;
+        }
+
+        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+
+        if (!clcf->alias && clcf->root_lengths == NULL) {
+            location = path.data + clcf->root.len;
+
+        } else {
+            location = ngx_palloc(r->pool, r->uri.len);
+            if (location == NULL) {
+                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return;
+            }
+
+            ngx_memcpy(location, r->uri.data, r->uri.len);
+        }
+
+        /*
+         * we do not need to set the r->headers_out.location->hash and
+         * r->headers_out.location->key fields
+         */
+
+        r->headers_out.location->value.len = r->uri.len;
+        r->headers_out.location->value.data = location;
+
+    }
+
+    r->headers_out.status = status;
+    r->header_only = 1;
+
+    ngx_http_finalize_request(r, ngx_http_send_header(r));
+    return;
+}
+
+
+static void *
+ngx_http_dav_create_loc_conf(ngx_conf_t *cf)
+{
+    ngx_http_dav_loc_conf_t  *conf;
+
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));
+    if (conf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    /*
+     * set by ngx_pcalloc():
+     *
+     *     conf->methods = 0;
+     */
+
+    return conf;
+}
+
+
+static char *
+ngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_http_dav_loc_conf_t  *prev = parent;
+    ngx_http_dav_loc_conf_t  *conf = child;
+
+    ngx_conf_merge_bitmask_value(conf->methods, prev->methods,
+                              (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));
+
+    return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_http_dav_init(ngx_cycle_t *cycle)
+{
+    ngx_http_handler_pt        *h;
+    ngx_http_core_main_conf_t  *cmcf;
+
+    cmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_core_module);
+
+    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
+    if (h == NULL) {
+        return NGX_ERROR;
+    }
+
+    *h = ngx_http_dav_handler;
+
+    return NGX_OK;
+}
diff --git a/src/http/modules/ngx_http_log_module.c b/src/http/modules/ngx_http_log_module.c
index 9436e59..07ac113 100644
--- a/src/http/modules/ngx_http_log_module.c
+++ b/src/http/modules/ngx_http_log_module.c
@@ -23,6 +23,7 @@
 
 typedef struct {
     ngx_open_file_t            *file;
+    time_t                      disk_full_time;
     ngx_array_t                *ops;        /* array of ngx_http_log_op_t */
 } ngx_http_log_t;
 
@@ -253,6 +254,17 @@
     log = lcf->logs->elts;
     for (l = 0; l < lcf->logs->nelts; l++) {
 
+        if (ngx_time() == log[l].disk_full_time) {
+
+            /*
+             * On FreeBSD writing to a full filesystem with enabled softupdates
+             * may block process for much longer time than writing to non-full
+             * filesystem, so we skip writing the log for one second.
+             */
+
+            continue;
+        }
+
         len = 0;
         op = log[l].ops->elts;
         for (i = 0; i < log[l].ops->nelts; i++) {
@@ -272,7 +284,13 @@
 
             if (len > (size_t) (file->last - file->pos)) {
 
-                ngx_write_fd(file->fd, file->buffer, file->pos - file->buffer);
+                if (ngx_write_fd(file->fd, file->buffer,
+                                 file->pos - file->buffer)
+                    == -1
+                    && ngx_errno == NGX_ENOSPC)
+                {
+                    log[l].disk_full_time = ngx_time();
+                }
 
                 file->pos = file->buffer;
             }
@@ -306,7 +324,11 @@
 
         ngx_linefeed(p);
 
-        ngx_write_fd(file->fd, line, p - line);
+        if (ngx_write_fd(file->fd, line, p - line) == -1
+            && ngx_errno == NGX_ENOSPC)
+        {
+            log[l].disk_full_time = ngx_time();
+        }
     }
 
     return NGX_OK;
@@ -1017,6 +1039,8 @@
         return NGX_CONF_ERROR;
     }
 
+    log->disk_full_time = 0;
+
     if (cf->args->nelts >= 3) {
         name = value[2];
 
diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index 7e2cec3..735cc37 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -13,6 +13,20 @@
 
 #include "XSUB.h"
 
+#define ngx_http_perl_set_request(r)                                          \
+    r = INT2PTR(ngx_http_request_t *, SvIV((SV *) SvRV(ST(0))))
+
+
+#define ngx_http_perl_set_targ(p, len, z)                                     \
+                                                                              \
+    sv_upgrade(TARG, SVt_PV);                                                 \
+    SvPOK_on(TARG);                                                           \
+    SvPV_set(TARG, (char *) p);                                               \
+    SvLEN_set(TARG, len + z);                                                 \
+    SvCUR_set(TARG, len);                                                     \
+    SvFAKE_on(TARG);                                                          \
+    SvREADONLY_on(TARG);                                                      \
+
 
 static ngx_int_t
 ngx_http_perl_sv2str(pTHX_ ngx_http_request_t *r, ngx_str_t *s, SV *sv)
@@ -79,16 +93,15 @@
 MODULE = nginx    PACKAGE = nginx
 
 
-int
+void
 send_http_header(r, ...)
-    nginx   r
-
-    PREINIT:
-
-    SV     *sv;
-
     CODE:
 
+    ngx_http_request_t  *r;
+    SV                  *sv;
+
+    ngx_http_perl_set_request(r);
+
     if (r->headers_out.status == 0) {
         r->headers_out.status = NGX_HTTP_OK;
     }
@@ -99,128 +112,105 @@
         if (ngx_http_perl_sv2str(aTHX_ r, &r->headers_out.content_type, sv)
             != NGX_OK)
         {
-            RETVAL = NGX_ERROR;
-            goto done;
+            XSRETURN_EMPTY;
         }
 
     } else {
         if (r->headers_out.content_type.len == 0) {
             if (ngx_http_set_content_type(r) != NGX_OK) {
-                RETVAL = NGX_ERROR;
-                goto done;
+                XSRETURN_EMPTY;
             }
         }
     }
 
-    RETVAL = ngx_http_send_header(r);
-
-    done:
-
-    OUTPUT:
-    RETVAL
+    (void) ngx_http_send_header(r);
 
 
-int
+void
 header_only(r)
-    nginx  r
-
-    CODE:
-    RETVAL = r->header_only;
-
-    OUTPUT:
-    RETVAL
-
-
-# The returning "char *" is more quickly than creating SV, because SV returned
-# from XS is never used as permanent storage. Even in simple case:
-# "$uri = $r->uri" the SV returned by $r->uri is copied to $uri's SV.
-
-char *
-uri(r, ...)
-    nginx  r
-
     CODE:
 
-    if (items != 1) {
-        croak("$r->uri(text) is not implemented");
-    }
+    dXSTARG;
+    ngx_http_request_t  *r;
 
-    RETVAL = ngx_palloc(r->pool, r->uri.len + 1);
-    if (RETVAL == NULL) {
-        XSRETURN_UNDEF;
-    }
+    ngx_http_perl_set_request(r);
 
-    ngx_cpystrn((u_char *) RETVAL, r->uri.data, r->uri.len + 1);
+    sv_upgrade(TARG, SVt_IV);
+    sv_setiv(TARG, r->header_only);
 
-    OUTPUT:
-    RETVAL
+    ST(0) = TARG;
 
 
-char *
-args(r, ...)
-    nginx  r
-
+void
+uri(r)
     CODE:
 
-    if (items != 1) {
-        croak("$r->args(text) is not implemented");
-    }
+    dXSTARG;
+    ngx_http_request_t  *r;
 
-    RETVAL = ngx_palloc(r->pool, r->args.len + 1);
-    if (RETVAL == NULL) {
-        XSRETURN_UNDEF;
-    }
+    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_targ(r->uri.data, r->uri.len, 0);
 
-    ngx_cpystrn((u_char *) RETVAL, r->args.data, r->args.len + 1);
-
-    OUTPUT:
-    RETVAL
+    ST(0) = TARG;
 
 
-char *
+void
+args(r)
+    CODE:
+
+    dXSTARG;
+    ngx_http_request_t  *r;
+
+    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_targ(r->args.data, r->args.len, 0);
+
+    ST(0) = TARG;
+
+
+void
 request_method(r)
-    nginx  r
-
     CODE:
 
-    RETVAL = ngx_palloc(r->pool, r->method_name.len + 1);
-    if (RETVAL == NULL) {
-        XSRETURN_UNDEF;
-    }
+    dXSTARG;
+    ngx_http_request_t  *r;
 
-    ngx_cpystrn((u_char *) RETVAL, r->method_name.data, r->method_name.len + 1);
+    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_targ(r->method_name.data, r->method_name.len, 0);
 
-    OUTPUT:
-    RETVAL
+    ST(0) = TARG;
 
 
-char *
+void
 remote_addr(r)
-    nginx  r
-
     CODE:
 
-    RETVAL = (char *) r->connection->addr_text.data;
+    dXSTARG;
+    ngx_http_request_t  *r;
 
-    OUTPUT:
-    RETVAL
+    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_targ(r->connection->addr_text.data,
+                           r->connection->addr_text.len, 1);
+
+    ST(0) = TARG;
 
 
-char *
+void
 header_in(r, key)
-    nginx             r
-    SV               *key
-
-    PREINIT:
-
-    u_char           *p;
-    STRLEN            len;
-    ngx_uint_t        i;
-    ngx_list_part_t  *part;
-    ngx_table_elt_t  *header;
-
     CODE:
 
+    dXSTARG;
+    ngx_http_request_t  *r;
+    SV                  *key;
+    u_char              *p;
+    STRLEN               len;
+    ngx_uint_t           i;
+    ngx_list_part_t     *part;
+    ngx_table_elt_t     *header;
+
+    ngx_http_perl_set_request(r);
+
+    key = ST(1);
+
     if (SvROK(key) && SvTYPE(SvRV(key)) == SVt_PV) {
         key = SvRV(key);
     }
@@ -248,7 +238,7 @@
             continue;
         }
 
-        RETVAL = (char *) header[i].value.data;
+        ngx_http_perl_set_targ(header[i].value.data, header[i].value.len, 0);
 
         goto done;
     }
@@ -257,73 +247,80 @@
 
     done:
 
-    OUTPUT:
-    RETVAL
+    ST(0) = TARG;
 
 
-SV *
+void
 request_body(r)
-    nginx         r
-
-    PREINIT:
-
-    STRLEN        len;
-    ngx_chain_t  *cl;
-
     CODE:
 
-    len = 0;
+    dXSTARG;
+    ngx_http_request_t  *r;
+    size_t               len;
 
-    for (cl = r->request_body->bufs; cl; cl = cl->next) {
-        if (cl->buf->in_file) {
-            XSRETURN_UNDEF;
-        }
+    ngx_http_perl_set_request(r);
 
-        len += cl->buf->last - cl->buf->pos;
+    if (r->request_body->temp_file || r->request_body->bufs == NULL) {
+        XSRETURN_UNDEF;
     }
 
+    len = r->request_body->bufs->buf->last - r->request_body->bufs->buf->pos;
+
     if (len == 0) {
         XSRETURN_UNDEF;
     }
 
-    RETVAL = newSV(len);
+    ngx_http_perl_set_targ(r->request_body->bufs->buf->pos, len, 0);
 
-    for (cl = r->request_body->bufs; cl; cl = cl->next) {
-        sv_catpvn(RETVAL, cl->buf->pos, cl->buf->last - cl->buf->pos);
+    ST(0) = TARG;
+
+
+void
+request_body_file(r)
+    CODE:
+
+    dXSTARG;
+    ngx_http_request_t  *r;
+
+    ngx_http_perl_set_request(r);
+
+    if (r->request_body->temp_file == NULL) {
+        XSRETURN_UNDEF;
     }
 
-    OUTPUT:
-    RETVAL
+    ngx_http_perl_set_targ(r->request_body->temp_file->file.name.data,
+                           r->request_body->temp_file->file.name.len, 1);
+
+    ST(0) = TARG;
 
 
-int
+void
 header_out(r, key, value)
-    nginx             r
-    SV               *key
-    SV               *value
-
-    PREINIT:
-
-    ngx_table_elt_t  *header;
-
     CODE:
 
+    ngx_http_request_t  *r;
+    SV                  *key;
+    SV                  *value;
+    ngx_table_elt_t     *header;
+
+    ngx_http_perl_set_request(r);
+
+    key = ST(1);
+    value = ST(2);
+
     header = ngx_list_push(&r->headers_out.headers);
     if (header == NULL) {
-        RETVAL = NGX_ERROR;
-        goto done;
+        XSRETURN_EMPTY;
     }
 
     header->hash = 1;
 
     if (ngx_http_perl_sv2str(aTHX_ r, &header->key, key) != NGX_OK) {
-        RETVAL = NGX_ERROR;
-        goto done;
+        XSRETURN_EMPTY;
     }
 
     if (ngx_http_perl_sv2str(aTHX_ r, &header->value, value) != NGX_OK) {
-        RETVAL = NGX_ERROR;
-        goto done;
+        XSRETURN_EMPTY;
     }
 
     if (header->key.len == sizeof("Content-Length") - 1
@@ -335,62 +332,51 @@
         r->headers_out.content_length = header;
     }
 
-    RETVAL = NGX_OK;
-
-    done:
-
-    OUTPUT:
-    RETVAL
+    XSRETURN_EMPTY;
 
 
-char *
+void
 filename(r)
-    nginx                 r
-
-    PREINIT:
-
-    ngx_str_t             path;
-    ngx_http_perl_ctx_t  *ctx;
-
     CODE:
 
+    dXSTARG;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+
+    ngx_http_perl_set_request(r);
+
     ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
-    if (ctx->filename) {
+    if (ctx->filename.data) {
         goto done;
     }
 
-    if (ngx_http_map_uri_to_path(r, &path, 0) == NULL) {
+    if (ngx_http_map_uri_to_path(r, &ctx->filename, 0) == NULL) {
         XSRETURN_UNDEF;
     }
 
-    ctx->filename = (char *) path.data;
-
-    sv_setpv(PL_statname, ctx->filename);
+    ctx->filename.len--;
+    sv_setpv(PL_statname, (char *) ctx->filename.data);
 
     done:
 
-    RETVAL = ctx->filename;
+    ngx_http_perl_set_targ(ctx->filename.data, ctx->filename.len, 1);
 
-    OUTPUT:
-    RETVAL
+    ST(0) = TARG;
 
 
-int
+void
 print(r, ...)
-    nginx       r
-
-    PREINIT:
-
-    SV         *sv;
-    int         i;
-    u_char     *p;
-    size_t      size;
-    STRLEN      len;
-    ngx_buf_t  *b;
-
     CODE:
 
-    RETVAL = NGX_OK;
+    ngx_http_request_t  *r;
+    SV                  *sv;
+    int                  i;
+    u_char              *p;
+    size_t               size;
+    STRLEN               len;
+    ngx_buf_t           *b;
+
+    ngx_http_perl_set_request(r);
 
     if (items == 2) {
 
@@ -410,13 +396,12 @@
             p = (u_char *) SvPV(sv, len);
 
             if (len == 0) {
-                goto done;
+                XSRETURN_EMPTY;
             }
 
             b = ngx_calloc_buf(r->pool);
             if (b == NULL) {
-                RETVAL = NGX_ERROR;
-                goto done;
+                XSRETURN_EMPTY;
             }
 
             b->memory = 1;
@@ -451,13 +436,12 @@
     }
 
     if (size == 0) {
-        goto done;
+        XSRETURN_EMPTY;
     }
 
     b = ngx_create_temp_buf(r->pool, size);
     if (b == NULL) {
-        RETVAL = NGX_ERROR;
-        goto done;
+        XSRETURN_EMPTY;
     }
 
     for (i = 1; i < items; i++) {
@@ -473,51 +457,49 @@
 
     out:
 
-    RETVAL = ngx_http_perl_output(r, b);
+    (void) ngx_http_perl_output(r, b);
 
-    done:
-
-    OUTPUT:
-    RETVAL
+    XSRETURN_EMPTY;
 
 
-int
+void
 sendfile(r, filename, offset = -1, bytes = 0)
-    nginx                     r
-    char                     *filename
+    CODE:
+
+    ngx_http_request_t       *r;
+    char                     *filename;
     int                       offset;
     size_t                    bytes;
-
-    PREINIT:
-
     ngx_fd_t                  fd;
     ngx_buf_t                *b;
     ngx_file_info_t           fi;
     ngx_pool_cleanup_t       *cln;
     ngx_pool_cleanup_file_t  *clnf;
 
-    CODE:
+    ngx_http_perl_set_request(r);
+
+    filename = SvPV_nolen(ST(1));
 
     if (filename == NULL) {
         croak("sendfile(): NULL filename");
     }
 
+    offset = items < 3 ? -1 : SvIV(ST(2));
+    bytes = items < 4 ? 0 : SvIV(ST(3));
+
     b = ngx_calloc_buf(r->pool);
     if (b == NULL) {
-        RETVAL = NGX_ERROR;
-        goto done;
+        XSRETURN_EMPTY;
     }
 
     b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
     if (b->file == NULL) {
-        RETVAL = NGX_ERROR;
-        goto done;
+        XSRETURN_EMPTY;
     }
 
     cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
     if (cln == NULL) {
-        RETVAL = NGX_ERROR;
-        goto done;
+        XSRETURN_EMPTY;
     }
 
     fd = ngx_open_file((u_char *) filename, NGX_FILE_RDONLY, NGX_FILE_OPEN);
@@ -525,8 +507,7 @@
     if (fd == NGX_INVALID_FILE) {
         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                       ngx_open_file_n " \"%s\" failed", filename);
-        RETVAL = NGX_ERROR;
-        goto done;
+        XSRETURN_EMPTY;
     }
 
     if (offset == -1) {
@@ -543,9 +524,7 @@
                               ngx_close_file_n " \"%s\" failed", filename);
             }
 
-            RETVAL = NGX_ERROR;
-            goto done;
-
+            XSRETURN_EMPTY;
         }
 
         bytes = ngx_file_size(&fi) - offset;
@@ -566,53 +545,46 @@
     b->file->fd = fd;
     b->file->log = r->connection->log;
 
-    RETVAL = ngx_http_perl_output(r, b);
+    (void) ngx_http_perl_output(r, b);
 
-    done:
-
-    OUTPUT:
-    RETVAL
+    XSRETURN_EMPTY;
 
 
-int
+void
 rflush(r)
-    nginx       r
-
-    PREINIT:
-
-    ngx_buf_t  *b;
-
     CODE:
 
+    ngx_http_request_t  *r;
+    ngx_buf_t           *b;
+
+    ngx_http_perl_set_request(r);
+
     b = ngx_calloc_buf(r->pool);
     if (b == NULL) {
-        RETVAL = NGX_ERROR;
-        goto done;
+        XSRETURN_EMPTY;
     }
 
     b->flush = 1;
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "$r->rflush");
 
-    RETVAL = ngx_http_perl_output(r, b);
+    (void) ngx_http_perl_output(r, b);
 
-    done:
-
-    OUTPUT:
-    RETVAL
+    XSRETURN_EMPTY;
 
 
 void
 internal_redirect(r, uri)
-    nginx                 r
-    SV                   *uri
+    CODE:
 
-    PREINIT:
-
+    ngx_http_request_t   *r;
+    SV                   *uri;
     ngx_uint_t            i;
     ngx_http_perl_ctx_t  *ctx;
 
-    CODE:
+    ngx_http_perl_set_request(r);
+
+    uri = ST(1);
 
     ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
 
@@ -632,32 +604,35 @@
     }
 
 
-char *
+void
 unescape(r, text, type = 0)
-    nginx    r
-    SV      *text
-    int      type
-
-    PREINIT:
-
-    u_char  *p, *dst, *src;
-    STRLEN   n;
-
     CODE:
 
-    src = (u_char *) SvPV(text, n);
+    dXSTARG;
+    ngx_http_request_t  *r;
+    SV                  *text;
+    int                  type;
+    u_char              *p, *dst, *src;
+    STRLEN               len;
 
-    p = ngx_palloc(r->pool, n + 1);
+    ngx_http_perl_set_request(r);
+
+    text = ST(1);
+
+    src = (u_char *) SvPV(text, len);
+
+    p = ngx_palloc(r->pool, len + 1);
     if (p == NULL) {
         XSRETURN_UNDEF;
     }
 
     dst = p;
 
-    ngx_unescape_uri(&dst, &src, n, (ngx_uint_t) type);
+    type = items < 3 ? 0 : SvIV(ST(2));
+
+    ngx_unescape_uri(&dst, &src, len, (ngx_uint_t) type);
     *dst = '\0';
 
-    RETVAL = (char *) p;
+    ngx_http_perl_set_targ(p, dst - p, 1);
 
-    OUTPUT:
-    RETVAL
+    ST(0) = TARG;
diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index e453dc3..84de262 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -165,10 +165,14 @@
 #endif
 
 
+static HV  *nginx_stash;
+
 static void
 ngx_http_perl_xs_init(pTHX)
 {
     newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
+
+    nginx_stash = gv_stashpv("nginx", TRUE);
 }
 
 
@@ -182,6 +186,9 @@
         return NGX_HTTP_NOT_FOUND;
     }
 
+    r->request_body_in_single_buf = 1;
+    r->request_body_in_persistent_file = 1;
+
     rc = ngx_http_read_client_request_body(r, ngx_http_perl_handle_request);
 
     if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
@@ -259,7 +266,7 @@
         uri.len = 0;
     }
 
-    ctx->filename = NULL;
+    ctx->filename.data = NULL;
     ctx->redirect_uri.len = 0;
 
     if (uri.len) {
@@ -332,7 +339,7 @@
         v->not_found = 1;
     }
 
-    ctx->filename = NULL;
+    ctx->filename.data = NULL;
     ctx->redirect_uri.len = 0;
 
     return rc;
@@ -409,7 +416,7 @@
 
     ngx_http_perl_free_interpreter(pmcf, ctx->perl);
 
-    ctx->filename = NULL;
+    ctx->filename.data = NULL;
     ctx->redirect_uri.len = 0;
     ctx->ssi = NULL;
 
@@ -631,8 +638,7 @@
 
     PUSHMARK(sp);
 
-    sv = sv_newmortal();
-    sv_setref_pv(sv, "nginx", r);
+    sv = sv_2mortal(sv_bless(newRV_noinc(newSViv(PTR2IV(r))), nginx_stash));
     XPUSHs(sv);
 
     if (args) {
diff --git a/src/http/modules/perl/ngx_http_perl_module.h b/src/http/modules/perl/ngx_http_perl_module.h
index 1bc3852..1609c47 100644
--- a/src/http/modules/perl/ngx_http_perl_module.h
+++ b/src/http/modules/perl/ngx_http_perl_module.h
@@ -21,8 +21,7 @@
 typedef struct {
     PerlInterpreter          *perl;
 
-    char                     *filename;
-
+    ngx_str_t                 filename;
     ngx_str_t                 redirect_uri;
     ngx_str_t                 redirect_args;
 
@@ -45,7 +44,7 @@
 #endif
 
 
-extern void boot_DynaLoader (pTHX_ CV* cv);
+extern void  boot_DynaLoader(pTHX_ CV* cv);
 
 
 #endif /* _NGX_HTTP_PERL_MODULE_H_INCLUDED_ */