| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Web::Simple::Application; |
|
2
|
|
|
|
|
|
|
|
|
3
|
15
|
|
|
15
|
|
19676
|
use Scalar::Util 'weaken'; |
|
|
15
|
|
|
|
|
23
|
|
|
|
15
|
|
|
|
|
1209
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
15
|
|
|
15
|
|
638
|
use Moo; |
|
|
15
|
|
|
|
|
11840
|
|
|
|
15
|
|
|
|
|
102
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
has 'config' => ( |
|
8
|
|
|
|
|
|
|
is => 'ro', |
|
9
|
|
|
|
|
|
|
default => sub { |
|
10
|
|
|
|
|
|
|
my ($self) = @_; |
|
11
|
|
|
|
|
|
|
+{ $self->default_config } |
|
12
|
|
|
|
|
|
|
}, |
|
13
|
|
|
|
|
|
|
trigger => sub { |
|
14
|
|
|
|
|
|
|
my ($self, $value) = @_; |
|
15
|
|
|
|
|
|
|
my %default = $self->default_config; |
|
16
|
|
|
|
|
|
|
my @not = grep !exists $value->{$_}, keys %default; |
|
17
|
|
|
|
|
|
|
@{$value}{@not} = @default{@not}; |
|
18
|
|
|
|
|
|
|
} |
|
19
|
|
|
|
|
|
|
); |
|
20
|
|
|
|
|
|
|
|
|
21
|
13
|
|
|
13
|
1
|
404
|
sub default_config { () } |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
has '_dispatcher' => (is => 'lazy'); |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub _build__dispatcher { |
|
26
|
13
|
|
|
13
|
|
7722
|
my $self = shift; |
|
27
|
13
|
|
|
|
|
8011
|
require Web::Dispatch; |
|
28
|
13
|
|
|
|
|
236
|
my $final = $self->_build_final_dispatcher; |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# We need to weaken both the copy of $self that the |
|
31
|
|
|
|
|
|
|
# app parameter will close over and the copy that'll |
|
32
|
|
|
|
|
|
|
# be passed through as a node argument. |
|
33
|
|
|
|
|
|
|
# |
|
34
|
|
|
|
|
|
|
# To ensure that this doesn't then result in us being |
|
35
|
|
|
|
|
|
|
# DESTROYed unexpectedly early, our to_psgi_app method |
|
36
|
|
|
|
|
|
|
# closes back over $self |
|
37
|
|
|
|
|
|
|
|
|
38
|
13
|
|
|
|
|
82
|
weaken($self); |
|
39
|
|
|
|
|
|
|
my %dispatch_args = ( |
|
40
|
63
|
|
|
63
|
|
411
|
dispatch_app => sub { $self->dispatch_request(@_), $final }, |
|
41
|
13
|
|
|
|
|
118
|
dispatch_object => $self |
|
42
|
|
|
|
|
|
|
); |
|
43
|
13
|
|
|
|
|
114
|
weaken($dispatch_args{dispatch_object}); |
|
44
|
13
|
|
|
|
|
107
|
Web::Dispatch->new(%dispatch_args); |
|
45
|
|
|
|
|
|
|
} |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub _build_final_dispatcher { |
|
48
|
13
|
|
|
13
|
|
80
|
[ 404, [ 'Content-type', 'text/plain' ], [ 'Not found' ] ] |
|
49
|
|
|
|
|
|
|
} |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
sub run_if_script { |
|
52
|
|
|
|
|
|
|
# ->to_psgi_app is true for require() but also works for plackup |
|
53
|
1
|
50
|
|
1
|
1
|
7
|
return $_[0]->to_psgi_app if caller(1); |
|
54
|
0
|
0
|
|
|
|
0
|
my $self = ref($_[0]) ? $_[0] : $_[0]->new; |
|
55
|
0
|
|
|
|
|
0
|
$self->run(@_); |
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub _run_cgi { |
|
59
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
60
|
0
|
|
|
|
|
0
|
require Plack::Handler::CGI; |
|
61
|
0
|
|
|
|
|
0
|
Plack::Handler::CGI->new->run($self->to_psgi_app); |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub _run_fcgi { |
|
65
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
|
66
|
0
|
|
|
|
|
0
|
require Plack::Handler::FCGI; |
|
67
|
0
|
|
|
|
|
0
|
Plack::Handler::FCGI->new->run($self->to_psgi_app); |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub to_psgi_app { |
|
71
|
63
|
100
|
|
63
|
1
|
235
|
my $self = ref($_[0]) ? $_[0] : $_[0]->new; |
|
72
|
63
|
|
|
|
|
1616
|
my $app = $self->_dispatcher->to_app; |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# Close over $self to keep $self alive even though |
|
75
|
|
|
|
|
|
|
# we weakened the copies the dispatcher has; the |
|
76
|
|
|
|
|
|
|
# if 0 causes the ops to be optimised away to |
|
77
|
|
|
|
|
|
|
# minimise the performance impact and avoid void |
|
78
|
|
|
|
|
|
|
# context warnings while still doing the closing |
|
79
|
|
|
|
|
|
|
# over part. As Mithaldu said: "Gnarly." ... |
|
80
|
|
|
|
|
|
|
|
|
81
|
63
|
|
|
63
|
|
495
|
return sub { $self if 0; goto &$app; }; |
|
|
63
|
|
|
|
|
86743
|
|
|
|
63
|
|
|
|
|
280
|
|
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub run { |
|
85
|
3
|
|
|
3
|
1
|
1459
|
my $self = shift; |
|
86
|
3
|
100
|
33
|
|
|
45
|
if ( |
|
|
|
100
|
33
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
87
|
|
|
|
|
|
|
$ENV{PHP_FCGI_CHILDREN} || $ENV{FCGI_ROLE} || $ENV{FCGI_SOCKET_PATH} |
|
88
|
|
|
|
|
|
|
|| ( -S STDIN && !$ENV{GATEWAY_INTERFACE} ) |
|
89
|
|
|
|
|
|
|
# If STDIN is a socket, almost certainly FastCGI, except for mod_cgid |
|
90
|
|
|
|
|
|
|
) { |
|
91
|
1
|
|
|
|
|
4
|
return $self->_run_fcgi; |
|
92
|
|
|
|
|
|
|
} elsif ($ENV{GATEWAY_INTERFACE}) { |
|
93
|
1
|
|
|
|
|
4
|
return $self->_run_cgi; |
|
94
|
|
|
|
|
|
|
} |
|
95
|
1
|
50
|
33
|
|
|
4
|
unless (@ARGV && $ARGV[0] =~ m{(^[A-Z/])|\@}) { |
|
96
|
1
|
|
|
|
|
4
|
return $self->_run_cli(@ARGV); |
|
97
|
|
|
|
|
|
|
} |
|
98
|
|
|
|
|
|
|
|
|
99
|
0
|
|
|
|
|
0
|
my @args = @ARGV; |
|
100
|
|
|
|
|
|
|
|
|
101
|
0
|
0
|
|
|
|
0
|
unshift(@args, 'GET') if $args[0] !~ /^[A-Z]/; |
|
102
|
|
|
|
|
|
|
|
|
103
|
0
|
|
|
|
|
0
|
$self->_run_cli_test_request(@args); |
|
104
|
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub _test_request_spec_to_http_request { |
|
107
|
60
|
|
|
60
|
|
142
|
my ($self, $method, $path, @rest) = @_; |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# if it's a reference, assume a request object |
|
110
|
60
|
100
|
|
|
|
270
|
return $method if ref($method); |
|
111
|
|
|
|
|
|
|
|
|
112
|
47
|
100
|
|
|
|
194
|
if ($path =~ s/^(.*?)\@//) { |
|
113
|
2
|
|
|
|
|
8
|
my $basic = $1; |
|
114
|
2
|
|
|
|
|
13
|
require MIME::Base64; |
|
115
|
2
|
|
|
|
|
29
|
unshift @rest, 'Authorization:', 'Basic '.MIME::Base64::encode($basic); |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
|
|
118
|
47
|
|
|
|
|
318
|
my $request = HTTP::Request->new($method => $path); |
|
119
|
|
|
|
|
|
|
|
|
120
|
47
|
|
|
|
|
63156
|
my @params; |
|
121
|
|
|
|
|
|
|
|
|
122
|
47
|
|
|
|
|
252
|
while (my ($header, $value) = splice(@rest, 0, 2)) { |
|
123
|
4
|
100
|
|
|
|
28
|
unless ($header =~ s/:$//) { |
|
124
|
2
|
|
|
|
|
5
|
push @params, $header, $value; |
|
125
|
|
|
|
|
|
|
} |
|
126
|
4
|
|
|
|
|
10
|
$header =~ s/_/-/g; |
|
127
|
4
|
50
|
|
|
|
15
|
if ($header eq 'Content') { |
|
128
|
0
|
|
|
|
|
0
|
$request->content($value); |
|
129
|
|
|
|
|
|
|
} else { |
|
130
|
4
|
|
|
|
|
21
|
$request->headers->push_header($header, $value); |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
|
|
134
|
47
|
100
|
100
|
|
|
537
|
if (($method eq 'POST' or $method eq 'PUT') and @params) { |
|
|
|
|
100
|
|
|
|
|
|
135
|
2
|
|
|
|
|
4
|
my $content = do { |
|
136
|
2
|
|
|
|
|
9
|
require URI; |
|
137
|
2
|
|
|
|
|
8
|
my $url = URI->new('http:'); |
|
138
|
2
|
|
|
|
|
112
|
$url->query_form(@params); |
|
139
|
2
|
|
|
|
|
239
|
$url->query; |
|
140
|
|
|
|
|
|
|
}; |
|
141
|
2
|
|
|
|
|
34
|
$request->header('Content-Type' => 'application/x-www-form-urlencoded'); |
|
142
|
2
|
|
|
|
|
109
|
$request->header('Content-Length' => length($content)); |
|
143
|
2
|
|
|
|
|
65
|
$request->content($content); |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
47
|
|
|
|
|
155
|
return $request; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub run_test_request { |
|
150
|
60
|
|
|
60
|
1
|
101052
|
my ($self, @req) = @_; |
|
151
|
|
|
|
|
|
|
|
|
152
|
60
|
|
|
|
|
5968
|
require HTTP::Request; |
|
153
|
|
|
|
|
|
|
|
|
154
|
60
|
|
|
|
|
203214
|
require Plack::Test; |
|
155
|
|
|
|
|
|
|
|
|
156
|
60
|
|
|
|
|
5732
|
my $request = $self->_test_request_spec_to_http_request(@req); |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
Plack::Test::test_psgi( |
|
159
|
60
|
|
|
60
|
|
151252
|
$self->to_psgi_app, sub { shift->($request) } |
|
160
|
60
|
|
|
|
|
288
|
); |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub _run_cli_test_request { |
|
164
|
0
|
|
|
0
|
|
|
my ($self, @req) = @_; |
|
165
|
0
|
|
|
|
|
|
my $response = $self->run_test_request(@req); |
|
166
|
|
|
|
|
|
|
|
|
167
|
0
|
|
|
|
|
|
binmode(STDOUT); binmode(STDERR); # for win32 |
|
|
0
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
|
|
169
|
0
|
|
|
|
|
|
print STDERR $response->status_line."\n"; |
|
170
|
0
|
|
|
|
|
|
print STDERR $response->headers_as_string("\n")."\n"; |
|
171
|
0
|
|
|
|
|
|
my $content = $response->content; |
|
172
|
0
|
0
|
0
|
|
|
|
$content .= "\n" if length($content) and $content !~ /\n\z/; |
|
173
|
0
|
0
|
|
|
|
|
print STDOUT $content if $content; |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub _run_cli { |
|
177
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
178
|
0
|
|
|
|
|
|
die $self->_cli_usage; |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub _cli_usage { |
|
182
|
0
|
|
|
0
|
|
|
"To run this script in CGI test mode, pass a URL path beginning with /:\n". |
|
183
|
|
|
|
|
|
|
"\n". |
|
184
|
|
|
|
|
|
|
" $0 /some/path\n". |
|
185
|
|
|
|
|
|
|
" $0 /\n" |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
1; |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 NAME |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Web::Simple::Application - A base class for your Web-Simple application |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
This is a base class for your L application. You probably don't |
|
197
|
|
|
|
|
|
|
need to construct this class yourself, since L does the 'heavy |
|
198
|
|
|
|
|
|
|
lifting' for you in that regards. |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=head1 METHODS |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
This class exposes the following public methods. |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head2 default_config |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
Merges with the C initializer to provide configuration information for |
|
207
|
|
|
|
|
|
|
your application. For example: |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
sub default_config { |
|
210
|
|
|
|
|
|
|
( |
|
211
|
|
|
|
|
|
|
title => 'Bloggery', |
|
212
|
|
|
|
|
|
|
posts_dir => $FindBin::Bin.'/posts', |
|
213
|
|
|
|
|
|
|
); |
|
214
|
|
|
|
|
|
|
} |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
Now, the C attribute of C<$self> will be set to a HashRef |
|
217
|
|
|
|
|
|
|
containing keys 'title' and 'posts_dir'. |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
The keys from default_config are merged into any config supplied, so |
|
220
|
|
|
|
|
|
|
if you construct your application like: |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
MyWebSimpleApp::Web->new( |
|
223
|
|
|
|
|
|
|
config => { title => 'Spoon', environment => 'dev' } |
|
224
|
|
|
|
|
|
|
) |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
then C will contain: |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
{ |
|
229
|
|
|
|
|
|
|
title => 'Spoon', |
|
230
|
|
|
|
|
|
|
posts_dir => '/path/to/myapp/posts', |
|
231
|
|
|
|
|
|
|
environment => 'dev' |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head2 run_if_script |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
The run_if_script method is designed to be used at the end of the script |
|
237
|
|
|
|
|
|
|
or .pm file where your application class is defined - for example: |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
## my_web_simple_app.pl |
|
240
|
|
|
|
|
|
|
#!/usr/bin/env perl |
|
241
|
|
|
|
|
|
|
use Web::Simple 'HelloWorld'; |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
{ |
|
244
|
|
|
|
|
|
|
package HelloWorld; |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub dispatch_request { |
|
247
|
|
|
|
|
|
|
sub (GET) { |
|
248
|
|
|
|
|
|
|
[ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ] |
|
249
|
|
|
|
|
|
|
}, |
|
250
|
|
|
|
|
|
|
sub () { |
|
251
|
|
|
|
|
|
|
[ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ] |
|
252
|
|
|
|
|
|
|
} |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
HelloWorld->run_if_script; |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
This returns a true value, so your file is now valid as a module - so |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
require 'my_web_simple_app.pl'; |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
my $hw = HelloWorld->new; |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
will work fine (and you can rename it to lib/HelloWorld.pm later to make it |
|
265
|
|
|
|
|
|
|
a real use-able module). |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
However, it detects if it's being run as a script (via testing $0) and if |
|
268
|
|
|
|
|
|
|
so attempts to do the right thing. |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
If run under a CGI environment, your application will execute as a CGI. |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
If run under a FastCGI environment, your application will execute as a |
|
273
|
|
|
|
|
|
|
FastCGI process (this works both for dynamic shared-hosting-style FastCGI |
|
274
|
|
|
|
|
|
|
and for apache FastCgiServer style setups). |
|
275
|
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
If run from the commandline with a URL path, it runs a GET request against |
|
277
|
|
|
|
|
|
|
that path - |
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
$ perl -Ilib examples/hello-world/hello-world.cgi / |
|
280
|
|
|
|
|
|
|
200 OK |
|
281
|
|
|
|
|
|
|
Content-Type: text/plain |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
Hello world! |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
You can also provide a method name - |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
$ perl -Ilib examples/hello-world/hello-world.cgi POST / |
|
288
|
|
|
|
|
|
|
405 Method Not Allowed |
|
289
|
|
|
|
|
|
|
Content-Type: text/plain |
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Method not allowed |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
For a POST or PUT request, pairs on the command line will be treated |
|
294
|
|
|
|
|
|
|
as form variables. For any request, pairs on the command line ending in : |
|
295
|
|
|
|
|
|
|
are treated as headers, and 'Content:' will set the request body - |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
$ ./myapp POST / Accept: text/html form_field_name form_field_value |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
$ ./myapp POST / Content-Type: text/json Content: '{ "json": "here" }' |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
The body of the response is sent to STDOUT and the headers to STDERR, so |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
$ ./myapp GET / >index.html |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
will generally do the right thing. |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
To send basic authentication credentials, use user:pass@ syntax - |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
$ ./myapp GET bob:secret@/protected/path |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
Additionally, you can treat the file as though it were a standard PSGI |
|
312
|
|
|
|
|
|
|
application file (*.psgi). For example you can start up up with C |
|
313
|
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
plackup my_web_simple_app.pl |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
or C |
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
starman my_web_simple_app.pl |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=head2 to_psgi_app |
|
321
|
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
This method is called by L to create the L app coderef |
|
323
|
|
|
|
|
|
|
for use via L and L. If you want to globally add middleware, |
|
324
|
|
|
|
|
|
|
you can override this method: |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
use Web::Simple 'HelloWorld'; |
|
327
|
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
{ |
|
329
|
|
|
|
|
|
|
package HelloWorld; |
|
330
|
|
|
|
|
|
|
use Plack::Builder; |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
around 'to_psgi_app', sub { |
|
333
|
|
|
|
|
|
|
my ($orig, $self) = (shift, shift); |
|
334
|
|
|
|
|
|
|
my $app = $self->$orig(@_); |
|
335
|
|
|
|
|
|
|
builder { |
|
336
|
|
|
|
|
|
|
enable ...; ## whatever middleware you want |
|
337
|
|
|
|
|
|
|
$app; |
|
338
|
|
|
|
|
|
|
}; |
|
339
|
|
|
|
|
|
|
}; |
|
340
|
|
|
|
|
|
|
} |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
This method can also be used to mount a Web::Simple application within |
|
343
|
|
|
|
|
|
|
a separate C<*.psgi> file - |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
use strictures 1; |
|
346
|
|
|
|
|
|
|
use Plack::Builder; |
|
347
|
|
|
|
|
|
|
use WSApp; |
|
348
|
|
|
|
|
|
|
use AnotherWSApp; |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
builder { |
|
351
|
|
|
|
|
|
|
mount '/' => WSApp->to_psgi_app; |
|
352
|
|
|
|
|
|
|
mount '/another' => AnotherWSApp->to_psgi_app; |
|
353
|
|
|
|
|
|
|
}; |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
This method can be called as a class method, in which case it implicitly |
|
356
|
|
|
|
|
|
|
calls ->new, or as an object method ... in which case it doesn't. |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=head2 run |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
Used for running your application under stand-alone CGI and FCGI modes. |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
I should document this more extensively but run_if_script will call it when |
|
363
|
|
|
|
|
|
|
you need it, so don't worry about it too much. |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=head2 run_test_request |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
my $res = $app->run_test_request(GET => '/' => %headers); |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
my $res = $app->run_test_request(POST => '/' => %headers_or_form); |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
my $res = $app->run_test_request($http_request); |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
Accepts either an L object or ($method, $path) and runs that |
|
374
|
|
|
|
|
|
|
request against the application, returning an L object. |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
If the HTTP method is POST or PUT, then a series of pairs can be passed after |
|
377
|
|
|
|
|
|
|
this to create a form style message body. If you need to test an upload, then |
|
378
|
|
|
|
|
|
|
create an L object by hand or use the C subroutine |
|
379
|
|
|
|
|
|
|
provided by L. |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
If you prefix the URL with 'user:pass@' this will be converted into |
|
382
|
|
|
|
|
|
|
an Authorization header for HTTP basic auth: |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
my $res = $app->run_test_request( |
|
385
|
|
|
|
|
|
|
GET => 'bob:secret@/protected/resource' |
|
386
|
|
|
|
|
|
|
); |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
If pairs are passed where the key ends in :, it is instead treated as a |
|
389
|
|
|
|
|
|
|
headers, so: |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
my $res = $app->run_test_request( |
|
392
|
|
|
|
|
|
|
POST => '/', |
|
393
|
|
|
|
|
|
|
'Accept:' => 'text/html', |
|
394
|
|
|
|
|
|
|
some_form_key => 'value' |
|
395
|
|
|
|
|
|
|
); |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
will do what you expect. You can also pass a special key of Content: to |
|
398
|
|
|
|
|
|
|
set the request body: |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
my $res = $app->run_test_request( |
|
401
|
|
|
|
|
|
|
POST => '/', |
|
402
|
|
|
|
|
|
|
'Content-Type:' => 'text/json', |
|
403
|
|
|
|
|
|
|
'Content:' => '{ "json": "here" }', |
|
404
|
|
|
|
|
|
|
); |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=head1 AUTHORS |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
See L for authors. |
|
409
|
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
See L for the copyright and license. |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
=cut |