Close response under mod_perl 2
Asked Answered
T

1

14

I'm trying to find out if there's a way to complete a response under mod_perl 2 without returning to the main handler. Haven't been able to find a method for that in the docs so far. The following is an example of what I'm trying to achieve:

#!/usr/bin/perl
# This is some mod_perl handler
use strict;
use warnings;
use Apache2::Const ':common';

sub handler {
    my $r = shift;
    if ($r->method eq 'POST') {
        # just to do something as example
        do_post_response($r);
    }
    $r->content_type('text/plain');
    print "Thank you, goodbye.";
    return Apache2::Const::OK;
}

sub do_post_response {
    my $r = shift;
    unless (check_somthing()) {
        # Suppose I find a situation that requires
        # a different response than normal...
        $r->content_type('text/plain');
        print "We have a situation...";
        $r->something_to_finish_the_request_immediatly(Apache2::Const::OK);
    }
}

In a regular Perl script, running as stand alone or under mod_cgi, I could just exit() with the new response, but under mod_perl I need to return something in the original handlersubroutine. This is leading me to keep track of a whole chain of calls where all of them have to return something until I get back to the main handler.

For example, instead of:

unless (check_something()) { ...

I need to do things like:

my $check = check_something();
return $check if $check;

and I also have to do something similar in the main handler, which is quite ungly for some situation handlings.

Is there a way to close the request when inside a nested call, just like what I tried to illustrate with my example?

EDIT: I've found that I can call a goto LABEL and place that label just before the last return in the main handlersubroutine. It works, but still feels like a dirty hack. I really hope there's a nicer way.

Thaliathalidomide answered 29/8, 2016 at 21:28 Comment(1)
I'm no expert on mod_perl2 but generally this looks like something you'd use exceptions for. Inside do_post_response(), use die "We have a situation..."; and catch this in handler() using an eval block or even better using a module like Try::Tiny. Add some Exception::Class if you need to differentiate your custom exceptions from others that may occur as a result of other uncaught failures.Orling
D
2

I think you are still fine to call exit() because mod_perl overrides what exit does:

exit

In the normal Perl code exit() is used to stop the program flow and exit the Perl interpreter. However under mod_perl we only want the stop the program flow without killing the Perl interpreter.

You should take no action if your code includes exit() calls and it's OK to continue using them. mod_perl worries to override the exit() function with its own version which stops the program flow, and performs all the necessary cleanups, but doesn't kill the server. This is done by overriding:

*CORE::GLOBAL::exit = \&ModPerl::Util::exit;

https://perl.apache.org/docs/2.0/user/coding/coding.html

Desberg answered 10/9, 2016 at 0:35 Comment(4)
I tried exit before asking this question. It doesn't work, it doesn't make the handler return what it needs to return, it's the same as calling a die.Thaliathalidomide
Then you have another problem in your code or your mod_perl has been modified. mod_perl overrides exit, as explained in the docs I linked.Desberg
Yes, it overrides exit, because you are in a persistent interpreter and you don't want it to really exit. If you use the real exit, then you actually kill the Apache thread. However, the fact that it overrides exit just terminates the current function call, but it doesn't return anything to Apache, and that's the problem, Apache needs an actual response or it takes it as a failure in the request.Thaliathalidomide
You could override CORE::GLOBAL::exit with a modified version of ModPerl::Util::exit that returns a value to Apache, but you are going to have to catch each place Apache calls into mod_perl and there is possibly going to be an exit to setup the return.Desberg

© 2022 - 2024 — McMap. All rights reserved.