line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Dancer::Test; |
2
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:SUKRIA'; |
3
|
|
|
|
|
|
|
#ABSTRACT: Test helpers to test a Dancer application |
4
|
|
|
|
|
|
|
$Dancer::Test::VERSION = '1.3514_04'; # TRIAL |
5
|
|
|
|
|
|
|
$Dancer::Test::VERSION = '1.351404'; |
6
|
|
|
|
|
|
|
# test helpers for Dancer apps |
7
|
|
|
|
|
|
|
|
8
|
86
|
|
|
86
|
|
874957
|
use strict; |
|
86
|
|
|
|
|
269
|
|
|
86
|
|
|
|
|
2176
|
|
9
|
86
|
|
|
86
|
|
392
|
use warnings; |
|
86
|
|
|
|
|
161
|
|
|
86
|
|
|
|
|
1870
|
|
10
|
86
|
|
|
86
|
|
4044
|
use Test::Builder; |
|
86
|
|
|
|
|
335773
|
|
|
86
|
|
|
|
|
2297
|
|
11
|
86
|
|
|
86
|
|
4403
|
use Test::More import => [ '!pass' ]; |
|
86
|
|
|
|
|
36014
|
|
|
86
|
|
|
|
|
933
|
|
12
|
86
|
|
|
86
|
|
60104
|
use Test::LongString; |
|
86
|
|
|
|
|
163153
|
|
|
86
|
|
|
|
|
484
|
|
13
|
|
|
|
|
|
|
|
14
|
86
|
|
|
86
|
|
6496
|
use Carp; |
|
86
|
|
|
|
|
178
|
|
|
86
|
|
|
|
|
3575
|
|
15
|
86
|
|
|
86
|
|
4762
|
use HTTP::Headers; |
|
86
|
|
|
|
|
57142
|
|
|
86
|
|
|
|
|
1790
|
|
16
|
86
|
|
|
86
|
|
413
|
use Scalar::Util 'blessed'; |
|
86
|
|
|
|
|
184
|
|
|
86
|
|
|
|
|
3710
|
|
17
|
|
|
|
|
|
|
|
18
|
86
|
|
|
86
|
|
5876
|
use Dancer ':syntax', ':tests'; |
|
86
|
|
|
|
|
163
|
|
|
86
|
|
|
|
|
490
|
|
19
|
86
|
|
|
86
|
|
518
|
use Dancer::App; |
|
86
|
|
|
|
|
180
|
|
|
86
|
|
|
|
|
2190
|
|
20
|
86
|
|
|
86
|
|
501
|
use Dancer::Deprecation; |
|
86
|
|
|
|
|
191
|
|
|
86
|
|
|
|
|
1823
|
|
21
|
86
|
|
|
86
|
|
449
|
use Dancer::Request; |
|
86
|
|
|
|
|
163
|
|
|
86
|
|
|
|
|
1877
|
|
22
|
86
|
|
|
86
|
|
401
|
use Dancer::Request::Upload; |
|
86
|
|
|
|
|
175
|
|
|
86
|
|
|
|
|
1875
|
|
23
|
86
|
|
|
86
|
|
400
|
use Dancer::SharedData; |
|
86
|
|
|
|
|
156
|
|
|
86
|
|
|
|
|
1783
|
|
24
|
86
|
|
|
86
|
|
460
|
use Dancer::Renderer; |
|
86
|
|
|
|
|
162
|
|
|
86
|
|
|
|
|
1776
|
|
25
|
86
|
|
|
86
|
|
408
|
use Dancer::Handler; |
|
86
|
|
|
|
|
174
|
|
|
86
|
|
|
|
|
2117
|
|
26
|
86
|
|
|
86
|
|
436
|
use Dancer::Config; |
|
86
|
|
|
|
|
204
|
|
|
86
|
|
|
|
|
3264
|
|
27
|
86
|
|
|
86
|
|
505
|
use Dancer::FileUtils qw(open_file); |
|
86
|
|
|
|
|
178
|
|
|
86
|
|
|
|
|
3895
|
|
28
|
|
|
|
|
|
|
|
29
|
86
|
|
|
86
|
|
501
|
use base 'Exporter'; |
|
86
|
|
|
|
|
194
|
|
|
86
|
|
|
|
|
10190
|
|
30
|
86
|
|
|
86
|
|
550
|
use vars '@EXPORT'; |
|
86
|
|
|
|
|
183
|
|
|
86
|
|
|
|
|
209843
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
@EXPORT = qw( |
33
|
|
|
|
|
|
|
route_exists |
34
|
|
|
|
|
|
|
route_doesnt_exist |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
response_exists |
37
|
|
|
|
|
|
|
response_doesnt_exist |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
response_status_is |
40
|
|
|
|
|
|
|
response_status_isnt |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
response_content_is |
43
|
|
|
|
|
|
|
response_content_isnt |
44
|
|
|
|
|
|
|
response_content_is_deeply |
45
|
|
|
|
|
|
|
response_content_like |
46
|
|
|
|
|
|
|
response_content_unlike |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
response_is_file |
49
|
|
|
|
|
|
|
response_headers_are_deeply |
50
|
|
|
|
|
|
|
response_headers_include |
51
|
|
|
|
|
|
|
response_redirect_location_is |
52
|
|
|
|
|
|
|
response_redirect_location_like |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
dancer_response |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
read_logs |
57
|
|
|
|
|
|
|
); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub import { |
60
|
86
|
|
|
86
|
|
676
|
my ($class, %options) = @_; |
61
|
86
|
|
100
|
|
|
646
|
$options{appdir} ||= '.'; |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# mimic PSGI env |
64
|
86
|
|
|
|
|
627
|
$ENV{SERVERNAME} = 'localhost'; |
65
|
86
|
|
|
|
|
329
|
$ENV{HTTP_HOST} = 'localhost'; |
66
|
86
|
|
|
|
|
330
|
$ENV{SERVER_PORT} = 80; |
67
|
86
|
|
|
|
|
299
|
$ENV{'psgi.url_scheme'} = 'http'; |
68
|
|
|
|
|
|
|
|
69
|
86
|
|
|
|
|
372
|
my ($package, $script) = caller; |
70
|
86
|
|
|
|
|
10923
|
$class->export_to_level(1, $class, @EXPORT); |
71
|
|
|
|
|
|
|
|
72
|
86
|
|
|
|
|
511
|
Dancer::_init_script_dir($options{appdir}); |
73
|
86
|
|
|
|
|
497
|
Dancer::Config->load; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# set a default session engine for tests |
76
|
86
|
|
|
|
|
414
|
setting 'session' => 'simple'; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# capture logs for testing |
79
|
86
|
|
|
|
|
344
|
setting 'logger' => 'capture'; |
80
|
86
|
|
|
|
|
393
|
setting 'log' => 'debug'; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Route Registry |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub _isa { |
86
|
1206
|
|
|
1206
|
|
2008
|
my ( $reference, $classname ) = @_; |
87
|
1206
|
|
66
|
|
|
5935
|
return blessed $reference && $reference->isa($classname); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub _req_to_response { |
91
|
430
|
|
|
430
|
|
1330
|
my $req = shift; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# already a response object |
94
|
430
|
100
|
|
|
|
882
|
return $req if _isa($req, 'Dancer::Response'); |
95
|
|
|
|
|
|
|
|
96
|
422
|
100
|
|
|
|
1544
|
return dancer_response( ref $req eq 'ARRAY' ? @$req : ( 'GET', $req ) ); |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
sub _req_label { |
100
|
274
|
|
|
274
|
|
486
|
my $req = shift; |
101
|
|
|
|
|
|
|
|
102
|
274
|
100
|
|
|
|
650
|
return _isa($req, 'Dancer::Response') ? 'response object' |
|
|
100
|
|
|
|
|
|
103
|
|
|
|
|
|
|
: ref $req eq 'ARRAY' ? join( ' ', @$req ) |
104
|
|
|
|
|
|
|
: "GET $req"; |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
sub expand_req { |
108
|
54
|
|
|
54
|
0
|
232
|
my $req = shift; |
109
|
54
|
50
|
|
|
|
278
|
return ref $req eq 'ARRAY' ? @$req : ( 'GET', $req ); |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub route_exists { |
113
|
43
|
|
|
43
|
1
|
14287
|
my ($req, $test_name) = @_; |
114
|
43
|
|
|
|
|
177
|
my $tb = Test::Builder->new; |
115
|
|
|
|
|
|
|
|
116
|
43
|
|
|
|
|
362
|
my ($method, $path) = expand_req($req); |
117
|
43
|
|
66
|
|
|
284
|
$test_name ||= "a route exists for $method $path"; |
118
|
|
|
|
|
|
|
|
119
|
43
|
|
|
|
|
257
|
$req = Dancer::Request->new_for_request($method => $path); |
120
|
43
|
|
|
|
|
206
|
return $tb->ok(defined(Dancer::App->find_route_through_apps($req)), $test_name); |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub route_doesnt_exist { |
124
|
4
|
|
|
4
|
1
|
2641
|
my ($req, $test_name) = @_; |
125
|
4
|
|
|
|
|
16
|
my $tb = Test::Builder->new; |
126
|
|
|
|
|
|
|
|
127
|
4
|
|
|
|
|
29
|
my ($method, $path) = expand_req($req); |
128
|
4
|
|
66
|
|
|
23
|
$test_name ||= "no route exists for $method $path"; |
129
|
|
|
|
|
|
|
|
130
|
4
|
|
|
|
|
19
|
$req = Dancer::Request->new_for_request($method => $path); |
131
|
4
|
|
|
|
|
19
|
return $tb->ok(!defined(Dancer::App->find_route_through_apps($req)), $test_name); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# Response status |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub response_exists { |
137
|
0
|
|
|
0
|
1
|
0
|
Dancer::Deprecation->deprecated( |
138
|
|
|
|
|
|
|
fatal => 1, |
139
|
|
|
|
|
|
|
feature => 'response_exists', |
140
|
|
|
|
|
|
|
reason => 'Use response_status_isnt and check for status 404.' |
141
|
|
|
|
|
|
|
); |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub response_doesnt_exist { |
145
|
0
|
|
|
0
|
1
|
0
|
Dancer::Deprecation->deprecated( |
146
|
|
|
|
|
|
|
fatal => 1, |
147
|
|
|
|
|
|
|
feature => 'response_doesnt_exist', |
148
|
|
|
|
|
|
|
reason => 'Use response_status_is and check for status 404.', |
149
|
|
|
|
|
|
|
); |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub response_status_is { |
153
|
160
|
|
|
160
|
1
|
79427
|
my ($req, $status, $test_name) = @_; |
154
|
160
|
|
66
|
|
|
792
|
$test_name ||= "response status is $status for " . _req_label($req); |
155
|
|
|
|
|
|
|
|
156
|
160
|
|
|
|
|
378
|
my $response = _req_to_response($req); |
157
|
160
|
|
|
|
|
840
|
my $tb = Test::Builder->new; |
158
|
160
|
|
|
|
|
1192
|
return $tb->is_eq($response->status, $status, $test_name); |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub response_status_isnt { |
162
|
4
|
|
|
4
|
1
|
3203
|
my ($req, $status, $test_name) = @_; |
163
|
4
|
|
66
|
|
|
30
|
$test_name ||= "response status is not $status for " . _req_label($req); |
164
|
|
|
|
|
|
|
|
165
|
4
|
|
|
|
|
14
|
my $response = _req_to_response($req); |
166
|
4
|
|
|
|
|
28
|
my $tb = Test::Builder->new; |
167
|
4
|
|
|
|
|
43
|
$tb->isnt_eq( $response->{status}, $status, $test_name ); |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# Response content |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub response_content_is { |
173
|
123
|
|
|
123
|
1
|
59965
|
my ($req, $matcher, $test_name) = @_; |
174
|
123
|
|
66
|
|
|
551
|
$test_name ||= "response content looks good for " . _req_label($req); |
175
|
|
|
|
|
|
|
|
176
|
123
|
|
|
|
|
547
|
my $response = _req_to_response($req); |
177
|
123
|
|
|
|
|
676
|
my $tb = Test::Builder->new; |
178
|
123
|
|
|
|
|
1014
|
return $tb->is_eq( $response->{content}, $matcher, $test_name ); |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub response_content_isnt { |
182
|
4
|
|
|
4
|
1
|
2755
|
my ($req, $matcher, $test_name) = @_; |
183
|
4
|
|
33
|
|
|
30
|
$test_name ||= "response content looks good for " . _req_label($req); |
184
|
|
|
|
|
|
|
|
185
|
4
|
|
|
|
|
15
|
my $response = _req_to_response($req); |
186
|
4
|
|
|
|
|
25
|
my $tb = Test::Builder->new; |
187
|
4
|
|
|
|
|
40
|
return $tb->isnt_eq( $response->{content}, $matcher, $test_name ); |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub response_content_like { |
191
|
41
|
|
|
41
|
1
|
16808
|
my ($req, $matcher, $test_name) = @_; |
192
|
41
|
|
66
|
|
|
194
|
$test_name ||= "response content looks good for " . _req_label($req); |
193
|
|
|
|
|
|
|
|
194
|
41
|
|
|
|
|
111
|
my $response = _req_to_response($req); |
195
|
41
|
|
|
|
|
225
|
return like_string( $response->{content}, $matcher, $test_name ); # better output for long content than Test::Builder |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub response_content_unlike { |
199
|
4
|
|
|
4
|
1
|
1748
|
my ($req, $matcher, $test_name) = @_; |
200
|
4
|
|
50
|
|
|
30
|
$test_name ||= "response content looks good for " , _req_label($req); |
201
|
|
|
|
|
|
|
|
202
|
4
|
|
|
|
|
14
|
my $response = _req_to_response($req); |
203
|
4
|
|
|
|
|
23
|
return unlike_string( $response->{content}, $matcher, $test_name ); # better for long content than Test::Builder |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
sub response_content_is_deeply { |
207
|
59
|
|
|
59
|
1
|
31895
|
my ($req, $matcher, $test_name) = @_; |
208
|
59
|
|
66
|
|
|
216
|
$test_name ||= "response content looks good for " . _req_label($req); |
209
|
|
|
|
|
|
|
|
210
|
59
|
|
|
|
|
110
|
local $Test::Builder::Level = $Test::Builder::Level + 1; |
211
|
59
|
|
|
|
|
96
|
my $response = _req_to_response($req); |
212
|
59
|
|
|
|
|
218
|
is_deeply $response->{content}, $matcher, $test_name; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub response_is_file { |
216
|
1
|
|
|
1
|
0
|
48
|
my ($req, $test_name) = @_; |
217
|
1
|
|
33
|
|
|
8
|
$test_name ||= "a file is returned for " . _req_label($req); |
218
|
|
|
|
|
|
|
|
219
|
1
|
|
|
|
|
4
|
my $response = _get_file_response($req); |
220
|
1
|
|
|
|
|
5
|
my $tb = Test::Builder->new; |
221
|
1
|
|
|
|
|
8
|
return $tb->ok(defined($response), $test_name); |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub response_headers_are_deeply { |
225
|
7
|
|
|
7
|
1
|
1078
|
my ($req, $expected, $test_name) = @_; |
226
|
7
|
|
66
|
|
|
28
|
$test_name ||= "headers are as expected for " . _req_label($req); |
227
|
|
|
|
|
|
|
|
228
|
7
|
|
|
|
|
14
|
local $Test::Builder::Level = $Test::Builder::Level + 1; |
229
|
7
|
|
|
|
|
16
|
my $response = _req_to_response($req); |
230
|
|
|
|
|
|
|
|
231
|
7
|
|
|
|
|
51
|
is_deeply( |
232
|
|
|
|
|
|
|
_sort_headers( $response->headers_to_array ), |
233
|
|
|
|
|
|
|
_sort_headers( $expected ), |
234
|
|
|
|
|
|
|
$test_name |
235
|
|
|
|
|
|
|
); |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
# Sort arrayref of headers (turn it into a list of arrayrefs, sort by the header |
239
|
|
|
|
|
|
|
# & value, then turn it back into an arrayref) |
240
|
|
|
|
|
|
|
sub _sort_headers { |
241
|
14
|
|
|
14
|
|
20
|
my @originalheaders = @{ shift() }; # take a copy we can modify |
|
14
|
|
|
|
|
49
|
|
242
|
14
|
|
|
|
|
35
|
my @headerpairs; |
243
|
14
|
|
|
|
|
38
|
while (my ($header, $value) = splice @originalheaders, 0, 2) { |
244
|
40
|
|
|
|
|
105
|
push @headerpairs, [ $header, $value ]; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
# We have an array of arrayrefs holding header => value pairs; sort them by |
248
|
|
|
|
|
|
|
# header then value, and return them flattened back into an arrayref |
249
|
|
|
|
|
|
|
return [ |
250
|
40
|
|
|
|
|
105
|
map { @$_ } |
251
|
14
|
50
|
|
|
|
34
|
sort { $a->[0] cmp $b->[0] || $a->[1] cmp $b->[1] } |
|
35
|
|
|
|
|
69
|
|
252
|
|
|
|
|
|
|
@headerpairs |
253
|
|
|
|
|
|
|
]; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
sub response_headers_include { |
258
|
27
|
|
|
27
|
1
|
8926
|
my ($req, $expected, $test_name) = @_; |
259
|
27
|
|
66
|
|
|
129
|
$test_name ||= "headers include expected data for " . _req_label($req); |
260
|
27
|
|
|
|
|
90
|
my $tb = Test::Builder->new; |
261
|
|
|
|
|
|
|
|
262
|
27
|
|
|
|
|
169
|
my $response = _req_to_response($req); |
263
|
27
|
|
|
|
|
90
|
return $tb->ok(_include_in_headers($response->headers_to_array, $expected), $test_name); |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
sub response_redirect_location_is { |
267
|
0
|
|
|
0
|
1
|
0
|
my ($req, $expected, $test_name) = @_; |
268
|
0
|
|
0
|
|
|
0
|
$test_name ||= "redirect location looks good for " . _req_label($req); |
269
|
0
|
|
|
|
|
0
|
my $tb = Test::Builder->new; |
270
|
|
|
|
|
|
|
|
271
|
0
|
|
|
|
|
0
|
my $response = _req_to_response($req); |
272
|
0
|
|
|
|
|
0
|
return $tb->is_eq($response->header('location'), $expected, $test_name); |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
sub response_redirect_location_like { |
276
|
0
|
|
|
0
|
1
|
0
|
my ($req, $matcher, $test_name) = @_; |
277
|
0
|
|
0
|
|
|
0
|
$test_name ||= "redirect location looks good for " . _req_label($req); |
278
|
0
|
|
|
|
|
0
|
my $tb = Test::Builder->new; |
279
|
|
|
|
|
|
|
|
280
|
0
|
|
|
|
|
0
|
my $response = _req_to_response($req); |
281
|
0
|
|
|
|
|
0
|
return $tb->like($response->header('location'), $matcher, $test_name); |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# make sure the given header sublist is included in the full headers array |
286
|
|
|
|
|
|
|
sub _include_in_headers { |
287
|
27
|
|
|
27
|
|
58
|
my ($full_headers, $expected_subset) = @_; |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# walk through all the expected header pairs, make sure |
290
|
|
|
|
|
|
|
# they exist with the same value in the full_headers list |
291
|
|
|
|
|
|
|
# return false as soon as one is not. |
292
|
27
|
|
|
|
|
149
|
for (my $i=0; $i
|
293
|
45
|
|
|
|
|
122
|
my ($name, $value) = ($expected_subset->[$i], $expected_subset->[$i + 1]); |
294
|
45
|
50
|
|
|
|
139
|
return 0 |
295
|
|
|
|
|
|
|
unless _check_header($full_headers, $name, $value); |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
# we've found all the expected pairs in the $full_headers list |
299
|
27
|
|
|
|
|
205
|
return 1; |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub _check_header { |
303
|
45
|
|
|
45
|
|
88
|
my ($headers, $key, $value) = @_; |
304
|
45
|
|
|
|
|
108
|
for (my $i=0; $i
|
305
|
112
|
|
|
|
|
173
|
my ($name, $val) = ($headers->[$i], $headers->[$i + 1]); |
306
|
112
|
100
|
100
|
|
|
363
|
return 1 if $name eq $key && $value eq $val; |
307
|
|
|
|
|
|
|
} |
308
|
0
|
|
|
|
|
0
|
return 0; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
sub dancer_response { |
312
|
502
|
|
|
502
|
1
|
38496
|
my ($method, $path, $args) = @_; |
313
|
502
|
|
100
|
|
|
2103
|
$args ||= {}; |
314
|
502
|
|
|
|
|
815
|
my $extra_env = {}; |
315
|
|
|
|
|
|
|
|
316
|
502
|
100
|
|
|
|
1655
|
if ($method =~ /^(?:PUT|POST)$/) { |
317
|
|
|
|
|
|
|
|
318
|
28
|
|
|
|
|
63
|
my ($content, $content_type); |
319
|
|
|
|
|
|
|
|
320
|
28
|
50
|
66
|
|
|
212
|
if ( $args->{body} and $args->{files} ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# XXX: When fixing this, update this method's POD |
322
|
0
|
|
|
|
|
0
|
croak 'dancer_response() does not support both body and files'; |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
elsif ( $args->{body} ) { |
325
|
13
|
|
|
|
|
28
|
$content = $args->{body}; |
326
|
|
|
|
|
|
|
$content_type = $args->{content_type} |
327
|
13
|
|
100
|
|
|
64
|
|| 'text/plain'; |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# coerce hashref into an url-encoded string |
330
|
13
|
100
|
66
|
|
|
50
|
if ( ref($content) && ( ref($content) eq 'HASH' ) ) { |
331
|
3
|
|
|
|
|
6
|
my @tokens; |
332
|
3
|
|
|
|
|
7
|
while ( my ( $name, $value ) = each %{$content} ) { |
|
6
|
|
|
|
|
26
|
|
333
|
3
|
|
|
|
|
12
|
$name = _url_encode($name); |
334
|
3
|
100
|
|
|
|
16
|
my @values = ref $value eq 'ARRAY' ? @$value : ($value); |
335
|
3
|
|
|
|
|
8
|
for my $value (@values) { |
336
|
4
|
|
|
|
|
9
|
$value = _url_encode($value); |
337
|
4
|
|
|
|
|
18
|
push @tokens, "${name}=${value}"; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
} |
340
|
3
|
|
|
|
|
24
|
$content = join( '&', @tokens ); |
341
|
3
|
|
|
|
|
9
|
$content_type = 'application/x-www-form-urlencoded'; |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
} |
344
|
|
|
|
|
|
|
elsif ( $args->{files} ) { |
345
|
3
|
|
|
|
|
9
|
$content_type = 'multipart/form-data; boundary=----BOUNDARY'; |
346
|
3
|
|
|
|
|
6
|
foreach my $file (@{$args->{files}}){ |
|
3
|
|
|
|
|
11
|
|
347
|
5
|
|
50
|
|
|
31
|
$file->{content_type} ||= 'text/plain'; |
348
|
5
|
|
|
|
|
11
|
$content .= qq/------BOUNDARY\r\n/; |
349
|
5
|
|
|
|
|
18
|
$content .= qq/Content-Disposition: form-data; name="$file->{name}"; filename="$file->{filename}"\r\n/; |
350
|
5
|
|
|
|
|
13
|
$content .= qq/Content-Type: $file->{content_type}\r\n\r\n/; |
351
|
5
|
100
|
|
|
|
14
|
if ( $file->{data} ) { |
352
|
1
|
|
|
|
|
4
|
$content .= $file->{data}; |
353
|
|
|
|
|
|
|
} else { |
354
|
|
|
|
|
|
|
open my $fh, '<', $file->{filename} |
355
|
4
|
50
|
|
|
|
111
|
or die "Failed to open $file->{filename} - $!"; |
356
|
4
|
50
|
|
|
|
142
|
if ( -B $file->{filename} ) { |
357
|
0
|
|
|
|
|
0
|
binmode $fh; |
358
|
|
|
|
|
|
|
} |
359
|
4
|
|
|
|
|
62
|
while (<$fh>) { |
360
|
4
|
|
|
|
|
48
|
$content .= $_; |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
} |
363
|
5
|
|
|
|
|
15
|
$content .= "\r\n"; |
364
|
|
|
|
|
|
|
} |
365
|
3
|
|
|
|
|
8
|
$content .= "------BOUNDARY"; |
366
|
|
|
|
|
|
|
} |
367
|
|
|
|
|
|
|
|
368
|
28
|
|
|
|
|
54
|
my $l = 0; |
369
|
28
|
100
|
|
|
|
81
|
$l = length $content if defined $content; |
370
|
28
|
|
|
10
|
|
797
|
open my $in, '<', \$content; |
|
10
|
|
|
|
|
79
|
|
|
10
|
|
|
|
|
18
|
|
|
10
|
|
|
|
|
267
|
|
371
|
28
|
|
|
|
|
7917
|
$extra_env->{'CONTENT_LENGTH'} = $l; |
372
|
28
|
|
100
|
|
|
136
|
$extra_env->{'CONTENT_TYPE'} = $content_type || ""; |
373
|
28
|
|
|
|
|
73
|
$extra_env->{'psgi.input'} = $in; |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
502
|
|
|
|
|
1574
|
my ($params, $body, $headers) = @$args{qw(params body headers)}; |
377
|
|
|
|
|
|
|
|
378
|
502
|
100
|
|
|
|
1489
|
$headers = HTTP::Headers->new(@{$headers||[]}) |
|
500
|
100
|
|
|
|
2806
|
|
379
|
|
|
|
|
|
|
unless _isa($headers, "HTTP::Headers"); |
380
|
|
|
|
|
|
|
|
381
|
502
|
100
|
|
|
|
5078
|
if ($headers->header('Content-Type')) { |
382
|
16
|
|
|
|
|
472
|
$extra_env->{'CONTENT_TYPE'} = $headers->header('Content-Type'); |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# handle all the keys of Request::_build_request_env(): |
386
|
502
|
|
|
|
|
17876
|
for my $key (qw( user_agent host accept_language accept_charset |
387
|
|
|
|
|
|
|
accept_encoding keep_alive connection accept accept_type referer |
388
|
|
|
|
|
|
|
x_requested_with )) { |
389
|
5522
|
|
|
|
|
9232
|
my $k = sprintf("HTTP_%s", uc $key); |
390
|
|
|
|
|
|
|
$extra_env->{$k} = $headers->{$key} |
391
|
5522
|
100
|
|
|
|
9151
|
if exists $headers->{$key}; |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
# fake the REQUEST_URI |
395
|
|
|
|
|
|
|
# TODO deal with the params |
396
|
502
|
50
|
|
|
|
1791
|
unless( $extra_env->{REQUEST_URI} ) { |
397
|
502
|
|
|
|
|
1284
|
$extra_env->{REQUEST_URI} = $path; |
398
|
502
|
100
|
100
|
|
|
2152
|
if ( $method eq 'GET' and $params ) { |
399
|
|
|
|
|
|
|
$extra_env->{REQUEST_URI} .= |
400
|
8
|
|
|
|
|
41
|
'?' . join '&', map { join '=', $_, $params->{$_} } |
|
7
|
|
|
|
|
41
|
|
401
|
|
|
|
|
|
|
sort keys %$params; |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
} |
404
|
|
|
|
|
|
|
|
405
|
502
|
|
|
|
|
2409
|
my $request = Dancer::Request->new_for_request( |
406
|
|
|
|
|
|
|
$method => $path, |
407
|
|
|
|
|
|
|
$params, $body, $headers, $extra_env |
408
|
|
|
|
|
|
|
); |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
# first, reset the current state |
411
|
502
|
|
|
|
|
1894
|
Dancer::SharedData->reset_all(); |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
# then store the request |
414
|
502
|
|
|
|
|
1508
|
Dancer::SharedData->request($request); |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# XXX this is a hack!! |
417
|
502
|
100
|
|
|
|
1625
|
$request = Dancer::Serializer->process_request($request) |
418
|
|
|
|
|
|
|
if Dancer::App->current->setting('serializer'); |
419
|
|
|
|
|
|
|
|
420
|
502
|
|
|
|
|
1687
|
my $get_action = Dancer::Handler::render_request($request); |
421
|
502
|
|
|
|
|
1445
|
my $response = Dancer::SharedData->response(); |
422
|
|
|
|
|
|
|
|
423
|
502
|
100
|
|
|
|
1252
|
$response->content('') if $method eq 'HEAD'; |
424
|
502
|
|
|
|
|
1312
|
Dancer::SharedData->reset_response(); |
425
|
502
|
50
|
|
|
|
2451
|
return $response if $get_action; |
426
|
0
|
0
|
0
|
|
|
0
|
(defined $response && $response->exists) ? return $response : return undef; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
# private |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
sub _url_encode { |
432
|
7
|
|
|
7
|
|
11
|
my $string = shift; |
433
|
7
|
|
|
|
|
19
|
$string =~ s/([\W])/"%" . uc(sprintf("%2.2x",ord($1)))/eg; |
|
1
|
|
|
|
|
9
|
|
434
|
7
|
|
|
|
|
12
|
return $string; |
435
|
|
|
|
|
|
|
} |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
sub _get_file_response { |
438
|
7
|
|
|
7
|
|
1344
|
my ($req) = @_; |
439
|
|
|
|
|
|
|
|
440
|
7
|
|
|
|
|
15
|
my ($method, $path, $params) = expand_req($req); |
441
|
7
|
|
|
|
|
34
|
my $request = Dancer::Request->new_for_request($method => $path, $params); |
442
|
7
|
|
|
|
|
22
|
Dancer::SharedData->request($request); |
443
|
7
|
|
|
|
|
98
|
return Dancer::Renderer::get_file_response(); |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
sub _get_handler_response { |
447
|
0
|
|
|
0
|
|
0
|
my ($req) = @_; |
448
|
0
|
|
|
|
|
0
|
my ($method, $path, $params) = expand_req($req); |
449
|
0
|
|
|
|
|
0
|
my $request = Dancer::Request->new_for_request($method => $path, $params); |
450
|
0
|
|
|
|
|
0
|
return Dancer::Handler->handle_request($request); |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
sub read_logs { |
454
|
2
|
|
|
2
|
1
|
10
|
return Dancer::Logger::Capture->trap->read; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
1; |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
__END__ |