Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: ModPerl: ModPerl

[mp2] reliable methods to prevent handlers from repeating

 

 

ModPerl modperl RSS feed   Index | Next | Previous | View Threaded


dorian at foobarsystems

Jan 20, 2005, 12:34 PM

Post #1 of 10 (1142 views)
Permalink
[mp2] reliable methods to prevent handlers from repeating

suppose i wanted the same logic as:

return Apache::DECLINED unless $r->is_initial_req;

except that sometimes it may be necessary to serve for a subrequest,
just not more than once. the construct i tried is:

for (my $pr = $r; $pr; $pr = $pr->prev) {
if ($pr->notes->get(__PACKAGE__ . '::SEEN')) {
$r->log->debug("We've been seen already.");
return Apache::DECLINED;
}
}
$r->notes->set(__PACKAGE__ . '::SEEN', 1);

but i get no love. what's the preferred way of dealing with this?

cheers

.dorian


geoff at modperlcookbook

Jan 20, 2005, 12:36 PM

Post #2 of 10 (1077 views)
Permalink
Re: [mp2] reliable methods to prevent handlers from repeating [In reply to]

Dorian Taylor wrote:
> suppose i wanted the same logic as:
>
> return Apache::DECLINED unless $r->is_initial_req;
>
> except that sometimes it may be necessary to serve for a subrequest,
> just not more than once. the construct i tried is:
>
> for (my $pr = $r; $pr; $pr = $pr->prev) {
> if ($pr->notes->get(__PACKAGE__ . '::SEEN')) {
> $r->log->debug("We've been seen already.");
> return Apache::DECLINED;
> }
> }
> $r->notes->set(__PACKAGE__ . '::SEEN', 1);
>
> but i get no love. what's the preferred way of dealing with this?

instead of looping around try $r->main->notes or $r->prev->notes

HTH

--Geoff


dorian at foobarsystems

Jan 20, 2005, 12:53 PM

Post #3 of 10 (1085 views)
Permalink
Re: [mp2] reliable methods to prevent handlers from repeating [In reply to]

> instead of looping around try $r->main->notes or $r->prev->notes

hm, that loop should eventually hit all requests in the chain though, no?

also, the first hit to that handler could in fact be a subrequest,
so $r->main->notes may never be set.

i do admit i'm cargo culting a little bit by looping over the request
chain. my understanding is the aforementioned statement equates to
"return declined if you find a true value in a specific note in
this request or any one that has occurred since the main request.".

if that's not the case, what am i missing?

what technique would you use if you had to have a handler work
exactly once, even though it may never be invoked as an initial
request?


geoff at modperlcookbook

Jan 20, 2005, 2:00 PM

Post #4 of 10 (1099 views)
Permalink
Re: [mp2] reliable methods to prevent handlers from repeating [In reply to]

> what technique would you use if you had to have a handler work
> exactly once, even though it may never be invoked as an initial
> request?

if you're in prefork, off the top of my head I might set a perl global and
then reset it using a cleanup handler. something like

$My::Foo::seen++;
$r->register_cleanup(sub { undef $My::Foo::seen });

so, again in prefork only, at the start of any request $My::Foo::seen should
be false. all bets are off if you're using threads.

--Geoff


gozer at ectoplasm

Jan 20, 2005, 2:32 PM

Post #5 of 10 (1085 views)
Permalink
Re: [mp2] reliable methods to prevent handlers from repeating [In reply to]

Dorian Taylor wrote:
> suppose i wanted the same logic as:
>
> return Apache::DECLINED unless $r->is_initial_req;
>
> except that sometimes it may be necessary to serve for a subrequest,
> just not more than once. the construct i tried is:
>
> for (my $pr = $r; $pr; $pr = $pr->prev) {
> if ($pr->notes->get(__PACKAGE__ . '::SEEN')) {
> $r->log->debug("We've been seen already.");
> return Apache::DECLINED;
> }
> }
> $r->notes->set(__PACKAGE__ . '::SEEN', 1);

That's because you are setting that SEEN flag in the subrequest itself,
and that subrequest will most likely be short lived. Nobody will see that
flag, unless that subrequest fires off another subrequest of it's own.

What you are trying to do is probably :

$r->main->notes->set(__PACKAGE__ . '::SEEN', 1);

And then your check becomes

$r->main->notes->get(__PACKAGE__ . '::SEEN');

Without a need to cycle over the requests list either.

--------------------------------------------------------------------------------
Philippe M. Chiasson m/gozer\@(apache|cpan|ectoplasm)\.org/ GPG KeyID : 88C3A5A5
http://gozer.ectoplasm.org/ F9BF E0C2 480E 7680 1AE5 3631 CB32 A107 88C3A5A5
Attachments: signature.asc (0.25 KB)


dorian at foobarsystems

Jan 20, 2005, 2:33 PM

Post #6 of 10 (1074 views)
Permalink
Re: [mp2] reliable methods to prevent handlers from repeating [In reply to]

On Thu, Jan 20, 2005 at 05:00:32PM -0500, Geoffrey Young wrote:
>
> > what technique would you use if you had to have a handler work
> > exactly once, even though it may never be invoked as an initial
> > request?
>
> if you're in prefork, off the top of my head I might set a perl global and
> then reset it using a cleanup handler. something like
>
> $My::Foo::seen++;
> $r->register_cleanup(sub { undef $My::Foo::seen });

hm, the reason why i did the thing with the notes is because i've
seen similar constructs in C apache modules to avoid handler looping
(i'm pretty sure mod_rewrite does it).

what about unregistering the handler from the stack? is that possible?

> so, again in prefork only, at the start of any request $My::Foo::seen should
> be false. all bets are off if you're using threads.

yeah, i'm staging in prefork (doing my best to write code that
*looks* thread-safe) currently but i'm going to be wanting to move
to worker as soon as possible.

just to clarify: what *are* $r->next/prev if not a way to access
the requests that have happened since the main, or is it only those
which have happened via internal_redirect? are the subrequests
generated by lookup_uri exempt from the list?

.d


dorian at foobarsystems

Jan 20, 2005, 2:45 PM

Post #7 of 10 (1075 views)
Permalink
Re: [mp2] reliable methods to prevent handlers from repeating [In reply to]

On Thu, Jan 20, 2005 at 02:32:59PM -0800, Philippe M. Chiasson wrote:
> Dorian Taylor wrote:
> >suppose i wanted the same logic as:
> >
> > return Apache::DECLINED unless $r->is_initial_req;
> >
> >except that sometimes it may be necessary to serve for a subrequest,
> >just not more than once. the construct i tried is:
> >
> > for (my $pr = $r; $pr; $pr = $pr->prev) {
> > if ($pr->notes->get(__PACKAGE__ . '::SEEN')) {
> > $r->log->debug("We've been seen already.");
> > return Apache::DECLINED;
> > }
> > }
> > $r->notes->set(__PACKAGE__ . '::SEEN', 1);
>
> That's because you are setting that SEEN flag in the subrequest itself,
> and that subrequest will most likely be short lived. Nobody will see that
> flag, unless that subrequest fires off another subrequest of it's own.

that's exactly it. the subrequest does in fact launch subrequests
of its own. it's possible that hits to the uri handled by this
handler may come by way of a lookup_uri from another handler, so a
test to is_initial_req or is_main won't suffice. the desired effect
is this:

GET /foo [handled by mod_foo]
`-> lookup_uri /somewhere/else [handled by this module]
`-> lookup_uri /somewhere/else/again [same, but return declined]

exactly as if the first request to /somewhere/else was the main
request and there was a test against $r->is_main, but there's no
guarantee of that.

> What you are trying to do is probably :
>
> $r->main->notes->set(__PACKAGE__ . '::SEEN', 1);
>
> And then your check becomes
>
> $r->main->notes->get(__PACKAGE__ . '::SEEN');
>
> Without a need to cycle over the requests list either.

if that was the case, couldn't i just test against $r->is_main?

if you had to deal with this scenario, what would you do?

cheers

.d


geoff at modperlcookbook

Jan 20, 2005, 3:54 PM

Post #8 of 10 (1086 views)
Permalink
Re: [mp2] reliable methods to prevent handlers from repeating [In reply to]

> what about unregistering the handler from the stack? is that possible?

yes, but only if the phase you want to unregister isn't the same as the
current phase

# works, except during a PerlAuthenHandler
$r->set_handlers(PerlAuthenHandler => []);

> just to clarify: what *are* $r->next/prev if not a way to access
> the requests that have happened since the main, or is it only those
> which have happened via internal_redirect? are the subrequests
> generated by lookup_uri exempt from the list?

$r->is_initial_req() should return true only for the request that came from
the browser.

$r->is_main() should return true for the first in a series of (sub)requests,
subrequests or otherwise. so, it returns true for the request that came
from the browser, as well as any initiated from internal_redirect()

$r->main returns the first $r in a series of (sub)requests, both those
generated from internal_redirect as well as the initial request from the
browser.

> if you had to deal with this scenario, what would you do?

probably the globals trick I mentioned :)

HTH

--Geoff


dorian at foobarsystems

Jan 21, 2005, 2:00 AM

Post #9 of 10 (1089 views)
Permalink
Re: [mp2] reliable methods to prevent handlers from repeating [In reply to]

aha.

$r->lookup_uri doesn't set the calling request object into $subr->prev,
according to the apache source. i can't see any way to refer to the
calling request from a subrequest, unless the calling request is
the main request.

how frustrating.

.d


dorian at foobarsystems

Jan 24, 2005, 1:23 PM

Post #10 of 10 (1068 views)
Permalink
Re: [mp2] reliable methods to prevent handlers from repeating [In reply to]

here's my solution to handler loop prevention when the initial
request to a handler isn't necessarily the main request:

sub handler {
my $r = shift;
my $mr = $r->main || $r;
if ($mr->notes->get(__PACKAGE__ . '::SEEN')) {
$r->log->debug("We are looping.");
return Apache::DECLINED;
}
$mr->notes->set(__PACKAGE__ . '::SEEN', 1);

# ... do stuff, including other lookup_uri/lookup_file requests.

$mr->notes->unset(__PACKAGE__ . '::SEEN');
return Apache::OK; # or whatever other status
}

comments?

.dorian

ModPerl modperl RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.