line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojolicious::Controller; |
2
|
52
|
|
|
52
|
|
68641
|
use Mojo::Base -base; |
|
52
|
|
|
|
|
155
|
|
|
52
|
|
|
|
|
455
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
# No imports, for security reasons! |
5
|
52
|
|
|
52
|
|
785
|
use Carp (); |
|
52
|
|
|
|
|
171
|
|
|
52
|
|
|
|
|
946
|
|
6
|
52
|
|
|
52
|
|
354
|
use Digest::SHA (); |
|
52
|
|
|
|
|
167
|
|
|
52
|
|
|
|
|
1032
|
|
7
|
52
|
|
|
52
|
|
1299
|
use Mojo::ByteStream; |
|
52
|
|
|
|
|
166
|
|
|
52
|
|
|
|
|
2780
|
|
8
|
52
|
|
|
52
|
|
1297
|
use Mojo::DynamicMethods -dispatch; |
|
52
|
|
|
|
|
201
|
|
|
52
|
|
|
|
|
568
|
|
9
|
52
|
|
|
52
|
|
1347
|
use Mojo::URL; |
|
52
|
|
|
|
|
181
|
|
|
52
|
|
|
|
|
575
|
|
10
|
52
|
|
|
52
|
|
380
|
use Mojo::Util; |
|
52
|
|
|
|
|
197
|
|
|
52
|
|
|
|
|
2486
|
|
11
|
52
|
|
|
52
|
|
25727
|
use Mojolicious::Routes::Match; |
|
52
|
|
|
|
|
183
|
|
|
52
|
|
|
|
|
478
|
|
12
|
52
|
|
|
52
|
|
519
|
use Scalar::Util (); |
|
52
|
|
|
|
|
157
|
|
|
52
|
|
|
|
|
189319
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
has [qw(app tx)] => undef, weak => 1; |
15
|
|
|
|
|
|
|
has match => sub { Mojolicious::Routes::Match->new(root => shift->app->routes) }; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub BUILD_DYNAMIC { |
18
|
3451
|
|
|
3451
|
0
|
7234
|
my ($class, $method, $dyn_methods) = @_; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
return sub { |
21
|
507
|
|
|
507
|
|
3075
|
my $self = shift; |
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
159
|
|
|
|
22
|
507
|
|
|
|
|
3290
|
my $dynamic = $dyn_methods->{$self->{app}{renderer}}{$method}; |
23
|
507
|
50
|
|
|
|
2270
|
return $self->$dynamic(@_) if $dynamic; |
24
|
0
|
|
|
|
|
0
|
my $package = ref $self; |
25
|
0
|
|
|
|
|
0
|
Carp::croak qq{Can't locate object method "$method" via package "$package"}; |
26
|
3451
|
|
|
|
|
21255
|
}; |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
1
|
|
|
1
|
1
|
9
|
sub continue { $_[0]->app->routes->continue($_[0]) } |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub cookie { |
32
|
148
|
|
|
148
|
1
|
779
|
my ($self, $name) = (shift, shift); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# Response cookie |
35
|
148
|
100
|
|
|
|
348
|
if (@_) { |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Cookie too big |
38
|
114
|
|
100
|
|
|
228
|
my $cookie = {name => $name, value => shift, %{shift // {}}}; |
|
114
|
|
|
|
|
802
|
|
39
|
114
|
50
|
|
|
|
414
|
$self->helpers->log->error(qq{Cookie "$name" is bigger than 4KiB}) if length $cookie->{value} > 4096; |
40
|
|
|
|
|
|
|
|
41
|
114
|
|
|
|
|
285
|
$self->res->cookies($cookie); |
42
|
114
|
|
|
|
|
497
|
return $self; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# Request cookies |
46
|
34
|
100
|
|
|
|
87
|
return undef unless my $cookie = $self->req->cookie($name); |
47
|
21
|
|
|
|
|
72
|
return $cookie->value; |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
245
|
|
|
245
|
1
|
405
|
sub every_cookie { [map { $_->value } @{shift->req->every_cookie(shift)}] } |
|
68
|
|
|
|
|
179
|
|
|
245
|
|
|
|
|
574
|
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub every_param { |
53
|
392
|
|
|
392
|
1
|
975
|
my ($self, $name) = @_; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# Captured unreserved values |
56
|
392
|
|
100
|
|
|
1159
|
my $captures = $self->stash->{'mojo.captures'} //= {}; |
57
|
392
|
100
|
100
|
|
|
1543
|
if (exists $captures->{$name} && !$self->app->routes->is_reserved($name)) { |
58
|
84
|
|
|
|
|
190
|
my $value = $captures->{$name}; |
59
|
84
|
100
|
|
|
|
652
|
return ref $value eq 'ARRAY' ? $value : [$value]; |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# Uploads or param values |
63
|
308
|
|
|
|
|
840
|
my $req = $self->req; |
64
|
308
|
|
|
|
|
1486
|
my $uploads = $req->every_upload($name); |
65
|
308
|
100
|
|
|
|
1430
|
return @$uploads ? $uploads : $req->every_param($name); |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
sub every_signed_cookie { |
69
|
243
|
|
|
243
|
1
|
530
|
my ($self, $name) = @_; |
70
|
|
|
|
|
|
|
|
71
|
243
|
|
|
|
|
625
|
my $secrets = $self->app->secrets; |
72
|
243
|
|
|
|
|
447
|
my @results; |
73
|
243
|
|
|
|
|
432
|
for my $value (@{$self->every_cookie($name)}) { |
|
243
|
|
|
|
|
660
|
|
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# Check signature with rotating secrets |
76
|
66
|
100
|
|
|
|
581
|
if ($value =~ s/--([^\-]+)$//) { |
77
|
57
|
|
|
|
|
172
|
my $signature = $1; |
78
|
|
|
|
|
|
|
|
79
|
57
|
|
|
|
|
114
|
my $valid; |
80
|
57
|
|
|
|
|
135
|
for my $secret (@$secrets) { |
81
|
65
|
|
|
|
|
1143
|
my $check = Digest::SHA::hmac_sha256_hex("$name=$value", $secret); |
82
|
65
|
100
|
50
|
|
|
281
|
++$valid and last if Mojo::Util::secure_compare($signature, $check); |
83
|
|
|
|
|
|
|
} |
84
|
57
|
100
|
|
|
|
159
|
if ($valid) { push @results, $value } |
|
45
|
|
|
|
|
200
|
|
85
|
|
|
|
|
|
|
|
86
|
12
|
|
|
|
|
54
|
else { $self->helpers->log->trace(qq{Cookie "$name" has bad signature}) } |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
9
|
|
|
|
|
28
|
else { $self->helpers->log->trace(qq{Cookie "$name" is not signed}) } |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
243
|
|
|
|
|
1332
|
return \@results; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub finish { |
96
|
18
|
|
|
18
|
1
|
142
|
my $self = shift; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# WebSocket |
99
|
18
|
|
33
|
|
|
79
|
my $tx = $self->tx || Carp::croak 'Transaction already destroyed'; |
100
|
18
|
100
|
50
|
|
|
87
|
$tx->finish(@_) and return $tx->established ? $self : $self->rendered(101) if $tx->is_websocket; |
|
|
100
|
|
|
|
|
|
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# Chunked stream |
103
|
9
|
100
|
|
|
|
47
|
return @_ ? $self->write_chunk(@_)->write_chunk('') : $self->write_chunk('') if $tx->res->content->is_chunked; |
|
|
100
|
|
|
|
|
|
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# Normal stream |
106
|
3
|
100
|
|
|
|
33
|
return @_ ? $self->write(@_)->write('') : $self->write(''); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
4692
|
|
|
4692
|
1
|
13481
|
sub helpers { $_[0]->app->renderer->get_helper('')->($_[0]) } |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
sub on { |
112
|
93
|
|
|
93
|
1
|
712
|
my ($self, $name, $cb) = @_; |
113
|
93
|
|
33
|
|
|
304
|
my $tx = $self->tx || Carp::croak 'Transaction already destroyed'; |
114
|
93
|
100
|
66
|
|
|
302
|
$self->rendered(101) if $tx->is_websocket && !$tx->established; |
115
|
93
|
|
|
89
|
|
675
|
return $tx->on($name => sub { shift; $self->$cb(@_) }); |
|
89
|
|
|
|
|
185
|
|
|
89
|
|
|
|
|
340
|
|
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub param { |
119
|
261
|
|
|
261
|
1
|
1257
|
my ($self, $name) = (shift, shift); |
120
|
261
|
100
|
|
|
|
1006
|
return $self->every_param($name)->[-1] unless @_; |
121
|
27
|
100
|
|
|
|
124
|
$self->stash->{'mojo.captures'}{$name} = @_ > 1 ? [@_] : $_[0]; |
122
|
27
|
|
|
|
|
288
|
return $self; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub render { |
126
|
973
|
|
|
973
|
1
|
2831
|
my $self = shift; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Template may be first argument |
129
|
973
|
100
|
|
|
|
5728
|
my ($template, $args) = (@_ % 2 ? shift : undef, {@_}); |
130
|
973
|
100
|
|
|
|
2587
|
$args->{template} = $template if $template; |
131
|
973
|
|
|
|
|
2585
|
my $app = $self->app; |
132
|
973
|
|
|
|
|
2892
|
my $plugins = $app->plugins->emit_hook(before_render => $self, $args); |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# Localize "extends" and "layout" to allow argument overrides |
135
|
973
|
|
|
|
|
2330
|
my ($maybe, $ts) = @{$args}{'mojo.maybe', 'mojo.string'}; |
|
973
|
|
|
|
|
2438
|
|
136
|
973
|
|
|
|
|
2264
|
my $stash = $self->stash; |
137
|
973
|
100
|
|
|
|
2828
|
local $stash->{layout} = $stash->{layout} if exists $stash->{layout}; |
138
|
973
|
100
|
|
|
|
2484
|
local $stash->{extends} = $stash->{extends} if exists $stash->{extends}; |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# Rendering to string |
141
|
973
|
100
|
100
|
|
|
5078
|
local @{$stash}{keys %$args} if $ts || $maybe; |
|
573
|
|
|
|
|
2013
|
|
142
|
973
|
100
|
|
|
|
2375
|
delete @{$stash}{qw(layout extends)} if $ts; |
|
26
|
|
|
|
|
87
|
|
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# All other arguments just become part of the stash |
145
|
973
|
|
|
|
|
3539
|
@$stash{keys %$args} = values %$args; |
146
|
973
|
|
|
|
|
2853
|
my $renderer = $app->renderer; |
147
|
973
|
|
|
|
|
3376
|
my ($output, $format) = $renderer->render($self); |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# Maybe no 404 |
150
|
953
|
50
|
|
|
|
3440
|
return defined $output ? Mojo::ByteStream->new($output) : undef if $ts; |
|
|
100
|
|
|
|
|
|
151
|
928
|
100
|
|
|
|
7392
|
return $maybe ? undef : Carp::croak("Could not render a response") unless defined $output; |
|
|
100
|
|
|
|
|
|
152
|
|
|
|
|
|
|
|
153
|
739
|
|
|
|
|
3353
|
$plugins->emit_hook(after_render => $self, \$output, $format); |
154
|
739
|
|
|
|
|
4073
|
return $renderer->respond($self, $output, $format, $stash->{status}); |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
968
|
|
|
968
|
1
|
2248
|
sub render_later { shift->stash('mojo.rendered' => 1) } |
158
|
|
|
|
|
|
|
|
159
|
552
|
|
|
552
|
1
|
1913
|
sub render_maybe { shift->render(@_, 'mojo.maybe' => 1) } |
160
|
|
|
|
|
|
|
|
161
|
26
|
|
|
26
|
1
|
176
|
sub render_to_string { shift->render(@_, 'mojo.string' => 1) } |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
sub rendered { |
164
|
1041
|
|
|
1041
|
1
|
2605
|
my ($self, $status) = @_; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Make sure we have a status |
167
|
1041
|
100
|
|
|
|
2713
|
$self->res->code($status) if $status; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# Finish transaction |
170
|
1041
|
|
|
|
|
2555
|
my $stash = $self->stash; |
171
|
1041
|
100
|
66
|
|
|
6190
|
if (!$stash->{'mojo.finished'} && ++$stash->{'mojo.finished'}) { |
172
|
944
|
|
|
|
|
2341
|
my $res = $self->res; |
173
|
944
|
100
|
100
|
|
|
3901
|
$res->code(200) if !$status && !$res->code; |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# Disable auto rendering and stop timer |
176
|
944
|
|
|
|
|
2707
|
my $app = $self->render_later->app; |
177
|
|
|
|
|
|
|
$self->helpers->log->trace(sub { |
178
|
255
|
|
|
255
|
|
596
|
my $timing = $self->helpers->timing; |
179
|
255
|
|
50
|
|
|
1028
|
my $elapsed = $timing->elapsed('mojo.timer') // 0; |
180
|
255
|
|
50
|
|
|
5134
|
my $rps = $timing->rps($elapsed) // '??'; |
181
|
255
|
|
|
|
|
892
|
my $code = $res->code; |
182
|
255
|
|
33
|
|
|
785
|
my $msg = $res->message || $res->default_message($code); |
183
|
255
|
|
|
|
|
6764
|
return "$code $msg (${elapsed}s, $rps/s)"; |
184
|
944
|
100
|
|
|
|
3592
|
}) unless $stash->{'mojo.static'}; |
185
|
|
|
|
|
|
|
|
186
|
944
|
|
|
|
|
8278
|
$app->plugins->emit_hook_reverse(after_dispatch => $self); |
187
|
944
|
|
|
|
|
2936
|
$app->sessions->store($self); |
188
|
|
|
|
|
|
|
} |
189
|
1041
|
|
|
|
|
3038
|
$self->tx->resume; |
190
|
1041
|
|
|
|
|
9314
|
return $self; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
5604
|
|
33
|
5604
|
1
|
17738
|
sub req { (shift->tx || Carp::croak 'Transaction already destroyed')->req } |
194
|
3426
|
|
33
|
3426
|
1
|
9374
|
sub res { (shift->tx || Carp::croak 'Transaction already destroyed')->res } |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub send { |
197
|
93
|
|
|
93
|
1
|
849
|
my ($self, $msg, $cb) = @_; |
198
|
93
|
|
33
|
|
|
301
|
my $tx = $self->tx || Carp::croak 'Transaction already destroyed'; |
199
|
93
|
100
|
|
|
|
351
|
Carp::croak 'No WebSocket connection to send message to' unless $tx->is_websocket; |
200
|
92
|
100
|
|
2
|
|
499
|
$tx->send($msg, $cb ? sub { shift; $self->$cb(@_) } : ()); |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
8
|
|
201
|
92
|
100
|
|
|
|
355
|
return $tx->established ? $self : $self->rendered(101); |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub session { |
205
|
237
|
|
|
237
|
1
|
608
|
my $self = shift; |
206
|
|
|
|
|
|
|
|
207
|
237
|
|
|
|
|
523
|
my $stash = $self->stash; |
208
|
237
|
100
|
|
|
|
950
|
$self->app->sessions->load($self) unless exists $stash->{'mojo.active_session'}; |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# Hash |
211
|
237
|
|
100
|
|
|
1016
|
my $session = $stash->{'mojo.session'} //= {}; |
212
|
237
|
100
|
|
|
|
5023
|
return $session unless @_; |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# Get |
215
|
39
|
100
|
66
|
|
|
188
|
return $session->{$_[0]} unless @_ > 1 || ref $_[0]; |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# Set |
218
|
21
|
50
|
|
|
|
74
|
my $values = ref $_[0] ? $_[0] : {@_}; |
219
|
21
|
|
|
|
|
90
|
@$session{keys %$values} = values %$values; |
220
|
|
|
|
|
|
|
|
221
|
21
|
|
|
|
|
74
|
return $self; |
222
|
|
|
|
|
|
|
} |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub signed_cookie { |
225
|
309
|
|
|
309
|
1
|
1056
|
my ($self, $name, $value, $options) = @_; |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# Request cookie |
228
|
309
|
100
|
|
|
|
1083
|
return $self->every_signed_cookie($name)->[-1] unless defined $value; |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# Response cookie |
231
|
68
|
|
|
|
|
347
|
my $sum = Digest::SHA::hmac_sha256_hex("$name=$value", $self->app->secrets->[0]); |
232
|
68
|
|
|
|
|
531
|
return $self->cookie($name, "$value--$sum", $options); |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
19840
|
|
|
19840
|
1
|
52222
|
sub stash { Mojo::Util::_stash(stash => @_) } |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub url_for { |
238
|
639
|
|
100
|
639
|
1
|
2479
|
my ($self, $target) = (shift, shift // ''); |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# Absolute URL |
241
|
639
|
100
|
66
|
|
|
2383
|
return $target if Scalar::Util::blessed $target && $target->isa('Mojo::URL'); |
242
|
619
|
100
|
|
|
|
2807
|
return Mojo::URL->new($target) if $target =~ m!^(?:[^:/?#]+:|//|#)!; |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# Base |
245
|
601
|
|
|
|
|
2562
|
my $url = Mojo::URL->new; |
246
|
601
|
|
|
|
|
1732
|
my $req = $self->req; |
247
|
601
|
|
|
|
|
2071
|
my $base = $url->base($req->url->base->clone)->base->userinfo(undef); |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# Relative URL |
250
|
601
|
|
|
|
|
1812
|
my $path = $url->path; |
251
|
601
|
100
|
|
|
|
2524
|
if ($target =~ m!^/!) { |
252
|
437
|
100
|
|
|
|
1197
|
if (defined(my $prefix = $self->stash->{path})) { |
253
|
30
|
|
|
|
|
111
|
my $real = $req->url->path->to_route; |
254
|
30
|
|
|
|
|
681
|
$real =~ s!/?\Q$prefix\E$!$target!; |
255
|
30
|
|
|
|
|
125
|
$target = $real; |
256
|
|
|
|
|
|
|
} |
257
|
437
|
|
|
|
|
1325
|
$url->parse($target); |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# Route |
261
|
|
|
|
|
|
|
else { |
262
|
164
|
|
|
|
|
563
|
my $generated = $self->match->path_for($target, @_); |
263
|
164
|
100
|
|
|
|
988
|
$path->parse($generated->{path}) if $generated->{path}; |
264
|
164
|
50
|
|
|
|
584
|
$base->scheme($base->protocol eq 'https' ? 'wss' : 'ws') if $generated->{websocket}; |
|
|
100
|
|
|
|
|
|
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# Make path absolute |
268
|
601
|
|
|
|
|
1617
|
my $base_path = $base->path; |
269
|
601
|
|
|
|
|
1149
|
unshift @{$path->parts}, @{$base_path->parts}; |
|
601
|
|
|
|
|
1494
|
|
|
601
|
|
|
|
|
1686
|
|
270
|
601
|
|
|
|
|
2061
|
$base_path->parts([])->trailing_slash(0); |
271
|
|
|
|
|
|
|
|
272
|
601
|
|
|
|
|
3243
|
return $url; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
sub url_for_asset { |
276
|
20
|
|
|
20
|
1
|
66
|
my ($self, $asset) = @_; |
277
|
20
|
|
|
|
|
63
|
return $self->url_for($self->app->static->asset_path($asset)); |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
sub url_for_file { |
281
|
374
|
|
|
374
|
1
|
789
|
my ($self, $file) = @_; |
282
|
374
|
|
|
|
|
1085
|
return $self->url_for($self->app->static->file_path($file)); |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
sub write { |
286
|
21
|
|
|
21
|
1
|
122
|
my ($self, $chunk, $cb) = @_; |
287
|
21
|
100
|
|
4
|
|
81
|
$self->res->content->write($chunk, $cb ? sub { shift; $self->$cb(@_) } : ()); |
|
4
|
|
|
|
|
19
|
|
|
4
|
|
|
|
|
28
|
|
288
|
21
|
|
|
|
|
72
|
return $self->rendered; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
sub write_chunk { |
292
|
61
|
|
|
61
|
1
|
531
|
my ($self, $chunk, $cb) = @_; |
293
|
61
|
100
|
|
37
|
|
189
|
$self->res->content->write_chunk($chunk, $cb ? sub { shift; $self->$cb(@_) } : ()); |
|
37
|
|
|
|
|
75
|
|
|
37
|
|
|
|
|
114
|
|
294
|
61
|
|
|
|
|
169
|
return $self->rendered; |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
1; |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
=encoding utf8 |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=head1 NAME |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
Mojolicious::Controller - Controller base class |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=head1 SYNOPSIS |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# Controller |
308
|
|
|
|
|
|
|
package MyApp::Controller::Foo; |
309
|
|
|
|
|
|
|
use Mojo::Base 'Mojolicious::Controller', -signatures; |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# Action |
312
|
|
|
|
|
|
|
sub bar ($self) { |
313
|
|
|
|
|
|
|
my $name = $self->param('name'); |
314
|
|
|
|
|
|
|
$self->res->headers->cache_control('max-age=1, no-cache'); |
315
|
|
|
|
|
|
|
$self->render(json => {hello => $name}); |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=head1 DESCRIPTION |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
L is the base class for your L controllers. It is also the default controller |
321
|
|
|
|
|
|
|
class unless you set L. |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
L implements the following attributes. |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=head2 app |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
my $app = $c->app; |
330
|
|
|
|
|
|
|
$c = $c->app(Mojolicious->new); |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
A reference back to the application that dispatched to this controller, usually a L object. Note that this |
333
|
|
|
|
|
|
|
attribute is weakened. |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# Use application logger |
336
|
|
|
|
|
|
|
$c->app->log->debug('Hello Mojo'); |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
# Generate path |
339
|
|
|
|
|
|
|
my $path = $c->app->home->child('templates', 'foo', 'bar.html.ep'); |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=head2 match |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
my $m = $c->match; |
344
|
|
|
|
|
|
|
$c = $c->match(Mojolicious::Routes::Match->new); |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
Router results for the current request, defaults to a L object. |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# Introspect |
349
|
|
|
|
|
|
|
my $name = $c->match->endpoint->name; |
350
|
|
|
|
|
|
|
my $foo = $c->match->endpoint->pattern->defaults->{foo}; |
351
|
|
|
|
|
|
|
my $action = $c->match->stack->[-1]{action}; |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=head2 tx |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
my $tx = $c->tx; |
356
|
|
|
|
|
|
|
$c = $c->tx(Mojo::Transaction::HTTP->new); |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
The transaction that is currently being processed, usually a L or |
359
|
|
|
|
|
|
|
L object. Note that this attribute is weakened. So the object needs to be referenced |
360
|
|
|
|
|
|
|
elsewhere as well when you're performing non-blocking operations and the underlying connection might get closed early. |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# Check peer information |
363
|
|
|
|
|
|
|
my $address = $c->tx->remote_address; |
364
|
|
|
|
|
|
|
my $port = $c->tx->remote_port; |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# Increase size limit for WebSocket messages to 16MiB |
367
|
|
|
|
|
|
|
$c->tx->max_websocket_size(16777216) if $c->tx->is_websocket; |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# Perform non-blocking operation without knowing the connection status |
370
|
|
|
|
|
|
|
my $tx = $c->tx; |
371
|
|
|
|
|
|
|
Mojo::IOLoop->timer(2 => sub { |
372
|
|
|
|
|
|
|
$c->app->log->debug($tx->is_finished ? 'Finished' : 'In progress'); |
373
|
|
|
|
|
|
|
}); |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=head1 METHODS |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
L inherits all methods from L and implements the following new ones. |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head2 continue |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
$c->continue; |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
Continue dispatch chain from an intermediate destination with L. |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=head2 cookie |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
my $value = $c->cookie('foo'); |
388
|
|
|
|
|
|
|
$c = $c->cookie(foo => 'bar'); |
389
|
|
|
|
|
|
|
$c = $c->cookie(foo => 'bar', {path => '/'}); |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
Access request cookie values and create new response cookies. If there are multiple values sharing the same name, and |
392
|
|
|
|
|
|
|
you want to access more than just the last one, you can use L"every_cookie">. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
# Create response cookie with domain and expiration date |
395
|
|
|
|
|
|
|
$c->cookie(user => 'sri', {domain => 'example.com', expires => time + 60}); |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# Create secure response cookie |
398
|
|
|
|
|
|
|
$c->cookie(secret => 'I <3 Mojolicious', {secure => 1, httponly => 1}); |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=head2 every_cookie |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
my $values = $c->every_cookie('foo'); |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Similar to L"cookie">, but returns all request cookie values sharing the same name as an array reference. |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
$ Get first cookie value |
407
|
|
|
|
|
|
|
my $first = $c->every_cookie('foo')->[0]; |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=head2 every_param |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
my $values = $c->every_param('foo'); |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
Similar to L"param">, but returns all values sharing the same name as an array reference. |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# Get first value |
416
|
|
|
|
|
|
|
my $first = $c->every_param('foo')->[0]; |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=head2 every_signed_cookie |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
my $values = $c->every_signed_cookie('foo'); |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
Similar to L"signed_cookie">, but returns all signed request cookie values sharing the same name as an array |
423
|
|
|
|
|
|
|
reference. |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
# Get first signed cookie value |
426
|
|
|
|
|
|
|
my $first = $c->every_signed_cookie('foo')->[0]; |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=head2 finish |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
$c = $c->finish; |
431
|
|
|
|
|
|
|
$c = $c->finish(1000); |
432
|
|
|
|
|
|
|
$c = $c->finish(1003 => 'Cannot accept data!'); |
433
|
|
|
|
|
|
|
$c = $c->finish('Bye!'); |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Close WebSocket connection or long poll stream gracefully. This method will automatically respond to WebSocket |
436
|
|
|
|
|
|
|
handshake requests with a C<101> response status, to establish the WebSocket connection. |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=head2 helpers |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
my $helpers = $c->helpers; |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
Return a proxy object containing the current controller object and on which helpers provided by L"app"> can be |
443
|
|
|
|
|
|
|
called. This includes all helpers from L and L. |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# Make sure to use the "title" helper and not the controller method |
446
|
|
|
|
|
|
|
$c->helpers->title('Welcome!'); |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
# Use a nested helper instead of the "reply" controller method |
449
|
|
|
|
|
|
|
$c->helpers->reply->not_found; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=head2 on |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
my $cb = $c->on(finish => sub {...}); |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
Subscribe to events of L"tx">, which is usually a L or L |
456
|
|
|
|
|
|
|
object. This method will automatically respond to WebSocket handshake requests with a C<101> response status, to |
457
|
|
|
|
|
|
|
establish the WebSocket connection. |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# Do something after the transaction has been finished |
460
|
|
|
|
|
|
|
$c->on(finish => sub ($c) { |
461
|
|
|
|
|
|
|
$c->app->log->debug('All data has been sent'); |
462
|
|
|
|
|
|
|
}); |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
# Receive WebSocket message |
465
|
|
|
|
|
|
|
$c->on(message => sub ($c, $msg) { |
466
|
|
|
|
|
|
|
$c->app->log->debug("Message: $msg"); |
467
|
|
|
|
|
|
|
}); |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# Receive JSON object via WebSocket message |
470
|
|
|
|
|
|
|
$c->on(json => sub ($c, $hash) { |
471
|
|
|
|
|
|
|
$c->app->log->debug("Test: $hash->{test}"); |
472
|
|
|
|
|
|
|
}); |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
# Receive WebSocket "Binary" message |
475
|
|
|
|
|
|
|
$c->on(binary => sub ($c, $bytes) { |
476
|
|
|
|
|
|
|
my $len = length $bytes; |
477
|
|
|
|
|
|
|
$c->app->log->debug("Received $len bytes"); |
478
|
|
|
|
|
|
|
}); |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
=head2 param |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
my $value = $c->param('foo'); |
483
|
|
|
|
|
|
|
$c = $c->param(foo => 'ba;r'); |
484
|
|
|
|
|
|
|
$c = $c->param(foo => 'ba;r', 'baz'); |
485
|
|
|
|
|
|
|
$c = $c->param(foo => ['ba;r', 'baz']); |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
Access route placeholder values that are not reserved stash values, file uploads as well as C and C |
488
|
|
|
|
|
|
|
parameters extracted from the query string and C or C message |
489
|
|
|
|
|
|
|
body, in that order. If there are multiple values sharing the same name, and you want to access more than just the last |
490
|
|
|
|
|
|
|
one, you can use L"every_param">. Parts of the request body need to be loaded into memory to parse C |
491
|
|
|
|
|
|
|
parameters, so you have to make sure it is not excessively large. There's a 16MiB limit for requests by default. |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# Get first value |
494
|
|
|
|
|
|
|
my $first = $c->every_param('foo')->[0]; |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
For more control you can also access request information directly. |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
# Only GET parameters |
499
|
|
|
|
|
|
|
my $foo = $c->req->query_params->param('foo'); |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
# Only POST parameters |
502
|
|
|
|
|
|
|
my $foo = $c->req->body_params->param('foo'); |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# Only GET and POST parameters |
505
|
|
|
|
|
|
|
my $foo = $c->req->param('foo'); |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
# Only file uploads |
508
|
|
|
|
|
|
|
my $foo = $c->req->upload('foo'); |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=head2 render |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
my $bool = $c->render; |
513
|
|
|
|
|
|
|
my $bool = $c->render(foo => 'bar', baz => 23); |
514
|
|
|
|
|
|
|
my $bool = $c->render(template => 'foo/index'); |
515
|
|
|
|
|
|
|
my $bool = $c->render(template => 'index', format => 'html'); |
516
|
|
|
|
|
|
|
my $bool = $c->render(data => $bytes); |
517
|
|
|
|
|
|
|
my $bool = $c->render(text => 'Hello!'); |
518
|
|
|
|
|
|
|
my $bool = $c->render(json => {foo => 'bar'}); |
519
|
|
|
|
|
|
|
my $bool = $c->render(handler => 'something'); |
520
|
|
|
|
|
|
|
my $bool = $c->render('foo/index'); |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
Render content with L and emit hooks L as well as |
523
|
|
|
|
|
|
|
L, or dies if no response could be generated. All additional key/value pairs get merged into |
524
|
|
|
|
|
|
|
the L"stash">. |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
# Render characters |
527
|
|
|
|
|
|
|
$c->render(text => 'I ♥ Mojolicious!'); |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
# Render characters (alternative) |
530
|
|
|
|
|
|
|
$c->stash(text => 'I ♥ Mojolicious!')->render; |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
# Render binary data |
533
|
|
|
|
|
|
|
use Mojo::JSON qw(encode_json); |
534
|
|
|
|
|
|
|
$c->render(data => encode_json({test => 'I ♥ Mojolicious!'})); |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# Render JSON |
537
|
|
|
|
|
|
|
$c->render(json => {test => 'I ♥ Mojolicious!'}); |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# Render inline template |
540
|
|
|
|
|
|
|
$c->render(inline => '<%= 1 + 1 %>'); |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
# Render template "foo/bar.html.ep" |
543
|
|
|
|
|
|
|
$c->render(template => 'foo/bar', format => 'html', handler => 'ep'); |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
# Render template "test.*.*" with arbitrary values "foo" and "bar" |
546
|
|
|
|
|
|
|
$c->render(template => 'test', foo => 'test', bar => 23); |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# Render template "test.xml.*" |
549
|
|
|
|
|
|
|
$c->render(template => 'test', format => 'xml'); |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# Render template "test.xml.*" (alternative) |
552
|
|
|
|
|
|
|
$c->render('test', format => 'xml'); |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
=head2 render_later |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
$c = $c->render_later; |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
Disable automatic rendering to delay response generation, only necessary if automatic rendering would result in a |
559
|
|
|
|
|
|
|
response. |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
# Delayed rendering |
562
|
|
|
|
|
|
|
$c->render_later; |
563
|
|
|
|
|
|
|
Mojo::IOLoop->timer(2 => sub { $c->render(text => 'Delayed by 2 seconds!') }); |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
=head2 render_maybe |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
my $bool = $c->render_maybe; |
568
|
|
|
|
|
|
|
my $bool = $c->render_maybe(foo => 'bar', baz => 23); |
569
|
|
|
|
|
|
|
my $bool = $c->render_maybe('foo/index', format => 'html'); |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
Try to render content, but do not call Lnot_found"> if no response |
572
|
|
|
|
|
|
|
could be generated, all arguments get localized automatically and are only available during this render operation, |
573
|
|
|
|
|
|
|
takes the same arguments as L"render">. |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
# Render template "index_local" only if it exists |
576
|
|
|
|
|
|
|
$c->render_maybe('index_local') or $c->render('index'); |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
=head2 render_to_string |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
my $output = $c->render_to_string('foo/index', format => 'pdf'); |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
Try to render content and return it wrapped in a L object or return C, all arguments get |
583
|
|
|
|
|
|
|
localized automatically and are only available during this render operation, takes the same arguments as L"render">. |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
# Render inline template |
586
|
|
|
|
|
|
|
my $two = $c->render_to_string(inline => '<%= 1 + 1 %>'); |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=head2 rendered |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
$c = $c->rendered; |
591
|
|
|
|
|
|
|
$c = $c->rendered(302); |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
Finalize response and emit hook L, defaults to using a C<200> response code. |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
# Custom response |
596
|
|
|
|
|
|
|
$c->res->headers->content_type('text/plain'); |
597
|
|
|
|
|
|
|
$c->res->body('Hello World!'); |
598
|
|
|
|
|
|
|
$c->rendered(200); |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
=head2 req |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
my $req = $c->req; |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
Get L object from L"tx">. |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
# Longer version |
607
|
|
|
|
|
|
|
my $req = $c->tx->req; |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
# Extract request information |
610
|
|
|
|
|
|
|
my $id = $c->req->request_id; |
611
|
|
|
|
|
|
|
my $method = $c->req->method; |
612
|
|
|
|
|
|
|
my $url = $c->req->url->to_abs; |
613
|
|
|
|
|
|
|
my $info = $c->req->url->to_abs->userinfo; |
614
|
|
|
|
|
|
|
my $host = $c->req->url->to_abs->host; |
615
|
|
|
|
|
|
|
my $agent = $c->req->headers->user_agent; |
616
|
|
|
|
|
|
|
my $custom = $c->req->headers->header('Custom-Header'); |
617
|
|
|
|
|
|
|
my $bytes = $c->req->body; |
618
|
|
|
|
|
|
|
my $str = $c->req->text; |
619
|
|
|
|
|
|
|
my $hash = $c->req->params->to_hash; |
620
|
|
|
|
|
|
|
my $all = $c->req->uploads; |
621
|
|
|
|
|
|
|
my $value = $c->req->json; |
622
|
|
|
|
|
|
|
my $foo = $c->req->json('/23/foo'); |
623
|
|
|
|
|
|
|
my $dom = $c->req->dom; |
624
|
|
|
|
|
|
|
my $bar = $c->req->dom('div.bar')->first->text; |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=head2 res |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
my $res = $c->res; |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
Get L object from L"tx">. |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
# Longer version |
633
|
|
|
|
|
|
|
my $res = $c->tx->res; |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
# Force file download by setting a response header |
636
|
|
|
|
|
|
|
$c->res->headers->content_disposition('attachment; filename=foo.png;'); |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# Use a custom response header |
639
|
|
|
|
|
|
|
$c->res->headers->header('Custom-Header' => 'whatever'); |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
# Make sure response is cached correctly |
642
|
|
|
|
|
|
|
$c->res->headers->cache_control('public, max-age=300'); |
643
|
|
|
|
|
|
|
$c->res->headers->append(Vary => 'Accept-Encoding'); |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=head2 send |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
$c = $c->send({binary => $bytes}); |
648
|
|
|
|
|
|
|
$c = $c->send({text => $bytes}); |
649
|
|
|
|
|
|
|
$c = $c->send({json => {test => [1, 2, 3]}}); |
650
|
|
|
|
|
|
|
$c = $c->send([$fin, $rsv1, $rsv2, $rsv3, $op, $payload]); |
651
|
|
|
|
|
|
|
$c = $c->send($chars); |
652
|
|
|
|
|
|
|
$c = $c->send($chars => sub ($c) {...}); |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
Send message or frame non-blocking via WebSocket, the optional drain callback will be executed once all data has been |
655
|
|
|
|
|
|
|
written. This method will automatically respond to WebSocket handshake requests with a C<101> response status, to |
656
|
|
|
|
|
|
|
establish the WebSocket connection. |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
# Send "Text" message |
659
|
|
|
|
|
|
|
$c->send('I ♥ Mojolicious!'); |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
# Send JSON object as "Text" message |
662
|
|
|
|
|
|
|
$c->send({json => {test => 'I ♥ Mojolicious!'}}); |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
# Send JSON object as "Binary" message |
665
|
|
|
|
|
|
|
use Mojo::JSON qw(encode_json); |
666
|
|
|
|
|
|
|
$c->send({binary => encode_json({test => 'I ♥ Mojolicious!'})}); |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
# Send "Ping" frame |
669
|
|
|
|
|
|
|
use Mojo::WebSocket qw(WS_PING); |
670
|
|
|
|
|
|
|
$c->send([1, 0, 0, 0, WS_PING, 'Hello World!']); |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
# Make sure the first message has been written before continuing |
673
|
|
|
|
|
|
|
$c->send('First message!' => sub ($c) { $c->send('Second message!') }); |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
For mostly idle WebSockets you might also want to increase the inactivity timeout with |
676
|
|
|
|
|
|
|
L, which usually defaults to C<30> seconds. |
677
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
# Increase inactivity timeout for connection to 300 seconds |
679
|
|
|
|
|
|
|
$c->inactivity_timeout(300); |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=head2 session |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
my $session = $c->session; |
684
|
|
|
|
|
|
|
my $foo = $c->session('foo'); |
685
|
|
|
|
|
|
|
$c = $c->session({foo => 'bar'}); |
686
|
|
|
|
|
|
|
$c = $c->session(foo => 'bar'); |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
Persistent data storage for the next few requests, all session data gets serialized with L and stored |
689
|
|
|
|
|
|
|
Base64 encoded in HMAC-SHA256 signed cookies, to prevent tampering. Note that cookies usually have a C<4096> byte |
690
|
|
|
|
|
|
|
(4KiB) limit, depending on browser. |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
# Manipulate session |
693
|
|
|
|
|
|
|
$c->session->{foo} = 'bar'; |
694
|
|
|
|
|
|
|
my $foo = $c->session->{foo}; |
695
|
|
|
|
|
|
|
delete $c->session->{foo}; |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
# Expiration date in seconds from now (persists between requests) |
698
|
|
|
|
|
|
|
$c->session(expiration => 604800); |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
# Expiration date as absolute epoch time (only valid for one request) |
701
|
|
|
|
|
|
|
$c->session(expires => time + 604800); |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
# Delete whole session by setting an expiration date in the past |
704
|
|
|
|
|
|
|
$c->session(expires => 1); |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
=head2 signed_cookie |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
my $value = $c->signed_cookie('foo'); |
709
|
|
|
|
|
|
|
$c = $c->signed_cookie(foo => 'bar'); |
710
|
|
|
|
|
|
|
$c = $c->signed_cookie(foo => 'bar', {path => '/'}); |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
Access signed request cookie values and create new signed response cookies. If there are multiple values sharing the |
713
|
|
|
|
|
|
|
same name, and you want to access more than just the last one, you can use L"every_signed_cookie">. Cookies are |
714
|
|
|
|
|
|
|
cryptographically signed with HMAC-SHA256, to prevent tampering, and the ones failing signature verification will be |
715
|
|
|
|
|
|
|
automatically discarded. |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=head2 stash |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
my $hash = $c->stash; |
720
|
|
|
|
|
|
|
my $foo = $c->stash('foo'); |
721
|
|
|
|
|
|
|
$c = $c->stash({foo => 'bar', baz => 23}); |
722
|
|
|
|
|
|
|
$c = $c->stash(foo => 'bar', baz => 23); |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
Non-persistent data storage and exchange for the current request, application wide default values can be set with |
725
|
|
|
|
|
|
|
L. Some stash values have a special meaning and are reserved, the full list is currently |
726
|
|
|
|
|
|
|
C, C, C, C, C, C, C, C, C, C, C, |
727
|
|
|
|
|
|
|
C, C, C, C, C and C. Note that all stash values with a C |
728
|
|
|
|
|
|
|
prefix are reserved for internal use. |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
# Remove value |
731
|
|
|
|
|
|
|
my $foo = delete $c->stash->{foo}; |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
# Assign multiple values at once |
734
|
|
|
|
|
|
|
$c->stash(foo => 'test', bar => 23); |
735
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
=head2 url_for |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
my $url = $c->url_for; |
739
|
|
|
|
|
|
|
my $url = $c->url_for(name => 'sebastian'); |
740
|
|
|
|
|
|
|
my $url = $c->url_for({name => 'sebastian'}); |
741
|
|
|
|
|
|
|
my $url = $c->url_for('test', name => 'sebastian'); |
742
|
|
|
|
|
|
|
my $url = $c->url_for('test', {name => 'sebastian'}); |
743
|
|
|
|
|
|
|
my $url = $c->url_for('/index.html'); |
744
|
|
|
|
|
|
|
my $url = $c->url_for('//example.com/index.html'); |
745
|
|
|
|
|
|
|
my $url = $c->url_for('http://example.com/index.html'); |
746
|
|
|
|
|
|
|
my $url = $c->url_for('mailto:sri@example.com'); |
747
|
|
|
|
|
|
|
my $url = $c->url_for('#whatever'); |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
Generate a portable L object with base for a path, URL or route. |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
# Rebuild URL for the current route |
752
|
|
|
|
|
|
|
$c->url_for; |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
# Rebuild URL for the current route, but replace the "name" placeholder value |
755
|
|
|
|
|
|
|
$c->url_for(name => 'sebastian'); |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
# Absolute URL for the current route |
758
|
|
|
|
|
|
|
$c->url_for->to_abs; |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
# Build URL for route "test" with two placeholder values |
761
|
|
|
|
|
|
|
$c->url_for('test', name => 'sebastian', foo => 'bar'); |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
# "http://127.0.0.1:3000/index.html" if application was started with Morbo |
764
|
|
|
|
|
|
|
$c->url_for('/index.html')->to_abs; |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
# "https://127.0.0.1:443/index.html" if application was started with Morbo |
767
|
|
|
|
|
|
|
$c->url_for('/index.html')->to_abs->scheme('https')->port(443); |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
# "/index.html?foo=bar" if application is deployed under "/" |
770
|
|
|
|
|
|
|
$c->url_for('/index.html')->query(foo => 'bar'); |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
# "/myapp/index.html?foo=bar" if application is deployed under "/myapp" |
773
|
|
|
|
|
|
|
$c->url_for('/index.html')->query(foo => 'bar'); |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
You can also use the helper L to inherit query parameters from the |
776
|
|
|
|
|
|
|
current request. |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
# "/list?q=mojo&page=2" if current request was for "/list?q=mojo&page=1" |
779
|
|
|
|
|
|
|
$c->url_with->query({page => 2}); |
780
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
=head2 url_for_asset |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
my $url = $c->url_for_asset('/app.js'); |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
Generate a portable L object with base for a static asset. |
787
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
=head2 url_for_file |
789
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
my $url = $c->url_for_file('/index.html'); |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
Generate a portable L object with base for a static file. |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
=head2 write |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
$c = $c->write; |
797
|
|
|
|
|
|
|
$c = $c->write(''); |
798
|
|
|
|
|
|
|
$c = $c->write($bytes); |
799
|
|
|
|
|
|
|
$c = $c->write($bytes => sub ($c) {...}); |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
Write dynamic content non-blocking, the optional drain callback will be executed once all data has been written. |
802
|
|
|
|
|
|
|
Calling this method without a chunk of data will finalize the response headers and allow for dynamic content to be |
803
|
|
|
|
|
|
|
written later. |
804
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
# Keep connection alive (with Content-Length header) |
806
|
|
|
|
|
|
|
$c->res->headers->content_length(6); |
807
|
|
|
|
|
|
|
$c->write('Hel' => sub ($c) { $c->write('lo!') }); |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
# Close connection when finished (without Content-Length header) |
810
|
|
|
|
|
|
|
$c->write('Hel' => sub ($c) { |
811
|
|
|
|
|
|
|
$c->write('lo!' => sub ($c) { $c->finish }); |
812
|
|
|
|
|
|
|
}); |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
You can call L"finish"> or write an empty chunk of data at any time to end the stream. |
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
HTTP/1.1 200 OK |
817
|
|
|
|
|
|
|
Date: Sat, 13 Sep 2014 16:48:29 GMT |
818
|
|
|
|
|
|
|
Content-Length: 6 |
819
|
|
|
|
|
|
|
Server: Mojolicious (Perl) |
820
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
Hello! |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
HTTP/1.1 200 OK |
824
|
|
|
|
|
|
|
Connection: close |
825
|
|
|
|
|
|
|
Date: Sat, 13 Sep 2014 16:48:29 GMT |
826
|
|
|
|
|
|
|
Server: Mojolicious (Perl) |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
Hello! |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
For Comet (long polling) you might also want to increase the inactivity timeout with |
831
|
|
|
|
|
|
|
L, which usually defaults to C<30> seconds. |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
# Increase inactivity timeout for connection to 300 seconds |
834
|
|
|
|
|
|
|
$c->inactivity_timeout(300); |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
=head2 write_chunk |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
$c = $c->write_chunk; |
839
|
|
|
|
|
|
|
$c = $c->write_chunk(''); |
840
|
|
|
|
|
|
|
$c = $c->write_chunk($bytes); |
841
|
|
|
|
|
|
|
$c = $c->write_chunk($bytes => sub ($c) {...}); |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
Write dynamic content non-blocking with chunked transfer encoding, the optional drain callback will be executed once |
844
|
|
|
|
|
|
|
all data has been written. Calling this method without a chunk of data will finalize the response headers and allow for |
845
|
|
|
|
|
|
|
dynamic content to be written later. |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
# Make sure previous chunk has been written before continuing |
848
|
|
|
|
|
|
|
$c->write_chunk('H' => sub ($c) { |
849
|
|
|
|
|
|
|
$c->write_chunk('ell' => sub ($c) { $c->finish('o!') }); |
850
|
|
|
|
|
|
|
}); |
851
|
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
You can call L"finish"> or write an empty chunk of data at any time to end the stream. |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
HTTP/1.1 200 OK |
855
|
|
|
|
|
|
|
Date: Sat, 13 Sep 2014 16:48:29 GMT |
856
|
|
|
|
|
|
|
Transfer-Encoding: chunked |
857
|
|
|
|
|
|
|
Server: Mojolicious (Perl) |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
1 |
860
|
|
|
|
|
|
|
H |
861
|
|
|
|
|
|
|
3 |
862
|
|
|
|
|
|
|
ell |
863
|
|
|
|
|
|
|
2 |
864
|
|
|
|
|
|
|
o! |
865
|
|
|
|
|
|
|
0 |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
=head1 HELPERS |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
In addition to the L"ATTRIBUTES"> and L"METHODS"> above you can also call helpers provided by L"app"> on |
870
|
|
|
|
|
|
|
L objects. This includes all helpers from L and |
871
|
|
|
|
|
|
|
L. |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
# Call helpers |
874
|
|
|
|
|
|
|
$c->layout('green'); |
875
|
|
|
|
|
|
|
$c->title('Welcome!'); |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
# Longer version |
878
|
|
|
|
|
|
|
$c->helpers->layout('green'); |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
=head1 SEE ALSO |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
L, L, L. |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
=cut |