root/trunk/extlib/Net/OpenID/URIFetch.pm @ 3531

Revision 3531, 4.6 kB (checked in by fumiakiy, 9 months ago)

Merged sockfish to trunk. "svn merge -r3114:3527 http://code.sixapart.com/svn/movabletype/branches/sockfish/ ."

Line 
1#!/usr/bin/perl
2
3=head1 NAME
4
5Net::OpenID::URIFetch - fetch and cache content from HTTP URLs
6
7=head1 DESCRIPTION
8
9This is roughly based on Ben Trott's URI::Fetch module, but
10URI::Fetch doesn't cache enough headers that Yadis can be implemented
11with it, so this is a lame copy altered to allow Yadis support.
12
13Hopefully one day URI::Fetch can be modified to do what we need and
14this can go away.
15
16This module is tailored to the needs of Net::OpenID::Consumer and probably
17isn't much use outside of it. See URI::Fetch for a more general module.
18
19=cut
20
21package Net::OpenID::URIFetch;
22
23use HTTP::Request;
24use HTTP::Status;
25use strict;
26use warnings;
27use Carp;
28
29our $HAS_ZLIB;
30BEGIN {
31    $HAS_ZLIB = eval "use Compress::Zlib (); 1;";
32}
33
34use constant URI_OK                => 200;
35use constant URI_MOVED_PERMANENTLY => 301;
36use constant URI_NOT_MODIFIED      => 304;
37use constant URI_GONE              => 410;
38
39sub fetch {
40    my ($class, $uri, $consumer, $content_hook) = @_;
41
42    if ($uri eq 'x-xrds-location') {
43        Carp::confess("Buh?");
44    }
45
46    my $ua = $consumer->ua;
47    my $cache = $consumer->cache;
48    my $ref;
49
50    # By prefixing the cache key, we can ensure we won't
51    # get left-over cache items from older versions of Consumer
52    # that used URI::Fetch.
53    my $cache_key = 'URIFetch:'.$uri;
54
55    if ($cache) {
56        if (my $blob = $cache->get($cache_key)) {
57            $ref = Storable::thaw($blob);
58        }
59    }
60
61    # We just serve anything from the last 60 seconds right out of the cache,
62    # thus avoiding doing several requests to the same URL when we do
63    # Yadis, then HTML discovery.
64    # TODO: Make this tunable?
65    if ($ref && $ref->{CacheTime} > (time() - 60)) {
66        $consumer->_debug("Cache HIT for $uri");
67        return Net::OpenID::URIFetch::Response->new(
68            status => 200,
69            content => $ref->{Content},
70            headers => $ref->{Headers},
71            final_uri => $ref->{FinalURI},
72        );
73    }
74    else {
75        $consumer->_debug("Cache MISS for $uri");
76    }
77
78    my $req = HTTP::Request->new(GET => $uri);
79    if ($HAS_ZLIB) {
80        $req->header('Accept-Encoding', 'gzip');
81    }
82    if ($ref) {
83        if (my $etag = ($ref->{Headers}->{etag})) {
84            $req->header('If-None-Match', $etag);
85        }
86        if (my $ts = ($ref->{Headers}->{'last-modified'})) {
87            $req->if_modified_since($ts);
88        }
89    }
90
91    my $res = $ua->request($req);
92
93    # There are only a few headers that OpenID/Yadis care about
94    my @useful_headers = qw(last-modified etag content-type x-yadis-location x-xrds-location);
95
96    my %response_fields;
97
98    if ($res->code == HTTP::Status::RC_NOT_MODIFIED()) {
99        $consumer->_debug("Server says it's not modified. Serving from cache.");
100        return Net::OpenID::URIFetch::Response->new(
101            status => 200,
102            content => $ref->{Content},
103            headers => $ref->{Headers},
104            final_uri => $ref->{FinalURI},
105        );
106    }
107    else {
108        my $content = $res->content;
109        my $final_uri = $res->request->uri->as_string();
110        my $final_cache_key = "URIFetch:".$final_uri;
111
112        if ($res->content_encoding && $res->content_encoding eq 'gzip') {
113            $content = Compress::Zlib::memGunzip($content);
114        }
115
116        if ($content_hook) {
117            $content_hook->(\$content);
118        }
119
120        my $headers = {};
121        foreach my $k (@useful_headers) {
122            $headers->{$k} = $res->header($k);
123        }
124
125        my $ret = Net::OpenID::URIFetch::Response->new(
126            status => $res->code,
127            content => $content,
128            headers => $headers,
129            final_uri => $final_uri,
130        );
131
132        if ($cache && $res->code == 200) {
133            my $cache_data = {
134                Headers => $ret->headers,
135                Content => $ret->content,
136                CacheTime => time(),
137                FinalURI => $final_uri,
138            };
139            my $cache_blob = Storable::freeze($cache_data);
140            $cache->set($final_cache_key, $cache_blob);
141            $cache->set($cache_key, $cache_blob);
142        }
143
144        return $ret;
145    }
146
147}
148
149package Net::OpenID::URIFetch::Response;
150
151sub new {
152    my ($class, %opts) = @_;
153
154    my $self = {};
155    $self->{final_uri} = delete($opts{final_uri});
156    $self->{status} = delete($opts{status});
157    $self->{content} = delete($opts{content});
158    $self->{headers} = delete($opts{headers});
159
160    return bless $self, $class;
161}
162
163sub final_uri {
164    return $_[0]->{final_uri};
165}
166
167sub status {
168    return $_[0]->{status};
169}
170
171sub content {
172    return $_[0]->{content};
173}
174
175sub headers {
176    return $_[0]->{headers};
177}
178
179sub header {
180    return $_[0]->{headers}{lc($_[1])};
181}
182
1831;
Note: See TracBrowser for help on using the browser.