/usr/local/CPAN/CPAN-CachingProxy/CPAN/CachingProxy.pm


#!/usr/bin/perl

package CPAN::CachingProxy;

use strict;
use Carp;
use Cache::File;
use Data::Dumper;
use LWP::UserAgent;

our $VERSION = 1.4002;

# wget -O MIRRORED.BY http://www.cpan.org/MIRRORED.BY

# new {{{
sub new {
    my $class = shift;
    my $this  = bless {@_}, $class;

    unless( $this->{cgi} ) {
        require CGI or die $@;
        $this->{cgi} = new CGI;
    }

    unless( $this->{cache_object} ) {
        $this->{cache_root}      = "/tmp/ccp/" unless exists $this->{cache_root};
        $this->{default_expire} = "2 day"      unless exists $this->{default_expire};
        $this->{index_expire}   = "3 hour"     unless exists $this->{index_expire};
        $this->{error_expire}   = "15 minute"  unless exists $this->{error_expire};

        $this->{index_regexp}   = qr/(?:03modlist\.data|02packages\.details\.txt|01mailrc\.txt)/ unless exists $this->{index_regexp};
        $this->{cache_object}   = Cache::File->new(cache_root=>$this->{cache_root}, default_expires => $this->{default_expire} );
    }

    $this->{key_space}        = "CK" unless $this->{key_space};

    unless( $this->{ua} ) {
        my $ua = $this->{ua} = new LWP::UserAgent;
           $ua->agent($this->{agent} ? $this->{agent} : "CCP/$VERSION (Paul's CPAN caching proxy / perlmonks-id=16186)");
           if( exists $this->{activity_timeout} ) {
               if( defined (my $at = $this->{activity_timeout}) ) {
                   $ua->timeout($at);
               }

           } else {
               $ua->timeout(12);
           }
    }

    $this->{ua}->timeout( $this->{activity_timeout} ) if defined $this->{activity_timeout};

    croak "there are no default mirrors, they must be set" unless $this->{mirrors};

    return $this;
}
# }}}
# run {{{
sub run {
    my $this   = shift;
    my $cgi    = $this->{cgi};
    my $mirror = $this->{mirrors}[ rand @{$this->{mirrors}} ];
    my $pinfo  = $cgi->path_info;
       $pinfo =~ s/^\///;
       $mirror=~ s/\/$//;

    my $CK    = "$this->{key_space}:$pinfo";
    my $again = 0;

    THE_TOP:
    my $cache = $this->{cache_object};
    if( $cache->exists($CK) and $cache->exists("$CK.hdr") ) { our $VAR1;
        my $res = eval $cache->get( "$CK.hdr" ); die "problem finding cache entry\n" if $@;

        my $status = $res->status_line;

        warn "[DEBUG] status: $status" if $this->{debug};
        print $cgi->header(-status=>$status, -type=>$res->header( 'content-type' ));

        if( $res->is_success ) {
            my $fh = $cache->handle( $CK, "<" ) or die "problem finding cache entry\n";
            my $buf;
            while( read $fh, $buf, 4096 ) {
                print $buf;
            }
            close $fh;

        } else {
            print $status;
        }

        unless( $res->is_success ) {
            warn "[DEBUG] removing $CK" if $this->{debug};
            $cache->remove($CK);
        }

        return;

    } elsif( not $again ) {
        $again = 1;

        my $expire = $this->{default_expire};
           $expire = $this->{index_expire} if $pinfo =~ $this->{index_regexp};

        $cache->set($CK, 1, $expire ); # doesn't seem like we should have to do this, but apparently we do

        my $URL = "$mirror/$pinfo";
         # $URL =~ s/\/{2,}/\//g;

        warn "[DEBUG] getting $URL" if $this->{debug};

        my $fh = $cache->handle( $CK, ">", $expire );
        my $request  = HTTP::Request->new(GET => $URL);
        my $response = $this->{ua}->request($request, sub { my $chunk = shift; print $fh $chunk });
        close $fh;

        warn "[DEBUG] setting $CK" if $this->{debug};
        $cache->set("$CK.hdr", Dumper($response), $expire);

        # if there was an error (which we don't know until ex post facto), go back and fix the expiry
        if( defined $this->{error_expire} and not $response->is_success ) {
            $cache->set_expiry( $CK       => $this->{error_expire} );
            $cache->set_expiry( "$CK.hdr" => $this->{error_expire} );
        }

        goto THE_TOP;
    }

    die "problem fetching $pinfo. :(\n";
}
# }}}