Re: HTTP::Daemon

Doug MacEachern (dougm@osf.org)
Fri, 18 Oct 1996 18:04:10 -0400 (EDT)


On 17 Oct 1996, Randal Schwartz wrote:

> >>>>> "Gisle" == Gisle Aas <aas@bergen.sn.no> writes:
> 
> Gisle> The following module will be in the next release of libwww-perl,
> Gisle> i.e. libwww-perl is not a client library any more :-)
> 
> Well, shoot, I independently came up with this...
> a full-featured multi-child proxy server.  Still a lot to do,
> but if you have comments to make on my code, I'd appreciate it.
> 
> I could wire your HTTP::Daemon stuff into it, but I've probably
> duplicated the code I needed already.

You might want to keep Apache's mod_perl in mind for this sort of thing.  You
can have your Apache/Perl module step in at the uri translation stage of the request and
decide to handle the request only if it's a proxy request, while letting apache do
most of the work, e.g. setting up children, connections, etc.  Your module is "compiled" into
perl's internal format when the server starts, so it's fast too.

I had played with something like this, but didn't spend enough time to make it
useful for anything more than decent example to start from:

---8<----

package Apache::ProxyCache;

use strict;

use File::Path;
use File::Basename;
use IO::File ();
use URI::URL ();
use LWP::UserAgent ();
use HTTP::Request ();
use Apache::Constants ':common';
use Apache ();

sub translate {
    my($r) = @_;

    return DECLINED unless $r->proxyreq;
    $r->handler("perl-script"); #ok, let's do it
    return OK;
}

sub handler {
    my($r) = @_;
    my $url = new URI::URL $r->uri;

    my($rc,$file,$fh,$CacheDir);
    
    $CacheDir = $r->dir_config("ApacheProxyCacheDir"); #from server config file
    my $file = join "/", $CacheDir, $url->host.$url->port, $url->path; #XXX handle escapes
    $file .= "index.html" if $url->path =~ m:/$:;

    if(-f $file) {
	$fh = new IO::File $file;
    }
    else {
	($rc, $fh) = forward_request($r, $url, $file);
	return $rc unless $fh;
    }

    $r->content_type("text/html");
    $r->send_http_header();
    $r->send_fd($fh);

    return OK;
}

sub forward_request {
    my($r, $url, $file) = @_;
    my($key,$val,%headers_in);

    my $request = new HTTP::Request $r->method, $url->as_string;

    %headers_in = $r->headers_in;
    while(($key,$val) = each %headers_in) {
	$request->header($key,$val);
    }

    my $ua = new LWP::UserAgent;
    my $res = $ua->request($request);
    unless($res->is_success) {
	$r->log_reason(sprintf("Apache::ProxyCache::handler failed: %s", $res->message), $r->uri);
	return($res->code, undef);
    }

    return(cache_it($r, $res->content, $file));
}

sub cache_it {
    my($r, $content, $file) = @_;
    my $dir = dirname $file;
    mkpath $dir unless -d $dir;
    my $fh = new IO::File "> $file";
    unless($fh) {
	$r->log_reason("open > $file failed $!", $r->uri);
	return(SERVER_ERROR, undef);
    }
    print $fh $content;
    $fh->close;
    
    return(OK, new IO::File $file); #shrug
}

1;

__END__

#in server config file add:
PerlTransHandler Apache::ProxyCache::translate
PerlHandler      Apache::ProxyCache::handler
PerlSetVar       ApacheProxyCacheDir  /tmp/foo

---8<---

You need the latest developer's release of mod_perl, now 0.83_07 (same as
0.83_06 with missing Makefile) which is on it's way to CPAN from:
http://www.osf.org/~dougm/apache/mod_perl-0.83_07.tar.gz

Cheers,
-Doug