line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Eve::HttpDispatcher; |
2
|
|
|
|
|
|
|
|
3
|
8
|
|
|
8
|
|
50
|
use parent qw(Eve::Class); |
|
8
|
|
|
|
|
18
|
|
|
8
|
|
|
|
|
82
|
|
4
|
|
|
|
|
|
|
|
5
|
8
|
|
|
8
|
|
537
|
use strict; |
|
8
|
|
|
|
|
23
|
|
|
8
|
|
|
|
|
535
|
|
6
|
8
|
|
|
8
|
|
46
|
use warnings; |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
231
|
|
7
|
|
|
|
|
|
|
|
8
|
8
|
|
|
8
|
|
4128
|
use Eve::Event::HttpResponseReady; |
|
8
|
|
|
|
|
19
|
|
|
8
|
|
|
|
|
205
|
|
9
|
8
|
|
|
8
|
|
43
|
use Eve::Exception; |
|
8
|
|
|
|
|
14
|
|
|
8
|
|
|
|
|
8557
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
B - an event handler for HTTP request events. |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use Eve::HttpDispatcher; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my $dispatcher = Eve::HttpDispatcher->new( |
20
|
|
|
|
|
|
|
request_constructor => $request_constructor, |
21
|
|
|
|
|
|
|
response => $response, |
22
|
|
|
|
|
|
|
base_uri => $base_uri, |
23
|
|
|
|
|
|
|
alias_base_uri_list => [$alias_base_uri, $another_alias_base_uri], |
24
|
|
|
|
|
|
|
event_map => $event_map); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
$dispatcher->bind( |
27
|
|
|
|
|
|
|
name => $name |
28
|
|
|
|
|
|
|
pattern => $pattern, |
29
|
|
|
|
|
|
|
resource_constructor => $resource_constructor); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
$dispatcher->bind( |
32
|
|
|
|
|
|
|
name => $name_404 |
33
|
|
|
|
|
|
|
pattern => $pattern_404, |
34
|
|
|
|
|
|
|
resource_constructor => $resource_constructor_404, |
35
|
|
|
|
|
|
|
exception => 'Eve::Exception::Http::404NotFound'); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
$dispatcher->handle(event => $event); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 DESCRIPTION |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
B class is a central component to a web |
42
|
|
|
|
|
|
|
service application. |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head3 Constructor arguments |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=over 4 |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=item C |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
a code reference that returns an HTTP request object when passed an |
51
|
|
|
|
|
|
|
environment hash |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=item C |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
an HTTP response object |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=item C |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
an event map object. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=item C |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
a base URI object used for resource binding. |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=item C |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
(optional) a reference to a list of additional base URI objects that |
68
|
|
|
|
|
|
|
will be used for resource matching. |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=back |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=head1 METHODS |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=head2 B |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
=cut |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub init { |
79
|
0
|
|
|
0
|
1
|
|
my ($self, %arg_hash) = @_; |
80
|
0
|
|
|
|
|
|
Eve::Support::arguments( |
81
|
|
|
|
|
|
|
\%arg_hash, |
82
|
|
|
|
|
|
|
my ($request_constructor, $response, $event_map, $base_uri), |
83
|
|
|
|
|
|
|
my $alias_base_uri_list = []); |
84
|
|
|
|
|
|
|
|
85
|
0
|
|
|
|
|
|
$self->{'_request_constructor'} = $request_constructor; |
86
|
0
|
|
|
|
|
|
$self->{'_response'} = $response; |
87
|
0
|
|
|
|
|
|
$self->{'_event_map'} = $event_map; |
88
|
0
|
|
|
|
|
|
$self->{'_base_uri'} = $base_uri; |
89
|
|
|
|
|
|
|
|
90
|
0
|
|
|
|
|
|
$self->{'_base_uri_list'} = [$base_uri, @{$alias_base_uri_list}]; |
|
0
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
0
|
|
|
|
|
|
$self->{'_uri_map'} = {}; |
93
|
0
|
|
|
|
|
|
$self->{'_name_map'} = {}; |
94
|
0
|
|
|
|
|
|
$self->{'_exception_name_hash'} = {}; |
95
|
|
|
|
|
|
|
|
96
|
0
|
|
|
|
|
|
return; |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=head2 B |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
Binds an HTTP resource. |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head3 Arguments |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=over 4 |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=item C |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
a name identifying the binding |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=item C |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
an URI pattern string that can contain placeholders |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=item C |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
a URI object that represents a base URL for the binding resource |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=item C |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
a code reference that returns an HTTP resource object |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=item C |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
(optional) an HTTP exception class name that the bound resource should |
126
|
|
|
|
|
|
|
be used to handle. B: there can be only one resource bound to |
127
|
|
|
|
|
|
|
handle a certain exception. |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=back |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=head3 Throws |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=over 3 |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=item C |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
if either name or compound URI or exception class name is not unique. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=back |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=cut |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub bind { |
144
|
0
|
|
|
0
|
1
|
|
my ($self, %arg_hash) = @_; |
145
|
0
|
|
|
|
|
|
Eve::Support::arguments( |
146
|
|
|
|
|
|
|
\%arg_hash, my ($name, $pattern, $resource_constructor), |
147
|
|
|
|
|
|
|
my $exception = \undef); |
148
|
|
|
|
|
|
|
|
149
|
0
|
0
|
|
|
|
|
if (exists $self->_uri_map->{$pattern}) { |
150
|
0
|
|
|
|
|
|
Eve::Error::HttpDispatcher->throw( |
151
|
|
|
|
|
|
|
message => 'Binding URI must be unique: '.$pattern); |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
0
|
0
|
|
|
|
|
if (exists $self->_name_map->{$name}) { |
155
|
0
|
|
|
|
|
|
Eve::Error::HttpDispatcher->throw( |
156
|
|
|
|
|
|
|
message => 'Binding name must be unique: '.$name); |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# Constructing all possible URIs that this resource is supposed to match |
160
|
|
|
|
|
|
|
# using the base URI as well as possible aliases |
161
|
0
|
|
|
|
|
|
my $uri_list = []; |
162
|
0
|
|
|
|
|
|
for my $uri (@{$self->_base_uri_list}) { |
|
0
|
|
|
|
|
|
|
163
|
0
|
|
|
|
|
|
my $map_uri = $uri->clone(); |
164
|
0
|
|
|
|
|
|
$map_uri->path_concat(string => $pattern); |
165
|
|
|
|
|
|
|
|
166
|
0
|
|
|
|
|
|
push(@{$uri_list}, $map_uri); |
|
0
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
0
|
|
|
|
|
|
$self->_uri_map->{$pattern} = { |
170
|
|
|
|
|
|
|
'resource' => $resource_constructor->(), |
171
|
|
|
|
|
|
|
'uri_list' => $uri_list}; |
172
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
$self->_name_map->{$name} = { |
174
|
|
|
|
|
|
|
'pattern' => $pattern, 'uri_list' => $uri_list}; |
175
|
|
|
|
|
|
|
|
176
|
0
|
0
|
|
|
|
|
if (defined $exception) { |
177
|
0
|
0
|
|
|
|
|
if (exists $self->_exception_name_hash->{$exception}) { |
178
|
0
|
|
|
|
|
|
Eve::Error::HttpDispatcher->throw( |
179
|
|
|
|
|
|
|
message => 'Exception name must be unique: ' . $exception); |
180
|
|
|
|
|
|
|
} |
181
|
0
|
|
|
|
|
|
$self->_exception_name_hash->{$exception} = $pattern; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
0
|
|
|
|
|
|
return; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 B |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head3 Arguments |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=over 4 |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=item C |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
a name identifying the binding |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=back |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=head3 Returns |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
A URI bound to the resource name. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head3 Throws |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=over 3 |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=item C |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
When there is no resource with the requested name. |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=back |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=cut |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
sub get_uri { |
216
|
0
|
|
|
0
|
1
|
|
my ($self, %arg_hash) = @_; |
217
|
0
|
|
|
|
|
|
Eve::Support::arguments(\%arg_hash, my $name); |
218
|
|
|
|
|
|
|
|
219
|
0
|
0
|
|
|
|
|
if (not exists $self->_name_map->{$name}) { |
220
|
0
|
|
|
|
|
|
Eve::Error::HttpDispatcher->throw( |
221
|
|
|
|
|
|
|
message => 'There is no resource with such name: '.$name); |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# We are relying on the fact that the page URI generated by the |
225
|
|
|
|
|
|
|
# base URI is always the first in the uri list for the specified |
226
|
|
|
|
|
|
|
# name. |
227
|
0
|
|
|
|
|
|
return $self->_name_map->{$name}->{'uri_list'}->[0]; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=head2 B |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
Chooses a resource using the request URI and delegates control to the |
233
|
|
|
|
|
|
|
resource's C method. It also passes placeholder matches |
234
|
|
|
|
|
|
|
into this method. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head3 Arguments |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=over 4 |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=item C |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
a C object. |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=back |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head3 Throws |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=over 3 |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=item C |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
if no resources match the request and no resources that handle the |
253
|
|
|
|
|
|
|
C are bound. |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=back |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=cut |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
sub handle { |
260
|
0
|
|
|
0
|
1
|
|
my ($self, %arg_hash) = @_; |
261
|
0
|
|
|
|
|
|
Eve::Support::arguments(\%arg_hash, my $event); |
262
|
|
|
|
|
|
|
|
263
|
0
|
|
|
|
|
|
$self->{'_request'} = |
264
|
|
|
|
|
|
|
$self->_request_constructor->(env_hash => $event->env_hash); |
265
|
|
|
|
|
|
|
|
266
|
0
|
|
|
|
|
|
my $response; |
267
|
|
|
|
|
|
|
|
268
|
0
|
|
|
|
|
|
eval { |
269
|
0
|
|
|
|
|
|
for my $key (keys %{$self->_uri_map}) { |
|
0
|
|
|
|
|
|
|
270
|
0
|
|
|
|
|
|
my $request_uri = $self->_request->get_uri(); |
271
|
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
|
for my $uri (@{$self->_uri_map->{$key}->{'uri_list'}}) { |
|
0
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
|
my $match_hash = $uri->match(uri => $request_uri); |
274
|
0
|
0
|
|
|
|
|
if ($match_hash) { |
275
|
0
|
|
|
|
|
|
$response = $self->_uri_map->{$key}->{'resource'}->process( |
276
|
|
|
|
|
|
|
matches_hash => $match_hash, |
277
|
|
|
|
|
|
|
request => $self->_request); |
278
|
0
|
|
|
|
|
|
return; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
0
|
|
|
|
|
|
Eve::Exception::Http::404NotFound->throw(); |
284
|
|
|
|
|
|
|
}; |
285
|
|
|
|
|
|
|
|
286
|
0
|
|
|
|
|
|
my $e; |
287
|
0
|
0
|
|
|
|
|
if ($e = Eve::Exception::Base->caught()) { |
|
|
0
|
|
|
|
|
|
288
|
0
|
0
|
|
|
|
|
if (defined $self->_exception_name_hash->{ref $e}){ |
289
|
|
|
|
|
|
|
my $resource = |
290
|
|
|
|
|
|
|
$self->_uri_map->{$self->_exception_name_hash->{ref $e}}->{ |
291
|
0
|
|
|
|
|
|
'resource'}; |
292
|
|
|
|
|
|
|
|
293
|
0
|
|
|
|
|
|
$response = $resource->process( |
294
|
|
|
|
|
|
|
matches_hash => {'exception' => $e}, |
295
|
|
|
|
|
|
|
request => $self->_request); |
296
|
|
|
|
|
|
|
} else { |
297
|
0
|
|
|
|
|
|
$e->throw(); |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
} elsif ($e = Exception::Class->caught()) { |
300
|
0
|
0
|
|
|
|
|
ref $e ? $e->rethrow() : die $e; |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
|
303
|
0
|
|
|
|
|
|
$event->response = $response; |
304
|
|
|
|
|
|
|
|
305
|
0
|
|
|
|
|
|
return; |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=head1 SEE ALSO |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=over 4 |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=item L |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=item L |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=item L |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=item L |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=back |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
Copyright 2012 Igor Zinovyev. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
327
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published |
328
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information. |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=head1 AUTHOR |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=over 4 |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=item L |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=item L |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=back |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=cut |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
1; |