You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by "David E. Wheeler" <da...@justatheory.com> on 2015/03/02 01:46:12 UTC
How Do I change the Document Root Per Request
Hi,
I want to set the document root for a request to map to the basic auth username. I tried this in a PerlFixupHandler:
sub handler {
my $r = shift;
# We only want to do this once per request.
return DECLINED unless $r->is_initial_req;
# Get the username.
my $user = $r->user or return HTTP_UNAUTHORIZED;
# Return forbidden if the username subdiectory does not exist.
my $doc_root = File::Spec->catdir($r->document_root, $user);
return HTTP_FORBIDDEN unless -d $doc_root;
# Set the document root for the duration of this request and return.
$r->document_root($doc_root);
return DECLINED;
}
But alas, it still serves the original document root. How can I get it to change the document root on a per-request basis? If I cant, should I change the URI or the filename, instead?
Thnks,
David
AcceptPathInfo usefulness (was: Re: How Do I change the Document Root Per Request)
Posted by Randolf Richardson <ra...@modperl.pl>.
> On Wed, Mar 11, 2015 at 1:25 PM, David E. Wheeler <da...@justatheory.com>
> wrote:
>
> > On Mar 11, 2015, at 9:59 AM, David E. Wheeler <da...@justatheory.com>
> > wrote:
> >
> > > # Set the filename.
> > > my $file = File::Spec->catfile($sub_root, substr $r->uri, 1);
> > > $r->filename($file);
> > > $r->finfo(APR::Finfo::stat($file, APR::Const::FINFO_NORM,
> > $r->pool));
> > > return DECLINED;
> > > }
> > >
> > > I have no PerlReponseHandler, so Apache handles the response...and returns
> > 404. Note that a request for /hi.txt; it´s only a request in a
> > subdirectory, /foo/bar.html, that fails. Why? Why isn´t Apache able to find
> > that file? Is there some other attribute of $r I need to set or unset?
> >
> > Turns out I was able to fix this issue by unsetting path_info. I just added
> >
> > $r->path_info(undef);
> >
> > To the above code, and now Apache can find the file.
> >
> > I do not understand the relationship between filename (which is often not
> > the full path to a file) and path_info. Fortunately, it looks like I don´t
> > have to. But we might want to consider updating the docs to recommend
> > unsetting path_info when setting the full path to a file in filename.
> >
> >
> That is very curious. What was in path_info before? Is this a difference
> between undef and ""? Because path_info shouldn't be involved in finding
> the file, unless Apache is configured to ignore path info. And even that
> doesn't make sense because you weren't testing with
> /foo/bar.html/extra/pathinfo
>
> You could also check how this directive affects the problem (whether it
> should be turned off, or if your handler needs accept / deny the path info
> as with the 'default' argument:
>
> AcceptPathInfo On
>
> I found this on apache 2.4's docs:
> http://httpd.apache.org/docs/current/mod/core.html#AcceptPathInfo
FYI: This is a very important feature in Apache that allows for
dynamic image scripts to function (amongst other things). For
example, I have captcha images stored in a BYTEA column in
PostgreSQL, and I have a custom mod_perl2 script called "captcha"
which, to the general public, simply looks like another directory (if
you want a working example of this script, you can see it in use here
for now: http://www.inter-corporate.com/test/captcha.pl ).
The GET method in HTTP is used to download the image from
/images/captcha/-number-.jpeg which doesn't actually exist anywhere
on the file system. If the number is incorrect (e.g., a row with
that ID number doesn't exist in the SQL table), then my script simply
returns the Apache2 constant for NOT_FOUND.
This technique can be used for other things too, such as the output
of messages in a web-based forum, or calendar events, etc. The end
result is that the address bar is very tidy with numbers or other
identifying information, without need for any special characters like
interrogation marks and equal signs, which can be cryptic to users
who are not computer literate -- the following two example URIs show
the difference between what I consider to be a more user-friendly URI
and a more cryptic [to many non-computer-literate users] URI:
More user-friendly
http://www.example.com/events/2015/02/25/today.pl
More cryptic
http://www.example.com/events.pl?d=20150225&v=today
Even more cryptic
http://www.example.com/events.pl?y=2015&m=2&d=25&v=today
I tend to include the ".pl" suffix as opposed to the ".html" suffix
(or no suffix at all) because I like to promote Perl, but this part
is optional if you want that first link to be even simpler.
The first example.com link above practically speaks for itself, so
users can share such links with little or no explanation of what the
link is for. The second and third example.com links may require some
explanation, and then there are problems where users sometimes remove
the various parameters inadvertently or insert other characters
without noticing that they've done so.
An additional advantage I've encountered with this is that it
effectively offloads GET parameters into the URI, which are processed
via splitting $r->path_info by a slash and then that leaves any POST
parameters from an HTML form for processing more cleanly
behind-the-scenes.
It's really nice because I can parse the last portion, "today.pl"
(from the first example.com link above), and if it was changed to
something like "banner.pl" or "logo.pl" then I could output an image
file instead to serve HTML code output by "today.pl" that references
an image as so: <img src=logo.pl width=65 height=65 border=0>
Using the relative URI, again the processing of $r->path_info
results in my initial "/events" Perl script determining which event
to look at (at least the date in this example, for which an event ID
number could be used instead of the date, etc.) and transmitting the
correct logo file when the web browser sends the request for it.
I hope this information will be of interest to someone.
Randolf Richardson - randolf@inter-corporate.com
Inter-Corporate Computer & Network Services, Inc.
Beautiful British Columbia, Canada
http://www.inter-corporate.com/
Re: How Do I change the Document Root Per Request
Posted by "David E. Wheeler" <da...@justatheory.com>.
On Mar 11, 2015, at 10:39 AM, Lathan Bidwell <la...@andrews.edu> wrote:
> That is very curious. What was in path_info before? Is this a difference between undef and ""? Because path_info shouldn't be involved in finding the file, unless Apache is configured to ignore path info. And even that doesn't make sense because you weren't testing with /foo/bar.html/extra/pathinfo
If I requested /foo/bar/hi.html, filename would be /foo/bar and path_info would be /hi.html. It might be that Apache decided to do something funky here because its map to storage handler could not find $docroot/foo/bar/hi.html, and it’s only later that my PerlTypeHandler sets the file to $docroot/$user/foo/bar/hi.html.
> You could also check how this directive affects the problem (whether it should be turned off, or if your handler needs accept / deny the path info as with the 'default' argument:
> AcceptPathInfo On
> I found this on apache 2.4's docs: http://httpd.apache.org/docs/current/mod/core.html#AcceptPathInfo
No idea, I’m loathe to mess with it anymore.
David
Re: How Do I change the Document Root Per Request
Posted by Lathan Bidwell <la...@andrews.edu>.
On Wed, Mar 11, 2015 at 1:25 PM, David E. Wheeler <da...@justatheory.com>
wrote:
> On Mar 11, 2015, at 9:59 AM, David E. Wheeler <da...@justatheory.com>
> wrote:
>
> > # Set the filename.
> > my $file = File::Spec->catfile($sub_root, substr $r->uri, 1);
> > $r->filename($file);
> > $r->finfo(APR::Finfo::stat($file, APR::Const::FINFO_NORM,
> $r->pool));
> > return DECLINED;
> > }
> >
> > I have no PerlReponseHandler, so Apache handles the response…and returns
> 404. Note that a request for /hi.txt; it’s only a request in a
> subdirectory, /foo/bar.html, that fails. Why? Why isn’t Apache able to find
> that file? Is there some other attribute of $r I need to set or unset?
>
> Turns out I was able to fix this issue by unsetting path_info. I just added
>
> $r->path_info(undef);
>
> To the above code, and now Apache can find the file.
>
> I do not understand the relationship between filename (which is often not
> the full path to a file) and path_info. Fortunately, it looks like I don’t
> have to. But we might want to consider updating the docs to recommend
> unsetting path_info when setting the full path to a file in filename.
>
>
That is very curious. What was in path_info before? Is this a difference
between undef and ""? Because path_info shouldn't be involved in finding
the file, unless Apache is configured to ignore path info. And even that
doesn't make sense because you weren't testing with
/foo/bar.html/extra/pathinfo
You could also check how this directive affects the problem (whether it
should be turned off, or if your handler needs accept / deny the path info
as with the 'default' argument:
AcceptPathInfo On
I found this on apache 2.4's docs:
http://httpd.apache.org/docs/current/mod/core.html#AcceptPathInfo
Best,
>
> David
>
>
Re: How Do I change the Document Root Per Request
Posted by "David E. Wheeler" <da...@justatheory.com>.
On Mar 11, 2015, at 9:59 AM, David E. Wheeler <da...@justatheory.com> wrote:
> # Set the filename.
> my $file = File::Spec->catfile($sub_root, substr $r->uri, 1);
> $r->filename($file);
> $r->finfo(APR::Finfo::stat($file, APR::Const::FINFO_NORM, $r->pool));
> return DECLINED;
> }
>
> I have no PerlReponseHandler, so Apache handles the response…and returns 404. Note that a request for /hi.txt; it’s only a request in a subdirectory, /foo/bar.html, that fails. Why? Why isn’t Apache able to find that file? Is there some other attribute of $r I need to set or unset?
Turns out I was able to fix this issue by unsetting path_info. I just added
$r->path_info(undef);
To the above code, and now Apache can find the file.
I do not understand the relationship between filename (which is often not the full path to a file) and path_info. Fortunately, it looks like I don’t have to. But we might want to consider updating the docs to recommend unsetting path_info when setting the full path to a file in filename.
Best,
David
Re: How Do I change the Document Root Per Request
Posted by "David E. Wheeler" <da...@justatheory.com>.
On Mar 11, 2015, at 10:15 AM, Lathan Bidwell <la...@andrews.edu> wrote:
> I am not an expert at this, so I don't have an answer.
Thanks for your reply, I appreciate it.
> But I can suggest a few debugging steps to clear out of the way:
>
> 1) Confirm that your document root is showing properly in the error log
> Does the error log report "/var/html/foo/bar.html" is not found, or does it only show the request? On my local apache instance if I do /blah/foo (non existant), I get: File does not exist: /home/www/html/blah (which has my document root in it).
Yeah, the document root is correct.
> 2) Confirm that path exists, Look for a folder structure you can create that will work
> if /var/html/foo/bar.html doesn't exist, is it looking for /var/www/foo/bar.html or /foo/bar.html
Yes, I was logging the value I was putting into filename(), and yes, it did exist.
> 3) Do you have any rewrite urls or other location / directory directives that could be overriding hits?
> Check your config files, and then put in some print STDERR / smart comments to confirm if your routing program is returning the DECLINED
Nope.
> 4) Does it work for DirectoryIndexes?
I don’t know, but it turns out that unsetting path_info() fixed the issue for me. How Apache uses filename and path_info together is opaque to me.
Best,
David
Re: How Do I change the Document Root Per Request
Posted by Lathan Bidwell <la...@andrews.edu>.
David,
I am not an expert at this, so I don't have an answer.
But I can suggest a few debugging steps to clear out of the way:
1) Confirm that your document root is showing properly in the error log
Does the error log report "/var/html/foo/bar.html" is not found, or does it
only show the request? On my local apache instance if I do /blah/foo (non
existant), I get: File does not exist: /home/www/html/blah (which has my
document root in it).
2) Confirm that path exists, Look for a folder structure you can create
that will work
if /var/html/foo/bar.html doesn't exist, is it looking for
/var/www/foo/bar.html or /foo/bar.html
3) Do you have any rewrite urls or other location / directory directives
that could be overriding hits?
Check your config files, and then put in some print STDERR / smart comments
to confirm if your routing program is returning the DECLINED
4) Does it work for DirectoryIndexes?
Lathan
On Wed, Mar 11, 2015 at 12:59 PM, David E. Wheeler <da...@justatheory.com>
wrote:
> On Mar 6, 2015, at 5:29 PM, David E. Wheeler <da...@justatheory.com>
> wrote:
>
> > And now it works just how I want.
>
> I take it back. It works for files in the root, but not subdirectories.
>
> So say my document root is /var/html, and a request comes in for
> /foo/bar.html. Apache has mapped it to /var/html/foo/bar.html, but in my
> PerlTypeHandler, I re-map it to /var/html/david/foo/bar.html and set finfo:
>
> sub handler {
> my $r = shift;
>
> # We only want to do this once per request.
> return DECLINED unless $r->is_initial_req;
>
> # Get the username.
> my $user = $r->user or return HTTP_UNAUTHORIZED;
>
> # Return forbidden if the subscriber ID subdirectory does not exist.
> my $doc_root = $r->document_root;
> my $sub_root = File::Spec->catdir($doc_root, $user);
> return HTTP_FORBIDDEN unless -d $sub_root;
>
> # Set the filename.
> my $file = File::Spec->catfile($sub_root, substr $r->uri, 1);
> $r->filename($file);
> $r->finfo(APR::Finfo::stat($file, APR::Const::FINFO_NORM, $r->pool));
> return DECLINED;
> }
>
> I have no PerlReponseHandler, so Apache handles the response…and returns
> 404. Note that a request for /hi.txt; it’s only a request in a
> subdirectory, /foo/bar.html, that fails. Why? Why isn’t Apache able to find
> that file? Is there some other attribute of $r I need to set or unset?
>
> Thanks,
>
> David
>
>
Re: How Do I change the Document Root Per Request
Posted by "David E. Wheeler" <da...@justatheory.com>.
On Mar 6, 2015, at 5:29 PM, David E. Wheeler <da...@justatheory.com> wrote:
> And now it works just how I want.
I take it back. It works for files in the root, but not subdirectories.
So say my document root is /var/html, and a request comes in for /foo/bar.html. Apache has mapped it to /var/html/foo/bar.html, but in my PerlTypeHandler, I re-map it to /var/html/david/foo/bar.html and set finfo:
sub handler {
my $r = shift;
# We only want to do this once per request.
return DECLINED unless $r->is_initial_req;
# Get the username.
my $user = $r->user or return HTTP_UNAUTHORIZED;
# Return forbidden if the subscriber ID subdirectory does not exist.
my $doc_root = $r->document_root;
my $sub_root = File::Spec->catdir($doc_root, $user);
return HTTP_FORBIDDEN unless -d $sub_root;
# Set the filename.
my $file = File::Spec->catfile($sub_root, substr $r->uri, 1);
$r->filename($file);
$r->finfo(APR::Finfo::stat($file, APR::Const::FINFO_NORM, $r->pool));
return DECLINED;
}
I have no PerlReponseHandler, so Apache handles the response…and returns 404. Note that a request for /hi.txt; it’s only a request in a subdirectory, /foo/bar.html, that fails. Why? Why isn’t Apache able to find that file? Is there some other attribute of $r I need to set or unset?
Thanks,
David
Re: How Do I change the Document Root Per Request
Posted by "David E. Wheeler" <da...@justatheory.com>.
On Mar 6, 2015, at 9:21 AM, David E. Wheeler <da...@justatheory.com> wrote:
>>
>> PerlMapToStorageHandler Apache2::Const::OK
>>
>> Otherwise it just fails super early. By adding this line, I am able to freely set the filename later.
>
> Alas, this does *not* work for directory requests, just files. Directories return 404. Is there some other attribute other than filename and finfo I need to set so that, when I decline a request, Apache will find the directory and use mod_autoindex to display it? It does do so if I remove the above PerlMapToStorageHandler statement, so there must be some crucial attribute that needs settings for it to find a directory, right?
I managed to fix this by changing my handler to a fixup handler and not setting any request handler at all. The fixup handler returns OK for HTML requests (it uses content negotiation and a GET param to decide what to return) and installs a relevant handler for other formats:
return OK if $format eq 'html';
$r->handler('modperl');
if ($format eq 'json') {
$r->set_handlers(PerlResponseHandler => \&handle_json);
} elsif ($format eq 'xml') {
$r->set_handlers(PerlResponseHandler => \&handle_xml);
} elsif ($format eq 'text') {
$r->set_handlers(PerlResponseHandler => \&handle_text);
}
And now it works just how I want.
Thanks,
David
Re: How Do I change the Document Root Per Request
Posted by "David E. Wheeler" <da...@justatheory.com>.
On Mar 3, 2015, at 2:27 PM, David E. Wheeler <da...@justatheory.com> wrote:
> And now I got it to work. The key was to add.
>
> PerlMapToStorageHandler Apache2::Const::OK
>
> Otherwise it just fails super early. By adding this line, I am able to freely set the filename later.
Alas, this does *not* work for directory requests, just files. Directories return 404. Is there some other attribute other than filename and finfo I need to set so that, when I decline a request, Apache will find the directory and use mod_autoindex to display it? It does do so if I remove the above PerlMapToStorageHandler statement, so there must be some crucial attribute that needs settings for it to find a directory, right?
Thanks,
David
Re: How Do I change the Document Root Per Request
Posted by "David E. Wheeler" <da...@justatheory.com>.
On Mar 3, 2015, at 11:34 AM, David E. Wheeler <da...@justatheory.com> wrote:
> I managed to get a little further by switching from PerlFixupHandler to PerlTypeHandler. I still get some 404s for files, but all the directories serve properly. As it happens, I have a response handler that handles directory requests, but it declines file requests. I assume, then, that the default Apache response handler doesn’t know that I’ve updated the filename. Is there some way to get it to do that?
And now I got it to work. The key was to add.
PerlMapToStorageHandler Apache2::Const::OK
Otherwise it just fails super early. By adding this line, I am able to freely set the filename later.
Thanks,
David
Re: How Do I change the Document Root Per Request
Posted by "David E. Wheeler" <da...@justatheory.com>.
On Mar 2, 2015, at 9:35 PM, David E. Wheeler <da...@justatheory.com> wrote:
> PerlLoadModule My::UserFixup
> <Location />
> PerlFixupHandler My::UserFixup
> AuthType Basic
> AuthName "User File Service"
> Require valid-user
> </Location>
I managed to get a little further by switching from PerlFixupHandler to PerlTypeHandler. I still get some 404s for files, but all the directories serve properly. As it happens, I have a response handler that handles directory requests, but it declines file requests. I assume, then, that the default Apache response handler doesn’t know that I’ve updated the filename. Is there some way to get it to do that?
Thanks,
David
Re: How Do I change the Document Root Per Request
Posted by "David E. Wheeler" <da...@justatheory.com>.
On Mar 2, 2015, at 8:27 PM, Fred Moyer <fr...@redhotpenguin.com> wrote:
> Can you show us your relevant httpd.conf snippet? No guesses right
> now, but that might help.
Sure.
PerlLoadModule My::UserFixup
<Location />
PerlFixupHandler My::UserFixup
AuthType Basic
AuthName "User File Service"
Require valid-user
</Location>
There’s also a simple PerlAuthenHandler. Pretty simple stuff.
I’ve gotten a little further since my post on Friday, changing the filename instead of the doc_root:
sub handler {
my $r = shift;
# We only want to do this once per request.
return DECLINED unless $r->is_initial_req;
# Get the username.
my $user = $r->user or return HTTP_UNAUTHORIZED;
# Return forbidden if the subscriber ID subdirectory does not exist.
my $doc_root = $r->document_root;
my $sub_root = File::Spec->catdir($doc_root, $user);
return HTTP_FORBIDDEN unless -d $sub_root;
# Set the filename.
my $file = File::Spec->catfile($sub_root, substr $r->uri, 1);
$r->filename($file);
$r->finfo(APR::Finfo::stat($file, APR::Const::FINFO_NORM, $r->pool));
return DECLINED;
}
This works for root requests (/), but not anything else, which always return 404. It’s driving me crazy. Maybe there’s something else i need to set? This has *got* to be a common thing people do, right? It’s driving me bananas (and towards Plack [again!]).
Thanks,
David
Re: How Do I change the Document Root Per Request
Posted by Fred Moyer <fr...@redhotpenguin.com>.
Can you show us your relevant httpd.conf snippet? No guesses right
now, but that might help.
On Sun, Mar 1, 2015 at 4:46 PM, David E. Wheeler <da...@justatheory.com> wrote:
> Hi,
>
> I want to set the document root for a request to map to the basic auth username. I tried this in a PerlFixupHandler:
>
> sub handler {
> my $r = shift;
>
> # We only want to do this once per request.
> return DECLINED unless $r->is_initial_req;
>
> # Get the username.
> my $user = $r->user or return HTTP_UNAUTHORIZED;
>
> # Return forbidden if the username subdiectory does not exist.
> my $doc_root = File::Spec->catdir($r->document_root, $user);
> return HTTP_FORBIDDEN unless -d $doc_root;
>
> # Set the document root for the duration of this request and return.
> $r->document_root($doc_root);
> return DECLINED;
> }
>
> But alas, it still serves the original document root. How can I get it to change the document root on a per-request basis? If I cant, should I change the URI or the filename, instead?
>
> Thnks,
>
> David