add files
|
1 |
package Mojo::Headers; |
2 |
use Mojo::Base -base; |
|
3 | ||
4 |
use Mojo::Util qw(get_line monkey_patch); |
|
5 | ||
6 |
has max_line_size => sub { $ENV{MOJO_MAX_LINE_SIZE} || 10240 }; |
|
7 | ||
8 |
# Common headers |
|
9 |
my @HEADERS = ( |
|
10 |
qw(Accept Accept-Charset Accept-Encoding Accept-Language Accept-Ranges), |
|
11 |
qw(Allow Authorization Cache-Control Connection Content-Disposition), |
|
12 |
qw(Content-Encoding Content-Length Content-Range Content-Type Cookie DNT), |
|
13 |
qw(Date ETag Expect Expires Host If-Modified-Since Last-Modified Link), |
|
14 |
qw(Location Origin Proxy-Authenticate Proxy-Authorization Range), |
|
15 |
qw(Sec-WebSocket-Accept Sec-WebSocket-Extensions Sec-WebSocket-Key), |
|
16 |
qw(Sec-WebSocket-Protocol Sec-WebSocket-Version Server Set-Cookie Status), |
|
17 |
qw(TE Trailer Transfer-Encoding Upgrade User-Agent Vary WWW-Authenticate) |
|
18 |
); |
|
19 |
for my $header (@HEADERS) { |
|
20 |
my $name = lc $header; |
|
21 |
$name =~ s/-/_/g; |
|
22 |
monkey_patch __PACKAGE__, $name, sub { scalar shift->header($header => @_) }; |
|
23 |
} |
|
24 | ||
25 |
# Lowercase headers |
|
26 |
my %NORMALCASE = map { lc($_) => $_ } @HEADERS; |
|
27 | ||
28 |
sub add { |
|
29 |
my ($self, $name) = (shift, shift); |
|
30 | ||
31 |
# Make sure we have a normal case entry for name |
|
32 |
my $key = lc $name; |
|
33 |
$self->{normalcase}{$key} //= $name unless $NORMALCASE{$key}; |
|
34 | ||
35 |
# Add lines |
|
36 |
push @{$self->{headers}{$key}}, map { ref $_ eq 'ARRAY' ? $_ : [$_] } @_; |
|
37 | ||
38 |
return $self; |
|
39 |
} |
|
40 | ||
41 |
sub append { |
|
42 |
my ($self, $name, $value) = @_; |
|
43 |
my $old = $self->header($name); |
|
44 |
return $self->header($name => defined $old ? "$old, $value" : $value); |
|
45 |
} |
|
46 | ||
47 |
sub clone { |
|
48 |
my $self = shift; |
|
49 |
return $self->new->from_hash($self->to_hash(1)); |
|
50 |
} |
|
51 | ||
52 |
sub from_hash { |
|
53 |
my ($self, $hash) = @_; |
|
54 | ||
55 |
# Empty hash deletes all headers |
|
56 |
delete $self->{headers} if keys %{$hash} == 0; |
|
57 | ||
58 |
# Merge |
|
59 |
while (my ($header, $value) = each %$hash) { |
|
60 |
$self->add($header => ref $value eq 'ARRAY' ? @$value : $value); |
|
61 |
} |
|
62 | ||
63 |
return $self; |
|
64 |
} |
|
65 | ||
66 |
sub header { |
|
67 |
my ($self, $name) = (shift, shift); |
|
68 | ||
69 |
# Replace |
|
70 |
return $self->remove($name)->add($name, @_) if @_; |
|
71 | ||
72 |
# String |
|
73 |
return unless my $headers = $self->{headers}{lc $name}; |
|
74 |
return join ', ', map { join ', ', @$_ } @$headers unless wantarray; |
|
75 | ||
76 |
# Array |
|
77 |
return @$headers; |
|
78 |
} |
|
79 | ||
80 |
sub is_finished { (shift->{state} // '') eq 'finished' } |
|
81 | ||
82 |
sub is_limit_exceeded { !!shift->{limit} } |
|
83 | ||
84 |
sub leftovers { delete shift->{buffer} } |
|
85 | ||
86 |
sub names { |
|
87 |
my $self = shift; |
|
88 |
return [map { $NORMALCASE{$_} || $self->{normalcase}{$_} || $_ } |
|
89 |
keys %{$self->{headers}}]; |
|
90 |
} |
|
91 | ||
92 |
sub parse { |
|
93 |
my $self = shift; |
|
94 | ||
95 |
$self->{state} = 'headers'; |
|
96 |
$self->{buffer} .= shift // ''; |
|
97 |
my $headers = $self->{cache} ||= []; |
|
98 |
my $max = $self->max_line_size; |
|
99 |
while (defined(my $line = get_line \$self->{buffer})) { |
|
100 | ||
101 |
# Check line size limit |
|
102 |
if (length $line > $max) { |
|
103 |
$self->{limit} = $self->{state} = 'finished'; |
|
104 |
return $self; |
|
105 |
} |
|
106 | ||
107 |
# New header |
|
108 |
if ($line =~ /^(\S+)\s*:\s*(.*)$/) { push @$headers, $1, [$2] } |
|
109 | ||
110 |
# Multiline |
|
111 |
elsif (@$headers && $line =~ s/^\s+//) { push @{$headers->[-1]}, $line } |
|
112 | ||
113 |
# Empty line |
|
114 |
else { |
|
115 |
$self->add(splice @$headers, 0, 2) while @$headers; |
|
116 |
$self->{state} = 'finished'; |
|
117 |
return $self; |
|
118 |
} |
|
119 |
} |
|
120 | ||
121 |
# Check line size limit |
|
122 |
$self->{limit} = $self->{state} = 'finished' |
|
123 |
if length $self->{buffer} > $max; |
|
124 | ||
125 |
return $self; |
|
126 |
} |
|
127 | ||
128 |
sub referrer { scalar shift->header(Referer => @_) } |
|
129 | ||
130 |
sub remove { |
|
131 |
my ($self, $name) = @_; |
|
132 |
delete $self->{headers}{lc $name}; |
|
133 |
return $self; |
|
134 |
} |
|
135 | ||
136 |
sub to_hash { |
|
137 |
my ($self, $multi) = @_; |
|
138 |
my %hash; |
|
139 |
$hash{$_} = $multi ? [$self->header($_)] : scalar $self->header($_) |
|
140 |
for @{$self->names}; |
|
141 |
return \%hash; |
|
142 |
} |
|
143 | ||
144 |
sub to_string { |
|
145 |
my $self = shift; |
|
146 | ||
147 |
# Make sure multiline values are formatted correctly |
|
148 |
my @headers; |
|
149 |
for my $name (@{$self->names}) { |
|
150 |
push @headers, "$name: " . join("\x0d\x0a ", @$_) for $self->header($name); |
|
151 |
} |
|
152 | ||
153 |
return join "\x0d\x0a", @headers; |
|
154 |
} |
|
155 | ||
156 |
1; |
|
157 | ||
158 |
=encoding utf8 |
|
159 | ||
160 |
=head1 NAME |
|
161 | ||
162 |
Mojo::Headers - Headers |
|
163 | ||
164 |
=head1 SYNOPSIS |
|
165 | ||
166 |
use Mojo::Headers; |
|
167 | ||
168 |
# Parse |
|
169 |
my $headers = Mojo::Headers->new; |
|
170 |
$headers->parse("Content-Length: 42\x0d\x0a"); |
|
171 |
$headers->parse("Content-Type: text/html\x0d\x0a\x0d\x0a"); |
|
172 |
say $headers->content_length; |
|
173 |
say $headers->content_type; |
|
174 | ||
175 |
# Build |
|
176 |
my $headers = Mojo::Headers->new; |
|
177 |
$headers->content_length(42); |
|
178 |
$headers->content_type('text/plain'); |
|
179 |
say $headers->to_string; |
|
180 | ||
181 |
=head1 DESCRIPTION |
|
182 | ||
183 |
L<Mojo::Headers> is a container for HTTP headers as described in RFC 2616. |
|
184 | ||
185 |
=head1 ATTRIBUTES |
|
186 | ||
187 |
L<Mojo::Headers> implements the following attributes. |
|
188 | ||
189 |
=head2 max_line_size |
|
190 | ||
191 |
my $size = $headers->max_line_size; |
|
192 |
$headers = $headers->max_line_size(1024); |
|
193 | ||
194 |
Maximum header line size in bytes, defaults to the value of the |
|
195 |
MOJO_MAX_LINE_SIZE environment variable or C<10240>. |
|
196 | ||
197 |
=head1 METHODS |
|
198 | ||
199 |
L<Mojo::Headers> inherits all methods from L<Mojo::Base> and implements the |
|
200 |
following new ones. |
|
201 | ||
202 |
=head2 accept |
|
203 | ||
204 |
my $accept = $headers->accept; |
|
205 |
$headers = $headers->accept('application/json'); |
|
206 | ||
207 |
Shortcut for the C<Accept> header. |
|
208 | ||
209 |
=head2 accept_charset |
|
210 | ||
211 |
my $charset = $headers->accept_charset; |
|
212 |
$headers = $headers->accept_charset('UTF-8'); |
|
213 | ||
214 |
Shortcut for the C<Accept-Charset> header. |
|
215 | ||
216 |
=head2 accept_encoding |
|
217 | ||
218 |
my $encoding = $headers->accept_encoding; |
|
219 |
$headers = $headers->accept_encoding('gzip'); |
|
220 | ||
221 |
Shortcut for the C<Accept-Encoding> header. |
|
222 | ||
223 |
=head2 accept_language |
|
224 | ||
225 |
my $language = $headers->accept_language; |
|
226 |
$headers = $headers->accept_language('de, en'); |
|
227 | ||
228 |
Shortcut for the C<Accept-Language> header. |
|
229 | ||
230 |
=head2 accept_ranges |
|
231 | ||
232 |
my $ranges = $headers->accept_ranges; |
|
233 |
$headers = $headers->accept_ranges('bytes'); |
|
234 | ||
235 |
Shortcut for the C<Accept-Ranges> header. |
|
236 | ||
237 |
=head2 add |
|
238 | ||
239 |
$headers = $headers->add(Foo => 'one value'); |
|
240 |
$headers = $headers->add(Foo => 'first value', 'second value'); |
|
241 |
$headers = $headers->add(Foo => ['first line', 'second line']); |
|
242 | ||
243 |
Add one or more header values with one or more lines. |
|
244 | ||
245 |
# "Vary: Accept" |
|
246 |
# "Vary: Accept-Encoding" |
|
247 |
$headers->vary('Accept')->add(Vary => 'Accept-Encoding')->to_string; |
|
248 | ||
249 |
=head2 allow |
|
250 | ||
251 |
my $allow = $headers->allow; |
|
252 |
$headers = $headers->allow('GET, POST'); |
|
253 | ||
254 |
Shortcut for the C<Allow> header. |
|
255 | ||
256 |
=head2 append |
|
257 | ||
258 |
$headers = $headers->append(Vary => 'Accept-Encoding'); |
|
259 | ||
260 |
Append value to header and flatten it if necessary. |
|
261 | ||
262 |
# "Vary: Accept" |
|
263 |
$headers->append(Vary => 'Accept')->to_string; |
|
264 | ||
265 |
# "Vary: Accept, Accept-Encoding" |
|
266 |
$headers->vary('Accept')->append(Vary => 'Accept-Encoding')->to_string; |
|
267 | ||
268 |
=head2 authorization |
|
269 | ||
270 |
my $authorization = $headers->authorization; |
|
271 |
$headers = $headers->authorization('Basic Zm9vOmJhcg=='); |
|
272 | ||
273 |
Shortcut for the C<Authorization> header. |
|
274 | ||
275 |
=head2 cache_control |
|
276 | ||
277 |
my $cache_control = $headers->cache_control; |
|
278 |
$headers = $headers->cache_control('max-age=1, no-cache'); |
|
279 | ||
280 |
Shortcut for the C<Cache-Control> header. |
|
281 | ||
282 |
=head2 clone |
|
283 | ||
284 |
my $clone = $headers->clone; |
|
285 | ||
286 |
Clone headers. |
|
287 | ||
288 |
=head2 connection |
|
289 | ||
290 |
my $connection = $headers->connection; |
|
291 |
$headers = $headers->connection('close'); |
|
292 | ||
293 |
Shortcut for the C<Connection> header. |
|
294 | ||
295 |
=head2 content_disposition |
|
296 | ||
297 |
my $disposition = $headers->content_disposition; |
|
298 |
$headers = $headers->content_disposition('foo'); |
|
299 | ||
300 |
Shortcut for the C<Content-Disposition> header. |
|
301 | ||
302 |
=head2 content_encoding |
|
303 | ||
304 |
my $encoding = $headers->content_encoding; |
|
305 |
$headers = $headers->content_encoding('gzip'); |
|
306 | ||
307 |
Shortcut for the C<Content-Encoding> header. |
|
308 | ||
309 |
=head2 content_length |
|
310 | ||
311 |
my $len = $headers->content_length; |
|
312 |
$headers = $headers->content_length(4000); |
|
313 | ||
314 |
Shortcut for the C<Content-Length> header. |
|
315 | ||
316 |
=head2 content_range |
|
317 | ||
318 |
my $range = $headers->content_range; |
|
319 |
$headers = $headers->content_range('bytes 2-8/100'); |
|
320 | ||
321 |
Shortcut for the C<Content-Range> header. |
|
322 | ||
323 |
=head2 content_type |
|
324 | ||
325 |
my $type = $headers->content_type; |
|
326 |
$headers = $headers->content_type('text/plain'); |
|
327 | ||
328 |
Shortcut for the C<Content-Type> header. |
|
329 | ||
330 |
=head2 cookie |
|
331 | ||
332 |
my $cookie = $headers->cookie; |
|
333 |
$headers = $headers->cookie('f=b'); |
|
334 | ||
335 |
Shortcut for the C<Cookie> header from RFC 6265. |
|
336 | ||
337 |
=head2 date |
|
338 | ||
339 |
my $date = $headers->date; |
|
340 |
$headers = $headers->date('Sun, 17 Aug 2008 16:27:35 GMT'); |
|
341 | ||
342 |
Shortcut for the C<Date> header. |
|
343 | ||
344 |
=head2 dnt |
|
345 | ||
346 |
my $dnt = $headers->dnt; |
|
347 |
$headers = $headers->dnt(1); |
|
348 | ||
349 |
Shortcut for the C<DNT> (Do Not Track) header, which has no specification yet, |
|
350 |
but is very commonly used. |
|
351 | ||
352 |
=head2 etag |
|
353 | ||
354 |
my $etag = $headers->etag; |
|
355 |
$headers = $headers->etag('abc321'); |
|
356 | ||
357 |
Shortcut for the C<ETag> header. |
|
358 | ||
359 |
=head2 expect |
|
360 | ||
361 |
my $expect = $headers->expect; |
|
362 |
$headers = $headers->expect('100-continue'); |
|
363 | ||
364 |
Shortcut for the C<Expect> header. |
|
365 | ||
366 |
=head2 expires |
|
367 | ||
368 |
my $expires = $headers->expires; |
|
369 |
$headers = $headers->expires('Thu, 01 Dec 1994 16:00:00 GMT'); |
|
370 | ||
371 |
Shortcut for the C<Expires> header. |
|
372 | ||
373 |
=head2 from_hash |
|
374 | ||
375 |
$headers = $headers->from_hash({'Content-Type' => 'text/html'}); |
|
376 |
$headers = $headers->from_hash({}); |
|
377 | ||
378 |
Parse headers from a hash reference, an empty hash removes all headers. |
|
379 | ||
380 |
=head2 header |
|
381 | ||
382 |
my $value = $headers->header('Foo'); |
|
383 |
my @values = $headers->header('Foo'); |
|
384 |
$headers = $headers->header(Foo => 'one value'); |
|
385 |
$headers = $headers->header(Foo => 'first value', 'second value'); |
|
386 |
$headers = $headers->header(Foo => ['first line', 'second line']); |
|
387 | ||
388 |
Get or replace the current header values. |
|
389 | ||
390 |
# Multiple headers with the same name |
|
391 |
for my $header ($headers->header('Set-Cookie')) { |
|
392 |
say 'Set-Cookie:'; |
|
393 | ||
394 |
# Multiple lines per header |
|
395 |
say for @$header; |
|
396 |
} |
|
397 | ||
398 |
=head2 host |
|
399 | ||
400 |
my $host = $headers->host; |
|
401 |
$headers = $headers->host('127.0.0.1'); |
|
402 | ||
403 |
Shortcut for the C<Host> header. |
|
404 | ||
405 |
=head2 if_modified_since |
|
406 | ||
407 |
my $date = $headers->if_modified_since; |
|
408 |
$headers = $headers->if_modified_since('Sun, 17 Aug 2008 16:27:35 GMT'); |
|
409 | ||
410 |
Shortcut for the C<If-Modified-Since> header. |
|
411 | ||
412 |
=head2 is_finished |
|
413 | ||
414 |
my $bool = $headers->is_finished; |
|
415 | ||
416 |
Check if header parser is finished. |
|
417 | ||
418 |
=head2 is_limit_exceeded |
|
419 | ||
420 |
my $bool = $headers->is_limit_exceeded; |
|
421 | ||
422 |
Check if a header has exceeded C<max_line_size>. |
|
423 | ||
424 |
=head2 last_modified |
|
425 | ||
426 |
my $date = $headers->last_modified; |
|
427 |
$headers = $headers->last_modified('Sun, 17 Aug 2008 16:27:35 GMT'); |
|
428 | ||
429 |
Shortcut for the C<Last-Modified> header. |
|
430 | ||
431 |
=head2 leftovers |
|
432 | ||
433 |
my $bytes = $headers->leftovers; |
|
434 | ||
435 |
Get leftover data from header parser. |
|
436 | ||
437 |
=head2 link |
|
438 | ||
439 |
my $link = $headers->link; |
|
440 |
$headers = $headers->link('<http://127.0.0.1/foo/3>; rel="next"'); |
|
441 | ||
442 |
Shortcut for the C<Link> header from RFC 5988. |
|
443 | ||
444 |
=head2 location |
|
445 | ||
446 |
my $location = $headers->location; |
|
447 |
$headers = $headers->location('http://127.0.0.1/foo'); |
|
448 | ||
449 |
Shortcut for the C<Location> header. |
|
450 | ||
451 |
=head2 names |
|
452 | ||
453 |
my $names = $headers->names; |
|
454 | ||
455 |
Return a list of all currently defined headers. |
|
456 | ||
457 |
# Names of all headers |
|
458 |
say for @{$headers->names}; |
|
459 | ||
460 |
=head2 origin |
|
461 | ||
462 |
my $origin = $headers->origin; |
|
463 |
$headers = $headers->origin('http://example.com'); |
|
464 | ||
465 |
Shortcut for the C<Origin> header from RFC 6454. |
|
466 | ||
467 |
=head2 parse |
|
468 | ||
469 |
$headers = $headers->parse("Content-Type: text/plain\x0d\x0a\x0d\x0a"); |
|
470 | ||
471 |
Parse formatted headers. |
|
472 | ||
473 |
=head2 proxy_authenticate |
|
474 | ||
475 |
my $authenticate = $headers->proxy_authenticate; |
|
476 |
$headers = $headers->proxy_authenticate('Basic "realm"'); |
|
477 | ||
478 |
Shortcut for the C<Proxy-Authenticate> header. |
|
479 | ||
480 |
=head2 proxy_authorization |
|
481 | ||
482 |
my $authorization = $headers->proxy_authorization; |
|
483 |
$headers = $headers->proxy_authorization('Basic Zm9vOmJhcg=='); |
|
484 | ||
485 |
Shortcut for the C<Proxy-Authorization> header. |
|
486 | ||
487 |
=head2 range |
|
488 | ||
489 |
my $range = $headers->range; |
|
490 |
$headers = $headers->range('bytes=2-8'); |
|
491 | ||
492 |
Shortcut for the C<Range> header. |
|
493 | ||
494 |
=head2 referrer |
|
495 | ||
496 |
my $referrer = $headers->referrer; |
|
497 |
$headers = $headers->referrer('http://example.com'); |
|
498 | ||
499 |
Shortcut for the C<Referer> header, there was a typo in RFC 2068 which |
|
500 |
resulted in C<Referer> becoming an official header. |
|
501 | ||
502 |
=head2 remove |
|
503 | ||
504 |
$headers = $headers->remove('Foo'); |
|
505 | ||
506 |
Remove a header. |
|
507 | ||
508 |
=head2 sec_websocket_accept |
|
509 | ||
510 |
my $accept = $headers->sec_websocket_accept; |
|
511 |
$headers = $headers->sec_websocket_accept('s3pPLMBiTxaQ9kYGzzhZRbK+xOo='); |
|
512 | ||
513 |
Shortcut for the C<Sec-WebSocket-Accept> header from RFC 6455. |
|
514 | ||
515 |
=head2 sec_websocket_extensions |
|
516 | ||
517 |
my $extensions = $headers->sec_websocket_extensions; |
|
518 |
$headers = $headers->sec_websocket_extensions('foo'); |
|
519 | ||
520 |
Shortcut for the C<Sec-WebSocket-Extensions> header from RFC 6455. |
|
521 | ||
522 |
=head2 sec_websocket_key |
|
523 | ||
524 |
my $key = $headers->sec_websocket_key; |
|
525 |
$headers = $headers->sec_websocket_key('dGhlIHNhbXBsZSBub25jZQ=='); |
|
526 | ||
527 |
Shortcut for the C<Sec-WebSocket-Key> header from RFC 6455. |
|
528 | ||
529 |
=head2 sec_websocket_protocol |
|
530 | ||
531 |
my $proto = $headers->sec_websocket_protocol; |
|
532 |
$headers = $headers->sec_websocket_protocol('sample'); |
|
533 | ||
534 |
Shortcut for the C<Sec-WebSocket-Protocol> header from RFC 6455. |
|
535 | ||
536 |
=head2 sec_websocket_version |
|
537 | ||
538 |
my $version = $headers->sec_websocket_version; |
|
539 |
$headers = $headers->sec_websocket_version(13); |
|
540 | ||
541 |
Shortcut for the C<Sec-WebSocket-Version> header from RFC 6455. |
|
542 | ||
543 |
=head2 server |
|
544 | ||
545 |
my $server = $headers->server; |
|
546 |
$headers = $headers->server('Mojo'); |
|
547 | ||
548 |
Shortcut for the C<Server> header. |
|
549 | ||
550 |
=head2 set_cookie |
|
551 | ||
552 |
my $cookie = $headers->set_cookie; |
|
553 |
$headers = $headers->set_cookie('f=b; path=/'); |
|
554 | ||
555 |
Shortcut for the C<Set-Cookie> header from RFC 6265. |
|
556 | ||
557 |
=head2 status |
|
558 | ||
559 |
my $status = $headers->status; |
|
560 |
$headers = $headers->status('200 OK'); |
|
561 | ||
562 |
Shortcut for the C<Status> header from RFC 3875. |
|
563 | ||
564 |
=head2 te |
|
565 | ||
566 |
my $te = $headers->te; |
|
567 |
$headers = $headers->te('chunked'); |
|
568 | ||
569 |
Shortcut for the C<TE> header. |
|
570 | ||
571 |
=head2 to_hash |
|
572 | ||
573 |
my $single = $headers->to_hash; |
|
574 |
my $multi = $headers->to_hash(1); |
|
575 | ||
576 |
Turn headers into hash reference, nested array references to represent |
|
577 |
multiline values are disabled by default. |
|
578 | ||
579 |
say $headers->to_hash->{DNT}; |
|
580 | ||
581 |
=head2 to_string |
|
582 | ||
583 |
my $str = $headers->to_string; |
|
584 | ||
585 |
Turn headers into a string, suitable for HTTP messages. |
|
586 | ||
587 |
=head2 trailer |
|
588 | ||
589 |
my $trailer = $headers->trailer; |
|
590 |
$headers = $headers->trailer('X-Foo'); |
|
591 | ||
592 |
Shortcut for the C<Trailer> header. |
|
593 | ||
594 |
=head2 transfer_encoding |
|
595 | ||
596 |
my $encoding = $headers->transfer_encoding; |
|
597 |
$headers = $headers->transfer_encoding('chunked'); |
|
598 | ||
599 |
Shortcut for the C<Transfer-Encoding> header. |
|
600 | ||
601 |
=head2 upgrade |
|
602 | ||
603 |
my $upgrade = $headers->upgrade; |
|
604 |
$headers = $headers->upgrade('websocket'); |
|
605 | ||
606 |
Shortcut for the C<Upgrade> header. |
|
607 | ||
608 |
=head2 user_agent |
|
609 | ||
610 |
my $agent = $headers->user_agent; |
|
611 |
$headers = $headers->user_agent('Mojo/1.0'); |
|
612 | ||
613 |
Shortcut for the C<User-Agent> header. |
|
614 | ||
615 |
=head2 vary |
|
616 | ||
617 |
my $vary = $headers->vary; |
|
618 |
$headers = $headers->vary('*'); |
|
619 | ||
620 |
Shortcut for the C<Vary> header. |
|
621 | ||
622 |
=head2 www_authenticate |
|
623 | ||
624 |
my $authenticate = $headers->www_authenticate; |
|
625 |
$headers = $headers->www_authenticate('Basic realm="realm"'); |
|
626 | ||
627 |
Shortcut for the C<WWW-Authenticate> header. |
|
628 | ||
629 |
=head1 SEE ALSO |
|
630 | ||
631 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
|
632 | ||
633 |
=cut |