Newer Older
633 lines | 14.601kb
add files
Yuki Kimoto authored on 2014-03-26
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