Torsten Förtsch wrote:
On 11/12/2012 06:19 PM, André Warnier wrote:
In response to some request URLs, I have to compose a response
structured as follows :
- a html frameset document with two frames
- in the 1st frame, the result of another HTTP request to the same
Apache server.
This URI of this HTTP request is created by "mangling" the original
request URL somewhat.
- in the 2d frame, the content of the HTML file corresponding to the
original request URI.
For example, if the original request URI was something like :
"/abc/def/ghi.html", then
- the top frame should contain the (html) output of a request to
"/cgi-bin/mangle.pl?arg=ghi"
- the bottom frame should contain the content of the
originally-requested "/abc/def/ghi.html" URI (which is a static file).
I am thinking of doing this by :
- creating a mod_perl ResponseHandler
- having this response handler make a first sub-request to the
"/cgi-bin/mangle.pl?arg=ghi" URI, grabbing the content of the response
to that sub-request, and insert it into the first <frame> of the output
<frameset> document.
- having the response handler do a lookup_uri of the original URI, get
the resulting filename, reading the file and insert its content into the
second <frame> of the output <frameset> document.
My 1st question is : is the above a valid plan, or is there something
fundamentally wrong with this approach ?
My 2d question is : looking at the Apache2::SubRequest documentation, I
do not see a clear way of getting the response content of a subrequest.
For example, the run() method explanation seems to indicate that the
response content is sent directly to the client.
Am I missing something ?
I don't really understand what you want to achieve. But if you just want
to insert a header on top of each page, this can be done without a
frameset (you know, they are utterly deprecated).
Use a PerlOutputFilterHandler to insert a <div> or perhaps even an
<iframe> just after the opening <body> tag.
Once you have found the <body> tag in the output stream, you pass it
further down the filter chain and add perhaps the opening <div>. Then
you create and run the subrequest. Then you put out the rest of the
brigade, remove the filter and return. Thus, the filter will probably be
called only once because the <body> tag tends to appear within the first
few kbytes of the document.
If that is the case, you can even adjust the Content-Length header
properly to include the additional stuff. Otherwise you should remove
the Content-Length header.
Things you have to consider:
- filtering data costs CPU cylces
This is especially true if you served just plain files before by
sendfile(2).
- on the other hand, you can put mod_cache in front of those requests
(even in the same apache) and they will be served even faster than the
plain files before
- deleting the Content-Length header is to be avoided if possible. It
increases the overall transfer time considerably
- if you have used precompressed plain files before they are now
useless. Instead you'll have to use the DEFLATE filter. This costs
additional CPU cycles. But again mod_cache helps a lot.
One other idea comes to mind. If you want to stick with your frameset
but prevent multiple requests perhaps you can use "data:" URLs as src.
This probably requires much more complicated filtering.
Torsten
Hi.
I didn't want to take too much time of anyone before, which is why I somewhat
oversimplified the issue. But considering the traffic on the lis os low, maybe you want
to hear the whole story after all.
The basic case is this : a bit aside from our usual professional activities and for a
friend, we run a website which is basically a shop with hundreds of individual items which
people can view and buy. (I will provide the URL privately to anyone who is more
interested. It's a cute shop.)
The pages corresponding to these individual items, at the moment, are individual static
pages in multiple sub-directories, and there are quite a lot of them. The friend creates
and maintains these static pages herself; she is an artist rather than a programmer, so
she can handle an html editor (which she does rather well) to edit static pages and test
them on her PC before copying them to the server, but we cannot ask her to handle any kind
of "template" pages or the like.
Add to this that the basic logic of the website and the design and techniques used date
back from some 10 years ago, have been patched and repatched several times over several
years, and are rather bad.
Now there is a requirement that, instead of being just static pages, each of these pages
should in addition contain a <form> with some specific item-related information, allowing
to buy these items on-line (so it cannot be done just with an include or a stylesheet).
What I am trying to achieve, without having to edit each of these individual hundreds of
pages, or changing the links to these pages, or change the basic design of the application
(because there is no budget for that), is to find a clever way on the server side
to respond to a normal "/Shop_xxx/def/xyz.html" URL of one of these pages, to combine into
one response both the required <form>, and the content of the existing unmodified static
page. I also do not want to parse the html on-the-fly and insert a <form> right into it,
because the html that she creates with her (T-online) editor is rather bad to start with,
and I have no guarantee that the result would be pleasing.
So that was the reason to think of the <frameset> solution, whereby in response to the
initial request for "/Shop_xxx/def/xyz.html", I would respond with a first <frame>
containing the form (generated by a back-end application, and depending on the item), and
a second <frame> containing her artfully-crafted static page describing that item in all
its glory.
The static pages in question are in several subdirectories of DocumentRoot, and at
different levels. Fortunately, all the top sub-directories names start with "/Shop_"
(after which there can be "Quilts" or "Babydecken" and things like that, and a variable
hierarchy of sub-directories containing html files and jpg images and the like.
So I have this configured :
<LocationMatch "^/Shop_">
sethandler modperl
PerlResponseHandler My::ShopResponse
...
</LocationMatch>
As a result in part of the previous communications on this list, this
PerlResponseHandler
does more or less what I want, except one remaining problem which I am trying to resolve
right now :
In response to an initial request for "/Shop_xxx/def/xyz.html", the handler
generates a
<frameset> document as such :
<html>
<frameset rows="100,*">
<frame name="top_frame" src="..the URI which generates the form.." /> (1)
<frame name="bottom_frame" src="/Shop_xxx/def/xyz.html.shop" /> (2)
</frameset>
</html>
(1) for the dynamically-generated html <form> document
(2) for the static existing page
Because the second frame's URI also starts with "/Shop_", when the browser requests this
frame, the same ResponseHandler is called.
The handler examines the URL and sees that it ends in ".shop" (instead of
".html").
So it knows that this time, it should not send another frameset, but instead it should
strip the trailing ".shop" and deliver, as is, the content of the static document
"/Shop_xxx/def/xyz.html".
But, how do I tell it to do that ?
I have tried :
my $uri = $r->uri();
if ($uri =~ m!\/([^/]+\.htm[l]?\.shop)$!i) {
$uri =~ s/\.shop$//; # strip the trailing ".shop"
$r->internal_redirect($uri);
return Apache2::Const::OK ;
}
and also :
my $uri = $r->uri();
if ($uri =~ m!\/([^/]+\.htm[l]?\.shop)$!i) {
$uri =~ s/\.shop$//; # strip the trailing ".shop"
my $subr = $r->lookup_uri($uri);
$subr->run();
return Apache2::Const::OK ;
}
but both of those result in a loop : they end up requesting "/Shop_xxx/def/xyz.html",
which hits the same Location, which runs the same handler, which then produces the
frameset, and so on.
So how do I tell Apache/mod_perl that this time "I mean it", and that it should directly
deliver the requested file, without re-running the whole cycle ?
I can of course request the corresponding filename() and deliver it myself (perhaps with
sendfile()), but that does not seem to be the most elegant way of doing this. Or is it ?
Oh, and I'd like it elegant, but I would prefer not having to introduce a PerlFixupHandler
or a PerlOutputFilter or Javascript, and do it all within this ResponseHandler. That's
because I have colleagues who know even less about mod_perl than I do, and I'd like to
leave them something simple to deal with in support and maintenance for another 10 years.
...
Aside : I just tried
my $uri = $r->uri();
if ($uri =~ m!\/([^/]+\.htm[l]?\.shop)$!i) {
$uri =~ s/\.shop$//; # strip the trailing ".shop"
my $subr = $r->lookup_uri($uri);
$r->sendfile($subr->filename());
return Apache2::Const::OK ;
}
and that works. So I guess that /is/ the right solution here.