| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Test::Override::UserAgent; |
|
2
|
|
|
|
|
|
|
|
|
3
|
18
|
|
|
18
|
|
1530990
|
use 5.008001; |
|
|
18
|
|
|
|
|
79
|
|
|
|
18
|
|
|
|
|
785
|
|
|
4
|
18
|
|
|
18
|
|
115
|
use strict; |
|
|
18
|
|
|
|
|
41
|
|
|
|
18
|
|
|
|
|
666
|
|
|
5
|
18
|
|
|
18
|
|
148
|
use warnings 'all'; |
|
|
18
|
|
|
|
|
44
|
|
|
|
18
|
|
|
|
|
1505
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
########################################################################### |
|
8
|
|
|
|
|
|
|
# METADATA |
|
9
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:DOUGDUDE'; |
|
10
|
|
|
|
|
|
|
our $VERSION = '0.004001'; |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
########################################################################### |
|
13
|
|
|
|
|
|
|
# MODULE IMPORTS |
|
14
|
18
|
|
|
18
|
|
107
|
use Carp qw(croak); |
|
|
18
|
|
|
|
|
63
|
|
|
|
18
|
|
|
|
|
1352
|
|
|
15
|
18
|
|
|
18
|
|
19567
|
use Clone; |
|
|
18
|
|
|
|
|
60928
|
|
|
|
18
|
|
|
|
|
1067
|
|
|
16
|
18
|
|
|
18
|
|
16130
|
use HTTP::Config 5.815; |
|
|
18
|
|
|
|
|
72784
|
|
|
|
18
|
|
|
|
|
544
|
|
|
17
|
18
|
|
|
18
|
|
3349
|
use HTTP::Date (); |
|
|
18
|
|
|
|
|
14408
|
|
|
|
18
|
|
|
|
|
386
|
|
|
18
|
18
|
|
|
18
|
|
2309
|
use HTTP::Headers; |
|
|
18
|
|
|
|
|
19043
|
|
|
|
18
|
|
|
|
|
534
|
|
|
19
|
18
|
|
|
18
|
|
1958
|
use HTTP::Response; |
|
|
18
|
|
|
|
|
695529
|
|
|
|
18
|
|
|
|
|
634
|
|
|
20
|
18
|
|
|
18
|
|
297
|
use HTTP::Status 5.817 (); |
|
|
18
|
|
|
|
|
565
|
|
|
|
18
|
|
|
|
|
378
|
|
|
21
|
18
|
|
|
18
|
|
18021
|
use LWP::UserAgent; # Not actually required here, but want it to be loaded |
|
|
18
|
|
|
|
|
75573
|
|
|
|
18
|
|
|
|
|
512
|
|
|
22
|
18
|
|
|
18
|
|
141
|
use Scalar::Util; |
|
|
18
|
|
|
|
|
34
|
|
|
|
18
|
|
|
|
|
1408
|
|
|
23
|
18
|
|
|
18
|
|
17825
|
use Sub::Install 0.90; |
|
|
18
|
|
|
|
|
33051
|
|
|
|
18
|
|
|
|
|
132
|
|
|
24
|
18
|
|
|
18
|
|
13763
|
use Test::Override::UserAgent::Scope; |
|
|
18
|
|
|
|
|
68
|
|
|
|
18
|
|
|
|
|
709
|
|
|
25
|
18
|
|
|
18
|
|
201
|
use Try::Tiny; |
|
|
18
|
|
|
|
|
35
|
|
|
|
18
|
|
|
|
|
1232
|
|
|
26
|
18
|
|
|
18
|
|
110
|
use URI; |
|
|
18
|
|
|
|
|
37
|
|
|
|
18
|
|
|
|
|
694
|
|
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
########################################################################### |
|
29
|
|
|
|
|
|
|
# ALL IMPORTS BEFORE THIS WILL BE ERASED |
|
30
|
18
|
|
|
18
|
|
110
|
use namespace::clean 0.04 -except => [qw(meta)]; |
|
|
18
|
|
|
|
|
330
|
|
|
|
18
|
|
|
|
|
156
|
|
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
########################################################################### |
|
33
|
|
|
|
|
|
|
# METHODS |
|
34
|
|
|
|
|
|
|
sub allow_live_requests { |
|
35
|
26
|
|
|
26
|
1
|
6481
|
my ($self, $new_value) = @_; |
|
36
|
|
|
|
|
|
|
|
|
37
|
26
|
100
|
|
|
|
103
|
if (defined $new_value) { |
|
38
|
|
|
|
|
|
|
# Set the new value |
|
39
|
6
|
|
|
|
|
16
|
$self->{allow_live_requests} = $new_value; |
|
40
|
|
|
|
|
|
|
} |
|
41
|
|
|
|
|
|
|
|
|
42
|
26
|
|
|
|
|
128
|
return $self->{allow_live_requests}; |
|
43
|
|
|
|
|
|
|
} |
|
44
|
|
|
|
|
|
|
sub handle_request { |
|
45
|
60
|
|
|
60
|
1
|
11299
|
my ($self, $request, %args) = @_; |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# Lookup the handler for the request |
|
48
|
60
|
|
|
|
|
234
|
my $handler = $self->_get_handler_for($request); |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# Hold the response |
|
51
|
60
|
|
|
|
|
119
|
my $response; |
|
52
|
|
|
|
|
|
|
|
|
53
|
60
|
100
|
|
|
|
233
|
if (defined $handler) { |
|
54
|
|
|
|
|
|
|
# Get the response |
|
55
|
44
|
|
|
|
|
197
|
$response = _convert_psgi_response($handler->($request)); |
|
56
|
|
|
|
|
|
|
|
|
57
|
44
|
100
|
|
|
|
262
|
if (!defined $response->request) { |
|
58
|
|
|
|
|
|
|
# Set the request that made this response |
|
59
|
43
|
|
|
|
|
667
|
$response->request($request); |
|
60
|
|
|
|
|
|
|
} |
|
61
|
|
|
|
|
|
|
} |
|
62
|
|
|
|
|
|
|
|
|
63
|
60
|
100
|
100
|
|
|
846
|
if (!defined $response && exists $args{live_request_handler}) { |
|
64
|
|
|
|
|
|
|
# There was no handler/response and a live requestor was provided |
|
65
|
15
|
100
|
|
|
|
139
|
if ($self->allow_live_requests) { |
|
66
|
|
|
|
|
|
|
# Make the live request |
|
67
|
2
|
|
|
|
|
10
|
$response = $args{live_request_handler}->($request); |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
else { |
|
70
|
|
|
|
|
|
|
# Make an internal response for not successful since no |
|
71
|
|
|
|
|
|
|
# live requests are allowed. |
|
72
|
13
|
|
|
|
|
143
|
$response = _new_internal_response( |
|
73
|
|
|
|
|
|
|
HTTP::Status::HTTP_NOT_FOUND, |
|
74
|
|
|
|
|
|
|
'Not Found (No Live Requests)', |
|
75
|
|
|
|
|
|
|
); |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
|
|
79
|
60
|
|
|
|
|
267
|
return $response; |
|
80
|
|
|
|
|
|
|
} |
|
81
|
|
|
|
|
|
|
sub install_in_scope { |
|
82
|
2
|
|
|
2
|
1
|
5731
|
my ($self) = @_; |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Return the scope variable |
|
85
|
2
|
|
|
|
|
33
|
return Test::Override::UserAgent::Scope->new( |
|
86
|
|
|
|
|
|
|
override => $self, |
|
87
|
|
|
|
|
|
|
); |
|
88
|
|
|
|
|
|
|
} |
|
89
|
|
|
|
|
|
|
sub install_in_user_agent { |
|
90
|
15
|
|
|
15
|
1
|
70419
|
my ($self, $user_agent, %args) = @_; |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# Get the clone argument |
|
93
|
15
|
100
|
|
|
|
79
|
my $clone = exists $args{clone} ? $args{clone} : 0; |
|
94
|
|
|
|
|
|
|
|
|
95
|
15
|
100
|
|
|
|
78
|
if ($clone) { |
|
96
|
|
|
|
|
|
|
# Make a clone of the user agent |
|
97
|
1
|
|
|
|
|
7
|
$user_agent = $user_agent->clone; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# Add as a handler in the user agent |
|
101
|
|
|
|
|
|
|
$user_agent->add_handler( |
|
102
|
|
|
|
|
|
|
request_send => sub { |
|
103
|
|
|
|
|
|
|
# Get the response |
|
104
|
|
|
|
|
|
|
my $response = $self->handle_request( |
|
105
|
|
|
|
|
|
|
shift, |
|
106
|
1
|
|
|
|
|
3
|
live_request_handler => sub { return; }, |
|
107
|
51
|
|
|
51
|
|
2704834
|
); |
|
108
|
|
|
|
|
|
|
|
|
109
|
51
|
|
|
|
|
251
|
return $response; |
|
110
|
|
|
|
|
|
|
}, |
|
111
|
15
|
|
|
|
|
500
|
owner => Scalar::Util::refaddr($self), |
|
112
|
|
|
|
|
|
|
); |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# Return the user agent |
|
115
|
15
|
|
|
|
|
830
|
return $user_agent; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
sub override_request { |
|
118
|
45
|
|
|
45
|
1
|
3867
|
my ($self, @args) = @_; |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Get the handler from the end |
|
121
|
45
|
|
|
|
|
78
|
my $handler = pop @args; |
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# Convert the arguments into a hash |
|
124
|
45
|
|
|
|
|
141
|
my %args = @args; |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# Register the handler |
|
127
|
45
|
|
|
|
|
166
|
$self->_register_handler($handler, %args); |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# Enable chaining |
|
130
|
45
|
|
|
|
|
187
|
return $self; |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
sub uninstall_from_user_agent { |
|
133
|
2
|
|
|
2
|
1
|
54200
|
my ($self, $user_agent) = @_; |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# Remove our handlers from the user agent |
|
136
|
2
|
|
|
|
|
27
|
$user_agent->remove_handler( |
|
137
|
|
|
|
|
|
|
'request_send', |
|
138
|
|
|
|
|
|
|
owner => Scalar::Util::refaddr($self), |
|
139
|
|
|
|
|
|
|
); |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# Return the user agent for some reason |
|
142
|
2
|
|
|
|
|
430
|
return $user_agent; |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
########################################################################### |
|
146
|
|
|
|
|
|
|
# STATIC METHODS |
|
147
|
|
|
|
|
|
|
sub import { |
|
148
|
19
|
|
|
19
|
|
1620
|
my ($class, %args) = @_; |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# What this module is being used for |
|
151
|
19
|
|
100
|
|
|
133
|
my $use_for = $args{for} || 'testing'; |
|
152
|
|
|
|
|
|
|
|
|
153
|
19
|
100
|
|
|
|
83
|
if ($use_for eq 'configuration') { |
|
154
|
|
|
|
|
|
|
# Get the calling package |
|
155
|
4
|
|
|
|
|
12
|
my $caller = caller; |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# Create a new configuration object that will be wrapped in |
|
158
|
|
|
|
|
|
|
# closures. |
|
159
|
4
|
|
|
|
|
36
|
my $conf = $class->new; |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# Create a defaults hash for colsures |
|
162
|
4
|
|
|
|
|
9
|
my $defaults = {}; |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# Install override_request |
|
165
|
|
|
|
|
|
|
Sub::Install::install_sub({ |
|
166
|
12
|
|
|
12
|
|
62
|
code => sub { return $conf->override_request(%{$defaults}, @_); }, |
|
|
12
|
|
|
|
|
41
|
|
|
167
|
4
|
|
|
|
|
40
|
into => $caller, |
|
168
|
|
|
|
|
|
|
as => 'override_request', |
|
169
|
|
|
|
|
|
|
}); |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# Install override_for |
|
172
|
|
|
|
|
|
|
Sub::Install::install_sub({ |
|
173
|
|
|
|
|
|
|
code => sub { |
|
174
|
3
|
|
|
3
|
|
19
|
my $block = pop; |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# Rember the current defaults |
|
177
|
3
|
|
|
|
|
5
|
my $previous_defaults = $defaults; |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
# Set the new defaults as an extension of the current |
|
180
|
3
|
|
|
|
|
4
|
$defaults = {%{Clone::clone($defaults)}, @_}; |
|
|
3
|
|
|
|
|
35
|
|
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# Run the block with the defaults in effect |
|
183
|
3
|
|
|
|
|
11
|
$block->(); |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# Restore the defaults |
|
186
|
3
|
|
|
|
|
8
|
$defaults = $previous_defaults; |
|
187
|
|
|
|
|
|
|
}, |
|
188
|
4
|
|
|
|
|
305
|
into => $caller, |
|
189
|
|
|
|
|
|
|
as => 'override_for', |
|
190
|
|
|
|
|
|
|
}); |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
# Install allow_live |
|
193
|
|
|
|
|
|
|
Sub::Install::install_sub({ |
|
194
|
|
|
|
|
|
|
code => sub { |
|
195
|
4
|
|
|
4
|
|
17
|
my $allow = shift; |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# Set the allow live requests (no arguments defaults to 1) |
|
198
|
4
|
100
|
|
|
|
21
|
$conf->allow_live_requests(defined $allow ? $allow : 1); |
|
199
|
|
|
|
|
|
|
}, |
|
200
|
4
|
|
|
|
|
208
|
into => $caller, |
|
201
|
|
|
|
|
|
|
as => 'allow_live', |
|
202
|
|
|
|
|
|
|
}); |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# Install custom configuration which retuns the config object |
|
205
|
|
|
|
|
|
|
Sub::Install::install_sub({ |
|
206
|
5
|
|
|
5
|
|
2361
|
code => sub { return $conf; }, |
|
207
|
4
|
|
|
|
|
218
|
into => $caller, |
|
208
|
|
|
|
|
|
|
as => 'configuration', |
|
209
|
|
|
|
|
|
|
}); |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
19
|
|
|
|
|
35073
|
return; |
|
213
|
|
|
|
|
|
|
} |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
########################################################################### |
|
216
|
|
|
|
|
|
|
# CONSTRUCTOR |
|
217
|
|
|
|
|
|
|
sub new { |
|
218
|
20
|
|
|
20
|
1
|
1844
|
my ($class, @args) = @_; |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# Get the arguments as a plain hash |
|
221
|
20
|
100
|
|
|
|
126
|
my %args = @args == 1 ? %{shift @args} |
|
|
1
|
|
|
|
|
4
|
|
|
222
|
|
|
|
|
|
|
: @args |
|
223
|
|
|
|
|
|
|
; |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# Create a hash with configuration information |
|
226
|
20
|
|
|
|
|
274
|
my %data = ( |
|
227
|
|
|
|
|
|
|
# Attributes |
|
228
|
|
|
|
|
|
|
allow_live_requests => 0, |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# Private attributes |
|
231
|
|
|
|
|
|
|
_lookup_table => HTTP::Config->new, |
|
232
|
|
|
|
|
|
|
_protocol_classes => {}, |
|
233
|
|
|
|
|
|
|
); |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# Set attributes |
|
236
|
20
|
|
|
|
|
311
|
foreach my $arg (grep { m{\A [^_]}msx } keys %data) { |
|
|
60
|
|
|
|
|
255
|
|
|
237
|
20
|
100
|
|
|
|
189
|
if (exists $args{$arg}) { |
|
238
|
2
|
|
|
|
|
7
|
$data{$arg} = $args{$arg}; |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# Bless the hash to this class |
|
243
|
20
|
|
|
|
|
88
|
my $self = bless \%data, $class; |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# Set our unique name |
|
246
|
20
|
|
|
|
|
253
|
$self->{_uniq_name} = $class . '::Number' . Scalar::Util::refaddr($self); |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# Return our blessed configuration |
|
249
|
20
|
|
|
|
|
148
|
return $self; |
|
250
|
|
|
|
|
|
|
} |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
########################################################################### |
|
253
|
|
|
|
|
|
|
# PRIVATE METHODS |
|
254
|
|
|
|
|
|
|
sub _get_handler_for { |
|
255
|
60
|
|
|
60
|
|
150
|
my ($self, $request) = @_; |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# Get the handler |
|
258
|
60
|
|
|
|
|
354
|
my @handlers = $self->{_lookup_table}->matching_items($request); |
|
259
|
|
|
|
|
|
|
|
|
260
|
60
|
|
|
|
|
19475
|
return $handlers[0]; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
sub _register_handler { |
|
263
|
45
|
|
|
45
|
|
122
|
my ($self, $handler, %args) = @_; |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# Add m_ to the beginning of the arguments |
|
266
|
45
|
|
|
|
|
122
|
for my $key (keys %args) { |
|
267
|
|
|
|
|
|
|
# Specially handle "url" key as HTTP::Config does not |
|
268
|
76
|
100
|
100
|
|
|
781
|
if ($key eq 'url' || $key eq 'uri') { |
|
|
|
50
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# Get the URI from the arguments |
|
270
|
3
|
|
|
|
|
21
|
my $uri = URI->new(delete $args{$key}); |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# Set a match against it's canonical value |
|
273
|
3
|
|
|
|
|
12184
|
$args{m_uri__canonical} = $uri->canonical; |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
elsif (q{m_} ne substr $key, 0, 2) { |
|
276
|
|
|
|
|
|
|
# Add m_ |
|
277
|
73
|
|
|
|
|
351
|
$args{"m_$key"} = delete $args{$key}; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# Set the handler |
|
282
|
45
|
|
|
|
|
1094
|
$self->{_lookup_table}->add_item($handler, %args); |
|
283
|
|
|
|
|
|
|
|
|
284
|
45
|
|
|
|
|
645
|
return; |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
########################################################################### |
|
288
|
|
|
|
|
|
|
# PRIVATE FUNCTIONS |
|
289
|
|
|
|
|
|
|
sub _convert_psgi_response { |
|
290
|
44
|
|
|
44
|
|
528
|
my ($response) = @_; |
|
291
|
|
|
|
|
|
|
|
|
292
|
44
|
100
|
|
|
|
255
|
if (!defined Scalar::Util::blessed($response)) { |
|
293
|
|
|
|
|
|
|
# Get the type of the response |
|
294
|
43
|
|
|
|
|
178
|
my $response_type = Scalar::Util::reftype($response); |
|
295
|
|
|
|
|
|
|
|
|
296
|
43
|
100
|
100
|
|
|
409
|
if (defined $response_type && $response_type eq 'ARRAY') { |
|
297
|
|
|
|
|
|
|
# This is a PSGI-formatted response |
|
298
|
|
|
|
|
|
|
try { |
|
299
|
|
|
|
|
|
|
# Validate the response |
|
300
|
41
|
|
|
41
|
|
1732
|
_validate_psgi_response($response); |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
# Unwrap the PSGI response |
|
303
|
32
|
|
|
|
|
49
|
my ($status_code, $headers, $body) = @{$response}; |
|
|
32
|
|
|
|
|
81
|
|
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# Change the headers to a header object |
|
306
|
32
|
|
|
|
|
61
|
$headers = HTTP::Headers->new(@{$headers}); |
|
|
32
|
|
|
|
|
225
|
|
|
307
|
|
|
|
|
|
|
|
|
308
|
32
|
100
|
|
|
|
2873
|
if (ref $body ne 'ARRAY') { |
|
309
|
|
|
|
|
|
|
# The body is a filehandle |
|
310
|
1
|
|
|
|
|
2
|
my $fh = $body; |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
# Change the body to an array reference |
|
313
|
1
|
|
|
|
|
4
|
$body = []; |
|
314
|
|
|
|
|
|
|
|
|
315
|
1
|
|
|
|
|
8
|
while (defined(my $line = $fh->getline)) { |
|
316
|
|
|
|
|
|
|
# Push the line into the body |
|
317
|
2
|
|
|
|
|
52
|
push @{$body}, $line; |
|
|
2
|
|
|
|
|
10
|
|
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# Close the file |
|
321
|
1
|
|
|
|
|
14
|
$fh->close; |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# Create the response object |
|
325
|
32
|
|
|
|
|
257
|
$response = HTTP::Response->new( |
|
326
|
32
|
|
|
|
|
91
|
$status_code, undef, $headers, join q{}, @{$body}); |
|
327
|
|
|
|
|
|
|
} |
|
328
|
|
|
|
|
|
|
catch { |
|
329
|
|
|
|
|
|
|
# Invalid PSGI response |
|
330
|
9
|
|
|
9
|
|
587
|
my $error = "$_"; # stringify error |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
# Remove line information from croak |
|
333
|
9
|
|
|
|
|
59
|
$error =~ s{\s at \s .+ \z}{}msx; |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# Set the response |
|
336
|
9
|
|
|
|
|
32
|
$response = _new_internal_response( |
|
337
|
|
|
|
|
|
|
HTTP::Status::HTTP_EXPECTATION_FAILED, |
|
338
|
|
|
|
|
|
|
$error, |
|
339
|
|
|
|
|
|
|
); |
|
340
|
41
|
|
|
|
|
531
|
}; |
|
341
|
|
|
|
|
|
|
} |
|
342
|
|
|
|
|
|
|
else { |
|
343
|
|
|
|
|
|
|
# Bad return value from handler |
|
344
|
2
|
|
|
|
|
12
|
$response = _new_internal_response( |
|
345
|
|
|
|
|
|
|
HTTP::Status::HTTP_EXPECTATION_FAILED, |
|
346
|
|
|
|
|
|
|
'Override handler returned invalid value: ' . $response |
|
347
|
|
|
|
|
|
|
); |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
|
|
351
|
44
|
|
|
|
|
6986
|
return $response; |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
sub _is_invalid_psgi_header_key { |
|
354
|
59
|
|
|
59
|
|
94
|
my ($key) = @_; |
|
355
|
|
|
|
|
|
|
|
|
356
|
59
|
|
100
|
|
|
890
|
return $key =~ m{(?:\A status \z | [:\n] | [_-] \z)}imsx |
|
357
|
|
|
|
|
|
|
|| $key !~ m{\A [a-z] [a-z0-9_-]* \z}imsx; |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
sub _is_invalid_psgi_header_value { |
|
360
|
59
|
|
|
59
|
|
106
|
my ($value) = @_; |
|
361
|
|
|
|
|
|
|
|
|
362
|
59
|
|
100
|
|
|
3223
|
return ref $value ne q{} || $value =~ m{[\x00-\x19\x21-\x25]}imsx; |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
sub _new_internal_response { |
|
365
|
24
|
|
|
24
|
|
54
|
my ($code, $message) = @_; |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
# Make a new response |
|
368
|
24
|
|
|
|
|
212
|
my $response = HTTP::Response->new($code, $message); |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# Set some headers for client information |
|
371
|
24
|
|
|
|
|
1326
|
$response->header( |
|
372
|
|
|
|
|
|
|
'Client-Date' => HTTP::Date::time2str(time), |
|
373
|
|
|
|
|
|
|
'Client-Response-Source' => __PACKAGE__, |
|
374
|
|
|
|
|
|
|
'Client-Warning' => 'Internal response', |
|
375
|
|
|
|
|
|
|
'Content-Type' => 'text/plain', |
|
376
|
|
|
|
|
|
|
); |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# Set the content as the status_line |
|
379
|
24
|
|
|
|
|
5130
|
$response->content("$code $message"); |
|
380
|
|
|
|
|
|
|
|
|
381
|
24
|
|
|
|
|
587
|
return $response; |
|
382
|
|
|
|
|
|
|
} |
|
383
|
|
|
|
|
|
|
sub _validate_psgi_response { |
|
384
|
41
|
|
|
41
|
|
87
|
my ($psgi) = @_; |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# Unwrap the response |
|
387
|
41
|
|
|
|
|
78
|
my ($code, $headers, $body) = @{$psgi}; |
|
|
41
|
|
|
|
|
103
|
|
|
388
|
|
|
|
|
|
|
|
|
389
|
41
|
100
|
|
|
|
261
|
if ($code !~ m{\A [1-9] \d{2,} \z}msx) { |
|
390
|
1
|
|
|
|
|
193
|
croak 'PSGI HTTP status code MUST be 100 or greater'; |
|
391
|
|
|
|
|
|
|
} |
|
392
|
|
|
|
|
|
|
|
|
393
|
40
|
100
|
|
|
|
164
|
if (ref $headers ne 'ARRAY') { |
|
394
|
1
|
|
|
|
|
167
|
croak 'PSGI headers MUST be an array reference'; |
|
395
|
|
|
|
|
|
|
} |
|
396
|
|
|
|
|
|
|
|
|
397
|
39
|
100
|
|
|
|
83
|
if (@{$headers} % 2 != 0) { |
|
|
39
|
|
|
|
|
180
|
|
|
398
|
1
|
|
|
|
|
162
|
croak 'PSGI headers MUST have even number of elements'; |
|
399
|
|
|
|
|
|
|
} |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# Headers copied |
|
402
|
38
|
|
|
|
|
75
|
my @headers = @{$headers}; |
|
|
38
|
|
|
|
|
118
|
|
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# Hold invalid stuff |
|
405
|
38
|
|
|
|
|
702
|
my (@invalid_header_keys, @invalid_header_values, |
|
406
|
|
|
|
|
|
|
$has_content_type, $has_content_length); |
|
407
|
|
|
|
|
|
|
|
|
408
|
38
|
|
|
|
|
195
|
while (my ($key, $value) = splice @headers, 0, 2) { |
|
409
|
59
|
100
|
|
|
|
179
|
if (_is_invalid_psgi_header_key($key)) { |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
# Remember the invalid key |
|
411
|
3
|
|
|
|
|
8
|
push @invalid_header_keys, $key; |
|
412
|
|
|
|
|
|
|
} |
|
413
|
|
|
|
|
|
|
elsif (lc $key eq 'content-type') { |
|
414
|
|
|
|
|
|
|
# The response has a defined content type |
|
415
|
32
|
|
|
|
|
68
|
$has_content_type = 1; |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
elsif (lc $key eq 'content-length') { |
|
418
|
|
|
|
|
|
|
# The response has a defined content length |
|
419
|
2
|
|
|
|
|
4
|
$has_content_length = 1; |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
|
|
422
|
59
|
100
|
|
|
|
227
|
if (_is_invalid_psgi_header_value($value)) { |
|
423
|
|
|
|
|
|
|
# Remember the key of the invalid value |
|
424
|
2
|
|
|
|
|
10
|
push @invalid_header_values, $key; |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
|
|
428
|
38
|
100
|
|
|
|
198
|
if (@invalid_header_keys) { |
|
429
|
1
|
|
|
|
|
174
|
croak 'PSGI headers have invalid key(s): ', |
|
430
|
|
|
|
|
|
|
join q{, }, sort @invalid_header_keys; |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
|
|
433
|
37
|
100
|
|
|
|
188
|
if (@invalid_header_values) { |
|
434
|
1
|
|
|
|
|
164
|
croak 'PSGI headers have invalid value(s): ', |
|
435
|
|
|
|
|
|
|
join q{, }, sort @invalid_header_values; |
|
436
|
|
|
|
|
|
|
} |
|
437
|
|
|
|
|
|
|
|
|
438
|
36
|
100
|
66
|
|
|
171
|
if (!$has_content_type && $code !~ m{\A 1 | [23]04}msx) { |
|
439
|
3
|
|
|
|
|
513
|
croak 'There MUST be a Content-Type for code other than 1xx, 204, and 304'; |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
|
|
442
|
33
|
100
|
100
|
|
|
171
|
if ($has_content_length && $code =~ m{\A 1 | [23]04}msx) { |
|
443
|
1
|
|
|
|
|
163
|
croak 'There MUST NOT be a Content-Length for 1xx, 204, and 304'; |
|
444
|
|
|
|
|
|
|
} |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
# Return true for successful check |
|
447
|
32
|
|
|
|
|
96
|
return 1; |
|
448
|
|
|
|
|
|
|
} |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
1; |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
__END__ |