File Coverage

blib/lib/Plack/Middleware/ESI.pm
Criterion Covered Total %
statement 80 86 93.0
branch 21 32 65.6
condition 1 3 33.3
subroutine 14 14 100.0
pod 1 1 100.0
total 117 136 86.0


line stmt bran cond sub pod time code
1             package Plack::Middleware::ESI;
2 1     1   1062 use strict;
  1         3  
  1         43  
3 1     1   6 use warnings;
  1         1  
  1         63  
4             our $VERSION = '0.1';
5 1     1   17 use parent qw(Plack::Middleware);
  1         3  
  1         9  
6 1     1   63 use Plack::Util;
  1         2  
  1         24  
7 1     1   1274 use Plack::Request;
  1         98262  
  1         35  
8 1     1   5456 use LWP::UserAgent;
  1         57332  
  1         39  
9 1     1   11 use HTTP::Request;
  1         2  
  1         28  
10 1     1   1162 use HTTP::Message::PSGI;
  1         5856  
  1         1225  
11              
12             sub call {
13 1     1 1 32443 my ($self, $env) = @_;
14 1         19 my $res = $self->app->($env);
15             $self->response_cb($res, sub {
16 1     1   20 my $res = shift;
17 1         6 my $h = Plack::Util::headers($res->[1]);
18 1         51 my $r = Plack::Request->new($env);
19 1         17 my $ct = $h->get('Content-Type');
20 1 50 33     51 if ($ct =~ /^text\// || $ct =~ /^application\/xh?t?ml\b/) { #"{}
21             return sub {
22 6         62 my $chunk = shift;
23 6 100       21 return unless defined $chunk;
24 5         15 return $self->_process_esi($chunk, $r);
25 1         19 };
26             }
27 1         154 });
28             }
29              
30             sub _process_esi {
31 5     5   13 my ($self, $chunk, $r) = @_;
32 5         23 my $chk_rx = qr{
33 5 50       42 return $chunk unless $chunk =~ $chk_rx;
34 5         18 my $rem_rx = qr{.*?}; #"{}
35 5         15 my $cmt_rx = qr{}; #"{}
36 5         18 my $inc_rx = qr{(]+?src="([^"]+)"[^>]*/>)}; #"{}
37 5         23 $chunk =~ s/$rem_rx//gs;
38 5         20 $chunk =~ s/$cmt_rx/$1/gs;
39 5         37 while ($chunk =~ $inc_rx) {
40 4         13 my $esi = $1;
41 4         8 my $url = $2;
42 4         16 my $content = $self->_get_content($url, $r);
43 4         149 $chunk =~ s/\Q$esi\E/$content/g;
44             }
45 5         43 return $chunk;
46             }
47              
48             sub _expand_url {
49 3     3   6 my ($self, $url, $r) = @_;
50 3         15 my $prefix = $r->scheme . '://localhost:' . $r->port;
51 3 50       51 if ($url =~ m{^/}) {
52 3         12 return $prefix . $url;
53             }
54             else {
55 0         0 my $path = $r->path;
56 0 0       0 $path .= '/' unless $path =~ /\/$/;
57 0         0 return $prefix . $path . $url;
58             }
59             }
60              
61             sub _get_content {
62 4     4   6 my ($self, $url, $r) = @_;
63 4         7 my $expanded_url = $url;
64 4 100       16 unless ($url =~ m{^https?://}) {
65 3         31 $expanded_url = $self->_expand_url($url, $r);
66             }
67 4         7 my $content = '';
68 4         8 eval {
69 4 100       15 if ($url ne $expanded_url) { # internal request
70 3         42 my $httpreq = HTTP::Request->new(GET=>$url);
71 3 50       301 $httpreq->uri->scheme('http') unless defined $httpreq->uri->scheme;
72 3 50       278 $httpreq->uri->host('localhost') unless defined $httpreq->uri->host;
73 3         295 my $reqenv = $httpreq->to_psgi;
74 3         2020 my $resp = HTTP::Response->from_psgi($self->app->($reqenv));
75 3         1437 $resp->request($httpreq);
76 3 100       36 $content = $resp->content if $resp->code == 200;
77             }
78 4 100       100 unless ($content) { # external request (or retrying an apparently internal req)
79 2         8 my $resp = $self->_ua->get($expanded_url);
80 2 100       468898 if ($resp->code == 200) {
    50          
81 1         27 $content = $resp->content;
82             }
83             elsif ($r->logger) {
84 0         0 $r->logger->({level=>'warning', message=>"ESI: URL $expanded_url returned non-OK status code " . $resp->code});
85             }
86             }
87             };
88 4 50       161 if ($@) {
89 0 0       0 if ($r->logger) {
90 0         0 $r->logger->({level=>'warning', message=>"ESI: ERROR while fetching $url: $@"});
91             }
92             }
93 4         16 return $content;
94             }
95              
96             sub _ua {
97 2     2   4 my $self = shift;
98 2 100       14 return $self->{_ua} if $self->{_ua};
99 1         11 my $ua = LWP::UserAgent->new;
100 1         17461 $ua->timeout(10);
101 1         22 $self->{_ua} = $ua;
102 1         7 return $ua;
103             }
104              
105              
106             1;
107              
108             __END__