Hi Torsten.
Once again, I thank you for the time spent researching and answering my
question(s).
Unfortunately, I am less fluent in C than you (which is why after all I
like and use mod_perl), so sometimes referring me to the C code is not
as enlightening to me as it undoubtedly is to you. But I am trying to
follow, nevertheless.
Torsten Foertsch wrote:
On Sun 12 Oct 2008, André Warnier wrote:
<Location /some/url>
SetHandler jakarta-servlet
SetEnvIf REQUEST_URI "\.(htm|web|css|gif|jpg|js|html?)$" no-jk
PerlXXXHandler My::Module->some_method
...
</Location>
("jakarta-servlet" above means mod_jk --> Tomcat)
(and PerlXXXHandler being any kind of Perl HTTP Handler running soon
enough)
The Question :
Is it possible, in the PerlXXXHandler, on a request-by-request base,
to "disable" the jakarta-servlet handler, and install the following
instead
SetHandler modperl
PerlResponseHandler My::Module->some_other_method
?
I know that I can do something like the above for regular static
pages, and I use this already :
$r->handler('modperl');
$r->set_handlers(PerlAuthzHandler => []); # stop authorization
from running
$r->set_handlers(PerlResponseHandler => \&_send_login_form);
However, when I try to do the same in a Location like the above, it
seems that the jakarta-servlet handler runs anyway, and my
PerlResponseHandler is never called.
Does that piece of code run in a /Perl(PostReadRequest|Trans|
MapToStorage)Handler/ ?
No, at the moment it runs in a PerlAuthenHandler.
As explained the other thread (requests and subrequests), I thought it
was more elegant and mod_perl-ish, instead of doing a re-direct to a
login page if the user needed authentifying, to having the login page
returned by a content handler.
(One main reason is that it allows me, in that content handler,to
"filter" the login page and insert in it, things I have stored
previously in $r->pnotes().)
For that thus, when the user is not authenticated when the request hits
the PerlAuthenHandler, this handler tries to set the content handler
(PerlResponseHandler) to the method send_login_form() in the same module.
This works fine when the previous content handler is Apache's default,
iow for static pages. But it fails miserably when there is already a
specific content handler (like mod_jk) for this Location.
Please, have a look again at the ap_process_request_internal() function
in httpd-2.x.y/server/request.c.
I would first have to know where this thing is. I have never so far
looked at the Apache source code, so I need some pointers there.
Not that if I look, I would necessarily understand much of it, but I can
try.
Now, why would I, since you provide the nice explanation below ?
This function implements almost the
complete request cycle (save PostReadRequest, Response and Log).
Just after ap_run_translate_name(), that is the PerlTransHandler, you'll
see this piece:
/* Reset to the server default config prior to running
map_to_storage
*/
r->per_dir_config = r->server->lookup_defaults;
if ((access_status = ap_run_map_to_storage(r))) {
/* This request wasn't in storage (e.g. TRACE) */
return access_status;
}
/* Rerun the location walk, which overrides any map_to_storage
config.
*/
if ((access_status = ap_location_walk(r))) {
return access_status;
}
/* Only on the main request! */
if (r->main == NULL) {
if ((access_status = ap_run_header_parser(r))) {
return access_status;
}
}
...
The line "r->per_dir_config = r->server->lookup_defaults;" resets all
request specific configurations applied so far. After that line the
request is configured as if no Directory/Location or other container
were present in your httpd.conf. Next comes MapToStorage. Here
Directory, DirectoryMatch, Files and FilesMatch containers are applied
to the request.
So, you mean that for every and each request, Apache re-configures
itself ? If that is so, it is quite amazing the speed at which it can
serve a page..
Then, and this is what I think hits you, comes an
ap_location_walk. Here Location containers are applied. So, your
SetHandler inside the Location overrides any $r->handler seen before.
Well, I am not sure :
The handler set in the configuration file for that Location, is
originally "jakarta-servlet" (as per the snippet below).
It is I, in my PerlAuthenHandler, who is trying to reset this to
"modperl", and in mod_perl, to my send_login_page() method.
Before I do that, there is no $r->handler nor PerlResponseHandler set.
I may be misunderstanding what you say above of course..
After the ap_location_walk comes the PerlHeaderParserHandler. But it is
skipped for subrequests.
That's fine, we are always in a main request here.
A further examination will show you
(probably not)
that the
next phase that is run by all requests is the PerlTypeHandler just
before Fixup. So, move your "$r->handler('modperl');" to one of those
and it will work.
I don't think I can do that, unless I want the PerlTypeHandler or
PerlFixupHandler to become an authentication handler. That would
probably for one upset Adam, for two be kind of funky, no ?
I prefer Fixup but that is based only on the fact
that it is documented as do-what-you-want phase. There are directives
that change the handler in the type-checker phase. In fact the
AddHandler directive works that way. Mod_mime looks at the file
extension and adjusts the handler, e.g "AddHandler cgi-script .pl". So
using Fixup your module is the last to kick in.
Now, in summary :
This is a very specialised authentication module, meant to be used in
very specific circumstances and not for general usage. I control the
environment, so I can pretty much do what I want as long as it does not
crash the system or present some big security hole or the like.
In my case, that <Location> containing the
sethandler jakarta-servlet
is pretty much unique: it is not a sub- or super-location of anything
else, and it fulfills only one very narrow purpose.
To my knowledge, there will be no other module playing a role there,
other than the jakarta-servlet initial handler, plus whatever I decide
to put in there as mod_perl modules and methods.
This for the entire duration of one request.
Now, assuming I have no other stuff in there than the following lines
<LocationMatch "/servlet.xyz$">
SetHandler jakarta-servlet
SetEnvIf REQUEST_URI "\.(htm|web|css|gif|jpg|js|html?)$" no-jk
AuthType MyModule
AuthName MyXyzApp
PerlSetVar MyModule_var1 "value"
PerlAccessHandler My::Module->some_access_method
PerlAuthenHandler My::Module->some_auth_method
PerlAuthzHandler My::Module->some_authz_method
require valid-user
</Location>
then, if I understand correctly what you write above, since I have no
PerlTransHandler, nor Fixup, etc.. I should be able to do the same in
the PerlAuthenHandler, is it no so ?
Or am I still confused about the order in which things happen ?
Or, a thought that just occurred to me, is the solution for me (stopping
jakarta-servlet from running and running my PerlresponseHandler
instead), as easy as setting the "no-jk" variable (if I can do that
within my PerlAuthenHandler, and how) ?
Or, another thought that just occurred : can I comment out the
"SetHandler jakarta-servlet" originally, set my PerlresponseHandler as
the default handler in the <Location>, and then *if the user is
authenticated* (the contrary of my current condition), *set* the content
handler to be jakarta-servlet instead of modperl ?
To carry state around use pnotes
(that's what I use mostly)
, notes or subprocess_env.