Perl: reworked perl module to pass ctx instead of request.

This ensures that correct ctx is always available, including after
filter finalization.  In particular, this fixes a segmentation fault
with the following configuration:

    location / {
        image_filter test;

        perl 'sub {
            my $r = shift;
            $r->send_http_header();
            $r->print("foo\n");
            $r->print("bar\n");
        }';
    }

This also seems to be the only way to correctly handle filter finalization
in various complex cases, for example, when embedded perl is used both
in the original handler and in an error page called after filter
finalization.
diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index 848e501..2e9808f 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -15,8 +15,10 @@
 #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_request(r, ctx)                                     \
+                                                                              \
+    ctx = INT2PTR(ngx_http_perl_ctx_t *, SvIV((SV *) SvRV(ST(0))));           \
+    r = ctx->request
 
 
 #define ngx_http_perl_set_targ(p, len)                                        \
@@ -64,14 +66,12 @@
 
 
 static ngx_int_t
-ngx_http_perl_output(ngx_http_request_t *r, ngx_buf_t *b)
+ngx_http_perl_output(ngx_http_request_t *r, ngx_http_perl_ctx_t *ctx,
+    ngx_buf_t *b)
 {
-    ngx_chain_t           out;
+    ngx_chain_t   out;
 #if (NGX_HTTP_SSI)
-    ngx_chain_t          *cl;
-    ngx_http_perl_ctx_t  *ctx;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+    ngx_chain_t  *cl;
 
     if (ctx->ssi) {
         cl = ngx_alloc_chain_link(r->pool);
@@ -105,9 +105,10 @@
 status(r, code)
     CODE:
 
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     r->headers_out.status = SvIV(ST(1));
 
@@ -121,10 +122,11 @@
 send_http_header(r, ...)
     CODE:
 
-    ngx_http_request_t  *r;
-    SV                  *sv;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    SV                   *sv;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     if (r->headers_out.status == 0) {
         r->headers_out.status = NGX_HTTP_OK;
@@ -157,9 +159,10 @@
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     sv_upgrade(TARG, SVt_IV);
     sv_setiv(TARG, r->header_only);
@@ -172,9 +175,10 @@
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
     ngx_http_perl_set_targ(r->uri.data, r->uri.len);
 
     ST(0) = TARG;
@@ -185,9 +189,10 @@
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
     ngx_http_perl_set_targ(r->args.data, r->args.len);
 
     ST(0) = TARG;
@@ -198,9 +203,10 @@
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
     ngx_http_perl_set_targ(r->method_name.data, r->method_name.len);
 
     ST(0) = TARG;
@@ -211,9 +217,10 @@
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
     ngx_http_perl_set_targ(r->connection->addr_text.data,
                            r->connection->addr_text.len);
 
@@ -226,6 +233,7 @@
 
     dXSTARG;
     ngx_http_request_t         *r;
+    ngx_http_perl_ctx_t        *ctx;
     SV                         *key;
     u_char                     *p, *lowcase_key, *value, sep;
     STRLEN                      len;
@@ -237,7 +245,7 @@
     ngx_http_header_t          *hh;
     ngx_http_core_main_conf_t  *cmcf;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     key = ST(1);
 
@@ -374,13 +382,12 @@
     ngx_http_request_t   *r;
     ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
         XSRETURN_UNDEF;
     }
 
-    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
     ctx->next = SvRV(ST(1));
 
     r->request_body_in_single_buf = 1;
@@ -404,13 +411,14 @@
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
-    u_char              *p, *data;
-    size_t               len;
-    ngx_buf_t           *buf;
-    ngx_chain_t         *cl;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    u_char               *p, *data;
+    size_t                len;
+    ngx_buf_t            *buf;
+    ngx_chain_t          *cl;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     if (r->request_body == NULL
         || r->request_body->temp_file
@@ -465,9 +473,10 @@
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     if (r->request_body == NULL || r->request_body->temp_file == NULL) {
         XSRETURN_UNDEF;
@@ -483,9 +492,10 @@
 discard_request_body(r)
     CODE:
 
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     ngx_http_discard_request_body(r);
 
@@ -494,12 +504,13 @@
 header_out(r, key, value)
     CODE:
 
-    ngx_http_request_t  *r;
-    SV                  *key;
-    SV                  *value;
-    ngx_table_elt_t     *header;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    SV                   *key;
+    SV                   *value;
+    ngx_table_elt_t      *header;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     key = ST(1);
     value = ST(2);
@@ -542,13 +553,12 @@
     CODE:
 
     dXSTARG;
-    size_t                root;
     ngx_http_request_t   *r;
     ngx_http_perl_ctx_t  *ctx;
+    size_t                root;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
-    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
     if (ctx->filename.data) {
         goto done;
     }
@@ -571,15 +581,16 @@
 print(r, ...)
     CODE:
 
-    ngx_http_request_t  *r;
-    SV                  *sv;
-    int                  i;
-    u_char              *p;
-    size_t               size;
-    STRLEN               len;
-    ngx_buf_t           *b;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    SV                   *sv;
+    int                   i;
+    u_char               *p;
+    size_t                size;
+    STRLEN                len;
+    ngx_buf_t            *b;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     if (items == 2) {
 
@@ -660,7 +671,7 @@
 
     out:
 
-    (void) ngx_http_perl_output(r, b);
+    (void) ngx_http_perl_output(r, ctx, b);
 
 
 void
@@ -668,6 +679,7 @@
     CODE:
 
     ngx_http_request_t        *r;
+    ngx_http_perl_ctx_t       *ctx;
     char                      *filename;
     off_t                      offset;
     size_t                     bytes;
@@ -676,7 +688,7 @@
     ngx_open_file_info_t       of;
     ngx_http_core_loc_conf_t  *clcf;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     filename = SvPV_nolen(ST(1));
 
@@ -750,17 +762,18 @@
     b->file->log = r->connection->log;
     b->file->directio = of.is_directio;
 
-    (void) ngx_http_perl_output(r, b);
+    (void) ngx_http_perl_output(r, ctx, b);
 
 
 void
 flush(r)
     CODE:
 
-    ngx_http_request_t  *r;
-    ngx_buf_t           *b;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    ngx_buf_t            *b;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     b = ngx_calloc_buf(r->pool);
     if (b == NULL) {
@@ -771,7 +784,7 @@
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "$r->flush");
 
-    (void) ngx_http_perl_output(r, b);
+    (void) ngx_http_perl_output(r, ctx, b);
 
     XSRETURN_EMPTY;
 
@@ -781,16 +794,14 @@
     CODE:
 
     ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
     SV                   *uri;
     ngx_uint_t            i;
-    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     uri = ST(1);
 
-    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
-
     if (ngx_http_perl_sv2str(aTHX_ r, &ctx->redirect_uri, uri) != NGX_OK) {
         XSRETURN_EMPTY;
     }
@@ -811,9 +822,10 @@
 allow_ranges(r)
     CODE:
 
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     r->allow_ranges = 1;
 
@@ -823,13 +835,14 @@
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
-    SV                  *text;
-    int                  type;
-    u_char              *p, *dst, *src;
-    STRLEN               len;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    SV                   *text;
+    int                   type;
+    u_char               *p, *dst, *src;
+    STRLEN                len;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     text = ST(1);
 
@@ -858,16 +871,16 @@
 
     dXSTARG;
     ngx_http_request_t         *r;
+    ngx_http_perl_ctx_t        *ctx;
     SV                         *name, *value;
     u_char                     *p, *lowcase;
     STRLEN                      len;
     ngx_str_t                   var, val;
     ngx_uint_t                  i, hash;
     ngx_http_perl_var_t        *v;
-    ngx_http_perl_ctx_t        *ctx;
     ngx_http_variable_value_t  *vv;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     name = ST(1);
 
@@ -919,8 +932,6 @@
 
     if (vv->not_found) {
 
-        ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
-
         if (ctx->variables) {
 
             v = ctx->variables->elts;
@@ -991,18 +1002,16 @@
     CODE:
 
     ngx_http_request_t   *r;
-    ngx_msec_t            sleep;
     ngx_http_perl_ctx_t  *ctx;
+    ngx_msec_t            sleep;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     sleep = (ngx_msec_t) SvIV(ST(1));
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "perl sleep: %M", sleep);
 
-    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
-
     ctx->next = SvRV(ST(2));
 
     r->connection->write->delayed = 1;
@@ -1016,13 +1025,14 @@
 log_error(r, err, msg)
     CODE:
 
-    ngx_http_request_t  *r;
-    SV                  *err, *msg;
-    u_char              *p;
-    STRLEN               len;
-    ngx_err_t            e;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    SV                   *err, *msg;
+    u_char               *p;
+    STRLEN                len;
+    ngx_err_t             e;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     err = ST(1);
 
diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index d06b43c..c2ef470 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -43,7 +43,8 @@
 static ngx_int_t ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires,
     ngx_log_t *log);
 static ngx_int_t ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
-    HV *nginx, SV *sub, SV **args, ngx_str_t *handler, ngx_str_t *rv);
+    ngx_http_perl_ctx_t *ctx, HV *nginx, SV *sub, SV **args,
+    ngx_str_t *handler, ngx_str_t *rv);
 static void ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv);
 
 static ngx_int_t ngx_http_perl_preconfiguration(ngx_conf_t *cf);
@@ -199,6 +200,8 @@
         }
 
         ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
+
+        ctx->request = r;
     }
 
     pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
@@ -220,8 +223,8 @@
         ctx->next = NULL;
     }
 
-    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sub, NULL, handler,
-                                    NULL);
+    rc = ngx_http_perl_call_handler(aTHX_ r, ctx, pmcf->nginx, sub, NULL,
+                                    handler, NULL);
 
     }
 
@@ -309,6 +312,8 @@
         }
 
         ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
+
+        ctx->request = r;
     }
 
     pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
@@ -321,7 +326,7 @@
     PERL_SET_CONTEXT(pmcf->perl);
     PERL_SET_INTERP(pmcf->perl);
 
-    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, pv->sub, NULL,
+    rc = ngx_http_perl_call_handler(aTHX_ r, ctx, pmcf->nginx, pv->sub, NULL,
                                     &pv->handler, &value);
 
     }
@@ -372,6 +377,8 @@
         }
 
         ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
+
+        ctx->request = r;
     }
 
     pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
@@ -430,8 +437,8 @@
         asv = NULL;
     }
 
-    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sv, asv, handler,
-                                    NULL);
+    rc = ngx_http_perl_call_handler(aTHX_ r, ctx, pmcf->nginx, sv, asv,
+                                    handler, NULL);
 
     SvREFCNT_dec(sv);
 
@@ -667,8 +674,9 @@
 
 
 static ngx_int_t
-ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r, HV *nginx, SV *sub,
-    SV **args, ngx_str_t *handler, ngx_str_t *rv)
+ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
+    ngx_http_perl_ctx_t *ctx, HV *nginx, SV *sub, SV **args,
+    ngx_str_t *handler, ngx_str_t *rv)
 {
     SV                *sv;
     int                n, status;
@@ -687,7 +695,7 @@
 
     PUSHMARK(sp);
 
-    sv = sv_2mortal(sv_bless(newRV_noinc(newSViv(PTR2IV(r))), nginx));
+    sv = sv_2mortal(sv_bless(newRV_noinc(newSViv(PTR2IV(ctx))), nginx));
     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 5e60b03..4f1eaa3 100644
--- a/src/http/modules/perl/ngx_http_perl_module.h
+++ b/src/http/modules/perl/ngx_http_perl_module.h
@@ -21,6 +21,8 @@
 typedef ngx_http_request_t   *nginx;
 
 typedef struct {
+    ngx_http_request_t       *request;
+
     ngx_str_t                 filename;
     ngx_str_t                 redirect_uri;
     ngx_str_t                 redirect_args;