| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
2
|
|
|
2
|
|
153302
|
use strict; |
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
121
|
|
|
2
|
|
|
|
|
|
|
package Plack::Middleware::Cache::CHI; |
|
3
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:PNU'; |
|
4
|
|
|
|
|
|
|
# ABSTRACT: Caching Reverse Proxy for Plack |
|
5
|
|
|
|
|
|
|
|
|
6
|
2
|
|
|
2
|
|
12
|
use warnings; |
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
74
|
|
|
7
|
2
|
|
|
2
|
|
664
|
use parent qw/Plack::Middleware/; |
|
|
2
|
|
|
|
|
311
|
|
|
|
2
|
|
|
|
|
16
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
2
|
|
|
2
|
|
16453
|
use Plack::Util::Accessor qw( chi rules scrub cachequeries trace ); |
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
15
|
|
|
10
|
2
|
|
|
2
|
|
1087
|
use Data::Dumper; |
|
|
2
|
|
|
|
|
6973
|
|
|
|
2
|
|
|
|
|
137
|
|
|
11
|
2
|
|
|
2
|
|
1692
|
use Plack::Request; |
|
|
2
|
|
|
|
|
2382933
|
|
|
|
2
|
|
|
|
|
63
|
|
|
12
|
2
|
|
|
2
|
|
1278
|
use Plack::Response; |
|
|
2
|
|
|
|
|
5817
|
|
|
|
2
|
|
|
|
|
54
|
|
|
13
|
2
|
|
|
2
|
|
814
|
use Time::HiRes qw( gettimeofday ); |
|
|
2
|
|
|
|
|
1443
|
|
|
|
2
|
|
|
|
|
16
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our $VERSION = '0.102'; # VERSION |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
our @trace; |
|
18
|
|
|
|
|
|
|
our $timer_call; |
|
19
|
|
|
|
|
|
|
our $timer_pass; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub _uinterval { |
|
22
|
8
|
|
|
8
|
|
25
|
my ( $t0, $t1 ) = ( @_, [gettimeofday] ); |
|
23
|
8
|
|
|
|
|
29
|
($t1->[0] - $t0->[0]) * 1_000_000 + $t1->[1] - $t0->[1]; |
|
24
|
|
|
|
|
|
|
} |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub call { |
|
27
|
5
|
|
|
5
|
1
|
43764
|
my ($self,$env) = @_; |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
## Pass-thru streaming responses |
|
30
|
5
|
50
|
|
|
|
21
|
return $self->app->($env) |
|
31
|
|
|
|
|
|
|
if ( ref $env eq 'CODE' ); |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
## Localize trace for this request |
|
34
|
5
|
|
|
|
|
12
|
local @trace = (); |
|
35
|
5
|
|
|
|
|
7
|
local $timer_pass = undef; |
|
36
|
5
|
|
|
|
|
24
|
local $timer_call = [gettimeofday]; |
|
37
|
|
|
|
|
|
|
|
|
38
|
5
|
|
|
|
|
32
|
my $req = Plack::Request->new($env); |
|
39
|
5
|
|
|
|
|
47
|
my $r = $self->handle($req); |
|
40
|
5
|
|
|
|
|
36
|
my $res = Plack::Response->new(@$r); |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
## Add trace and cache key to response headers |
|
43
|
5
|
|
|
|
|
428
|
$timer_call = _uinterval($timer_call); |
|
44
|
5
|
|
|
|
|
17
|
my $trace = join q{, }, @trace; |
|
45
|
5
|
|
|
|
|
15
|
my $key = $self->cachekey($req); |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
## The subrequest is timed separately |
|
48
|
5
|
100
|
|
|
|
29
|
if ( $timer_pass ) { |
|
49
|
3
|
|
|
|
|
4
|
$timer_call -= $timer_pass; |
|
50
|
3
|
|
|
|
|
9
|
$res->headers->push_header( |
|
51
|
|
|
|
|
|
|
'X-Plack-Cache-Time-Pass' => "$timer_pass us", |
|
52
|
|
|
|
|
|
|
); |
|
53
|
|
|
|
|
|
|
} |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
$res->headers->push_header( |
|
56
|
5
|
|
|
|
|
112
|
'X-Plack-Cache' => $trace, |
|
57
|
|
|
|
|
|
|
'X-Plack-Cache-Key' => $key, |
|
58
|
|
|
|
|
|
|
'X-Plack-Cache-Time' => "$timer_call us", |
|
59
|
|
|
|
|
|
|
); |
|
60
|
|
|
|
|
|
|
|
|
61
|
5
|
|
|
|
|
324
|
$res->finalize; |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub handle { |
|
65
|
5
|
|
|
5
|
0
|
6
|
my ($self,$req) = @_; |
|
66
|
|
|
|
|
|
|
|
|
67
|
5
|
50
|
33
|
|
|
18
|
if ( $req->method eq 'GET' or $req->method eq 'HEAD' ) { |
|
68
|
5
|
50
|
|
|
|
54
|
if ( $req->headers->header('Expect') ) { |
|
69
|
0
|
|
|
|
|
0
|
push @trace, 'expect'; |
|
70
|
0
|
|
|
|
|
0
|
$self->pass($req); |
|
71
|
|
|
|
|
|
|
} else { |
|
72
|
5
|
|
|
|
|
638
|
$self->lookup($req); |
|
73
|
|
|
|
|
|
|
} |
|
74
|
|
|
|
|
|
|
} else { |
|
75
|
0
|
|
|
|
|
0
|
$self->invalidate($req); |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
sub pass { |
|
80
|
3
|
|
|
3
|
0
|
4
|
my ($self,$req) = @_; |
|
81
|
3
|
|
|
|
|
7
|
push @trace, 'pass'; |
|
82
|
3
|
|
|
|
|
14
|
$timer_pass = [gettimeofday]; |
|
83
|
|
|
|
|
|
|
|
|
84
|
3
|
|
|
|
|
10
|
my $res = $self->app->($req->env); |
|
85
|
|
|
|
|
|
|
|
|
86
|
3
|
|
|
|
|
691
|
$timer_pass = _uinterval($timer_pass); |
|
87
|
3
|
|
|
|
|
8
|
return $res; |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub invalidate { |
|
91
|
0
|
|
|
0
|
0
|
0
|
my ($self,$req) = @_; |
|
92
|
0
|
|
|
|
|
0
|
push @trace, 'invalidate'; |
|
93
|
0
|
|
|
|
|
0
|
$self->chi->remove( $self->cachekey($req) ); |
|
94
|
0
|
|
|
|
|
0
|
$self->pass($req); |
|
95
|
|
|
|
|
|
|
} |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub match { |
|
98
|
5
|
|
|
5
|
0
|
7
|
my ($self, $req) = @_; |
|
99
|
|
|
|
|
|
|
|
|
100
|
5
|
|
|
|
|
6
|
my $path; |
|
101
|
|
|
|
|
|
|
my $opts; |
|
102
|
|
|
|
|
|
|
|
|
103
|
5
|
50
|
|
|
|
6
|
my @rules = @{ $self->rules || [] }; |
|
|
5
|
|
|
|
|
19
|
|
|
104
|
5
|
|
100
|
|
|
112
|
while ( @rules || return ) { |
|
105
|
10
|
|
|
|
|
16
|
my $match = shift @rules; |
|
106
|
10
|
|
|
|
|
15
|
$opts = shift @rules; |
|
107
|
10
|
|
|
|
|
27
|
$path = $req->path_info; |
|
108
|
10
|
50
|
|
|
|
110
|
last if 'CODE' eq ref $match ? $match->($path) : $path =~ $match; |
|
|
|
100
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
} |
|
110
|
4
|
|
|
|
|
8
|
return $opts; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub lookup { |
|
114
|
5
|
|
|
5
|
0
|
9
|
my ($self, $req) = @_; |
|
115
|
5
|
|
|
|
|
15
|
push @trace, 'lookup'; |
|
116
|
|
|
|
|
|
|
|
|
117
|
5
|
|
|
|
|
13
|
my $opts = $self->match($req); |
|
118
|
|
|
|
|
|
|
|
|
119
|
5
|
100
|
|
|
|
17
|
return $self->pass($req) |
|
120
|
|
|
|
|
|
|
if not defined $opts; |
|
121
|
|
|
|
|
|
|
|
|
122
|
4
|
50
|
33
|
|
|
16
|
return $self->invalidate($req) |
|
123
|
|
|
|
|
|
|
if ( $req->param and not $self->cachequeries ); |
|
124
|
|
|
|
|
|
|
|
|
125
|
4
|
|
|
|
|
708
|
my $entry = $self->fetch( $req ); |
|
126
|
4
|
|
|
|
|
508
|
my $res = [ 500, ['Content-Type','text/plain'], ['ISE'] ]; |
|
127
|
|
|
|
|
|
|
|
|
128
|
4
|
100
|
|
|
|
11
|
if ( defined $entry ) { |
|
129
|
2
|
|
|
|
|
4
|
push @trace, 'hit'; |
|
130
|
2
|
|
|
|
|
4
|
$res = $entry->[1]; |
|
131
|
2
|
50
|
|
|
|
7
|
return $self->invalidate($req) |
|
132
|
|
|
|
|
|
|
if not $self->valid($req,$res); |
|
133
|
|
|
|
|
|
|
} else { |
|
134
|
2
|
|
|
|
|
5
|
push @trace, 'miss'; |
|
135
|
2
|
|
|
|
|
6
|
$res = $self->delegate($req); |
|
136
|
2
|
50
|
|
|
|
9
|
$self->store($req,$res,$opts) |
|
137
|
|
|
|
|
|
|
if $self->valid($req,$res); |
|
138
|
|
|
|
|
|
|
} |
|
139
|
4
|
|
|
|
|
1269
|
return $res; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub valid { |
|
143
|
4
|
|
|
4
|
0
|
7
|
my ($self, $req, $res) = @_; |
|
144
|
|
|
|
|
|
|
|
|
145
|
4
|
|
|
|
|
7
|
my $res_status = $res->[0]; |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
return |
|
148
|
|
|
|
|
|
|
unless ( |
|
149
|
4
|
50
|
66
|
|
|
44
|
$res_status == 200 or |
|
|
|
|
66
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
150
|
|
|
|
|
|
|
$res_status == 203 or |
|
151
|
|
|
|
|
|
|
$res_status == 300 or |
|
152
|
|
|
|
|
|
|
$res_status == 301 or |
|
153
|
|
|
|
|
|
|
$res_status == 302 or |
|
154
|
|
|
|
|
|
|
$res_status == 404 or |
|
155
|
|
|
|
|
|
|
$res_status == 410 |
|
156
|
|
|
|
|
|
|
); |
|
157
|
|
|
|
|
|
|
|
|
158
|
4
|
|
|
|
|
17
|
return 1; |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub cachekey { |
|
162
|
11
|
|
|
11
|
0
|
13
|
my ($self, $req) = @_; |
|
163
|
|
|
|
|
|
|
|
|
164
|
11
|
|
|
|
|
29
|
my $uri = $req->uri->canonical; |
|
165
|
|
|
|
|
|
|
|
|
166
|
11
|
50
|
|
|
|
2757
|
$uri->query(undef) |
|
167
|
|
|
|
|
|
|
if not $self->cachequeries; |
|
168
|
|
|
|
|
|
|
|
|
169
|
11
|
|
|
|
|
74
|
$uri->as_string; |
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub fetch { |
|
173
|
4
|
|
|
4
|
0
|
8
|
my ($self, $req) = @_; |
|
174
|
4
|
|
|
|
|
8
|
push @trace, 'fetch'; |
|
175
|
|
|
|
|
|
|
|
|
176
|
4
|
|
|
|
|
9
|
my $key = $self->cachekey($req); |
|
177
|
4
|
|
|
|
|
24
|
$self->chi->get( $key ); |
|
178
|
|
|
|
|
|
|
} |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub store { |
|
181
|
2
|
|
|
2
|
0
|
3
|
my ($self, $req, $res, $opts) = @_; |
|
182
|
2
|
|
|
|
|
5
|
push @trace, 'store'; |
|
183
|
|
|
|
|
|
|
|
|
184
|
2
|
|
|
|
|
5
|
my $key = $self->cachekey($req); |
|
185
|
2
|
|
|
|
|
12
|
$self->chi->set( $key, [$req->headers,$res], $opts ); |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
sub delegate { |
|
189
|
2
|
|
|
2
|
0
|
4
|
my ($self, $req, $opts) = @_; |
|
190
|
2
|
|
|
|
|
3
|
push @trace, 'delegate'; |
|
191
|
|
|
|
|
|
|
|
|
192
|
2
|
|
|
|
|
7
|
my $res = $self->pass($req); |
|
193
|
2
|
50
|
|
|
|
4
|
foreach ( @{ $self->scrub || [] } ) { |
|
|
2
|
|
|
|
|
7
|
|
|
194
|
2
|
|
|
|
|
22
|
Plack::Util::header_remove( $res->[1], $_ ); |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
2
|
|
|
|
|
43
|
my $body; |
|
198
|
|
|
|
|
|
|
Plack::Util::foreach( $res->[2], sub { |
|
199
|
2
|
50
|
|
2
|
|
135
|
$body .= $_[0] if $_[0]; |
|
200
|
2
|
|
|
|
|
13
|
}); |
|
201
|
|
|
|
|
|
|
|
|
202
|
2
|
|
|
|
|
64
|
return [ $res->[0], $res->[1], [$body] ]; |
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
1; |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
__END__ |