Newer Older
642 lines | 15.568kb
add files
Yuki Kimoto authored on 2014-03-26
1
package Mojo::Message;
2
use Mojo::Base 'Mojo::EventEmitter';
3

            
4
use Carp 'croak';
5
use Mojo::Asset::Memory;
6
use Mojo::Content::Single;
7
use Mojo::DOM;
8
use Mojo::JSON;
9
use Mojo::JSON::Pointer;
10
use Mojo::Parameters;
11
use Mojo::Upload;
12
use Mojo::Util 'decode';
13

            
14
has content => sub { Mojo::Content::Single->new };
15
has default_charset  => 'UTF-8';
16
has max_line_size    => sub { $ENV{MOJO_MAX_LINE_SIZE} || 10240 };
17
has max_message_size => sub { $ENV{MOJO_MAX_MESSAGE_SIZE} // 10485760 };
18
has version          => '1.1';
19

            
20
sub body {
21
  my $self = shift;
22

            
23
  # Downgrade multipart content
24
  my $content = $self->content;
25
  $content = $self->content(Mojo::Content::Single->new)->content
26
    if $content->is_multipart;
27

            
28
  # Get
29
  return $content->asset->slurp unless @_;
30

            
31
  # Set
32
  $content->asset(Mojo::Asset::Memory->new->add_chunk(@_));
33

            
34
  return $self;
35
}
36

            
37
sub body_params {
38
  my $self = shift;
39

            
40
  return $self->{body_params} if $self->{body_params};
41
  my $params = $self->{body_params} = Mojo::Parameters->new;
42
  $params->charset($self->content->charset || $self->default_charset);
43

            
44
  # "application/x-www-form-urlencoded"
45
  my $type = $self->headers->content_type // '';
46
  if ($type =~ m!application/x-www-form-urlencoded!i) {
47
    $params->parse($self->content->asset->slurp);
48
  }
49

            
50
  # "multipart/form-data"
51
  elsif ($type =~ m!multipart/form-data!i) {
52
    $params->append(@$_[0, 1]) for @{$self->_parse_formdata};
53
  }
54

            
55
  return $params;
56
}
57

            
58
sub body_size { shift->content->body_size }
59

            
60
sub build_body       { shift->_build('get_body_chunk') }
61
sub build_headers    { shift->_build('get_header_chunk') }
62
sub build_start_line { shift->_build('get_start_line_chunk') }
63

            
64
sub cookie { shift->_cache(cookies => @_) }
65

            
66
sub cookies { croak 'Method "cookies" not implemented by subclass' }
67

            
68
sub dom {
69
  my $self = shift;
70
  return undef if $self->content->is_multipart;
71
  my $dom = $self->{dom} ||= Mojo::DOM->new($self->text);
72
  return @_ ? $dom->find(@_) : $dom;
73
}
74

            
75
sub error {
76
  my $self = shift;
77

            
78
  # Set
79
  if (@_) {
80
    $self->{error} = [@_];
81
    return $self->finish;
82
  }
83

            
84
  # Get
85
  return unless my $err = $self->{error};
86
  return wantarray ? @$err : $err->[0];
87
}
88

            
89
sub extract_start_line {
90
  croak 'Method "extract_start_line" not implemented by subclass';
91
}
92

            
93
sub finish {
94
  my $self = shift;
95
  $self->{state} = 'finished';
96
  return $self->{finished}++ ? $self : $self->emit('finish');
97
}
98

            
99
sub fix_headers {
100
  my $self = shift;
101

            
102
  # Content-Length or Connection (unless chunked transfer encoding is used)
103
  my $content = $self->content;
104
  return $self if $self->{fix}++ || $content->is_chunked;
105
  my $headers = $self->headers;
106
  $content->is_dynamic
107
    ? $headers->connection('close')
108
    : $headers->content_length($self->body_size)
109
    unless $headers->content_length;
110

            
111
  return $self;
112
}
113

            
114
sub get_body_chunk {
115
  my ($self, $offset) = @_;
116

            
117
  $self->emit('progress', 'body', $offset);
118
  my $chunk = $self->content->get_body_chunk($offset);
119
  return $chunk if !defined $chunk || length $chunk;
120
  $self->finish;
121

            
122
  return $chunk;
123
}
124

            
125
sub get_header_chunk {
126
  my ($self, $offset) = @_;
127
  $self->emit('progress', 'headers', $offset);
128
  return $self->fix_headers->content->get_header_chunk($offset);
129
}
130

            
131
sub get_start_line_chunk {
132
  croak 'Method "get_start_line_chunk" not implemented by subclass';
133
}
134

            
135
sub header_size { shift->fix_headers->content->header_size }
136

            
137
sub headers { shift->content->headers }
138

            
139
sub is_finished { (shift->{state} // '') eq 'finished' }
140

            
141
sub is_limit_exceeded { !!shift->{limit} }
142

            
143
sub json {
144
  my ($self, $pointer) = @_;
145
  return undef if $self->content->is_multipart;
146
  my $data = $self->{json} ||= Mojo::JSON->new->decode($self->body);
147
  return $pointer ? Mojo::JSON::Pointer->new->get($data, $pointer) : $data;
148
}
149

            
150
sub param { shift->body_params->param(@_) }
151

            
152
sub parse {
153
  my ($self, $chunk) = @_;
154

            
155
  # Check message size
156
  my $max = $self->max_message_size;
157
  return $self->_limit('Maximum message size exceeded', 413)
158
    if $max && ($self->{raw_size} += length($chunk //= '')) > $max;
159

            
160
  $self->{buffer} .= $chunk;
161

            
162
  # Start line
163
  unless ($self->{state}) {
164

            
165
    # Check line size
166
    my $len = index $self->{buffer}, "\x0a";
167
    $len = length $self->{buffer} if $len < 0;
168
    return $self->_limit('Maximum line size exceeded', 431)
169
      if $len > $self->max_line_size;
170

            
171
    $self->{state} = 'content' if $self->extract_start_line(\$self->{buffer});
172
  }
173

            
174
  # Content
175
  my $state = $self->{state} // '';
176
  $self->content($self->content->parse(delete $self->{buffer}))
177
    if $state eq 'content' || $state eq 'finished';
178

            
179
  # Check line size
180
  return $self->_limit('Maximum line size exceeded', 431)
181
    if $self->headers->is_limit_exceeded;
182

            
183
  # Check buffer size
184
  return $self->error('Maximum buffer size exceeded', 400)
185
    if $self->content->is_limit_exceeded;
186

            
187
  return $self->emit('progress')->content->is_finished ? $self->finish : $self;
188
}
189

            
190
sub start_line_size { length shift->build_start_line }
191

            
192
sub text {
193
  my $self    = shift;
194
  my $body    = $self->body;
195
  my $charset = $self->content->charset;
196
  return $charset ? decode($charset, $body) // $body : $body;
197
}
198

            
199
sub to_string {
200
  my $self = shift;
201
  return $self->build_start_line . $self->build_headers . $self->build_body;
202
}
203

            
204
sub upload { shift->_cache(uploads => @_) }
205

            
206
sub uploads {
207
  my $self = shift;
208

            
209
  my @uploads;
210
  for my $data (@{$self->_parse_formdata(1)}) {
211
    my $upload = Mojo::Upload->new(
212
      name     => $data->[0],
213
      filename => $data->[2],
214
      asset    => $data->[1]->asset,
215
      headers  => $data->[1]->headers
216
    );
217
    push @uploads, $upload;
218
  }
219

            
220
  return \@uploads;
221
}
222

            
223
sub _build {
224
  my ($self, $method) = @_;
225

            
226
  my $buffer = '';
227
  my $offset = 0;
228
  while (1) {
229

            
230
    # No chunk yet, try again
231
    next unless defined(my $chunk = $self->$method($offset));
232

            
233
    # End of part
234
    last unless my $len = length $chunk;
235

            
236
    $offset += $len;
237
    $buffer .= $chunk;
238
  }
239

            
240
  return $buffer;
241
}
242

            
243
sub _cache {
244
  my ($self, $method, $name) = @_;
245

            
246
  # Cache objects by name
247
  unless ($self->{$method}) {
248
    $self->{$method} = {};
249
    push @{$self->{$method}{$_->name}}, $_ for @{$self->$method};
250
  }
251

            
252
  return unless my $objects = $self->{$method}{$name};
253
  return wantarray ? @$objects : $objects->[0];
254
}
255

            
256
sub _limit {
257
  my $self = shift;
258
  $self->{limit} = 1;
259
  $self->error(@_);
260
}
261

            
262
sub _parse_formdata {
263
  my ($self, $upload) = @_;
264

            
265
  my @formdata;
266
  my $content = $self->content;
267
  return \@formdata unless $content->is_multipart;
268
  my $charset = $content->charset || $self->default_charset;
269

            
270
  # Check all parts recursively
271
  my @parts = ($content);
272
  while (my $part = shift @parts) {
273

            
274
    if ($part->is_multipart) {
275
      unshift @parts, @{$part->parts};
276
      next;
277
    }
278

            
279
    next unless my $disposition = $part->headers->content_disposition;
280
    my ($filename) = $disposition =~ /[; ]filename\s*=\s*"?((?:\\"|[^"])*)"?/i;
281
    next if ($upload && !defined $filename) || (!$upload && defined $filename);
282
    my ($name) = $disposition =~ /[; ]name\s*=\s*"?((?:\\"|[^";])+)"?/i;
283
    if ($charset) {
284
      $name     = decode($charset, $name)     // $name     if $name;
285
      $filename = decode($charset, $filename) // $filename if $filename;
286
    }
287

            
288
    unless ($upload) {
289
      $part = $part->asset->slurp;
290
      $part = decode($charset, $part) // $part if $charset;
291
    }
292

            
293
    push @formdata, [$name, $part, $filename];
294
  }
295

            
296
  return \@formdata;
297
}
298

            
299
1;
300

            
301
=encoding utf8
302

            
303
=head1 NAME
304

            
305
Mojo::Message - HTTP message base class
306

            
307
=head1 SYNOPSIS
308

            
309
  package Mojo::Message::MyMessage;
310
  use Mojo::Base 'Mojo::Message';
311

            
312
  sub cookies              {...}
313
  sub extract_start_line   {...}
314
  sub get_start_line_chunk {...}
315

            
316
=head1 DESCRIPTION
317

            
318
L<Mojo::Message> is an abstract base class for HTTP messages as described in
319
RFC 2616 and RFC 2388.
320

            
321
=head1 EVENTS
322

            
323
L<Mojo::Message> inherits all events from L<Mojo::EventEmitter> and can emit
324
the following new ones.
325

            
326
=head2 finish
327

            
328
  $msg->on(finish => sub {
329
    my $msg = shift;
330
    ...
331
  });
332

            
333
Emitted after message building or parsing is finished.
334

            
335
  my $before = time;
336
  $msg->on(finish => sub {
337
    my $msg = shift;
338
    $msg->headers->header('X-Parser-Time' => time - $before);
339
  });
340

            
341
=head2 progress
342

            
343
  $msg->on(progress => sub {
344
    my $msg = shift;
345
    ...
346
  });
347

            
348
Emitted when message building or parsing makes progress.
349

            
350
  # Building
351
  $msg->on(progress => sub {
352
    my ($msg, $state, $offset) = @_;
353
    say qq{Building "$state" at offset $offset};
354
  });
355

            
356
  # Parsing
357
  $msg->on(progress => sub {
358
    my $msg = shift;
359
    return unless my $len = $msg->headers->content_length;
360
    my $size = $msg->content->progress;
361
    say 'Progress: ', $size == $len ? 100 : int($size / ($len / 100)), '%';
362
  });
363

            
364
=head1 ATTRIBUTES
365

            
366
L<Mojo::Message> implements the following attributes.
367

            
368
=head2 content
369

            
370
  my $msg = $msg->content;
371
  $msg    = $msg->content(Mojo::Content::Single->new);
372

            
373
Message content, defaults to a L<Mojo::Content::Single> object.
374

            
375
=head2 default_charset
376

            
377
  my $charset = $msg->default_charset;
378
  $msg        = $msg->default_charset('UTF-8');
379

            
380
Default charset used for form-data parsing, defaults to C<UTF-8>.
381

            
382
=head2 max_line_size
383

            
384
  my $size = $msg->max_line_size;
385
  $msg     = $msg->max_line_size(1024);
386

            
387
Maximum start line size in bytes, defaults to the value of the
388
MOJO_MAX_LINE_SIZE environment variable or C<10240>.
389

            
390
=head2 max_message_size
391

            
392
  my $size = $msg->max_message_size;
393
  $msg     = $msg->max_message_size(1024);
394

            
395
Maximum message size in bytes, defaults to the value of the
396
MOJO_MAX_MESSAGE_SIZE environment variable or C<10485760>. Setting the value
397
to C<0> will allow messages of indefinite size. Note that increasing this
398
value can also drastically increase memory usage, should you for example
399
attempt to parse an excessively large message body with the L</"body_params">,
400
L</"dom"> or L</"json"> methods.
401

            
402
=head2 version
403

            
404
  my $version = $msg->version;
405
  $msg        = $msg->version('1.1');
406

            
407
HTTP version of message, defaults to C<1.1>.
408

            
409
=head1 METHODS
410

            
411
L<Mojo::Message> inherits all methods from L<Mojo::EventEmitter> and
412
implements the following new ones.
413

            
414
=head2 body
415

            
416
  my $bytes = $msg->body;
417
  $msg      = $msg->body('Hello!');
418

            
419
Slurp or replace L</"content">, L<Mojo::Content::MultiPart> will be
420
automatically downgraded to L<Mojo::Content::Single>.
421

            
422
=head2 body_params
423

            
424
  my $params = $msg->body_params;
425

            
426
POST parameters extracted from C<application/x-www-form-urlencoded> or
427
C<multipart/form-data> message body, usually a L<Mojo::Parameters> object.
428
Note that this method caches all data, so it should not be called before the
429
entire message body has been received. Parts of the message body need to be
430
loaded into memory to parse POST parameters, so you have to make sure it is
431
not excessively large.
432

            
433
  # Get POST parameter value
434
  say $msg->body_params->param('foo');
435

            
436
=head2 body_size
437

            
438
  my $size = $msg->body_size;
439

            
440
Content size in bytes.
441

            
442
=head2 build_body
443

            
444
  my $bytes = $msg->build_body;
445

            
446
Render whole body.
447

            
448
=head2 build_headers
449

            
450
  my $bytes = $msg->build_headers;
451

            
452
Render all headers.
453

            
454
=head2 build_start_line
455

            
456
  my $bytes = $msg->build_start_line;
457

            
458
Render start line.
459

            
460
=head2 cookie
461

            
462
  my $cookie  = $msg->cookie('foo');
463
  my @cookies = $msg->cookie('foo');
464

            
465
Access message cookies, usually L<Mojo::Cookie::Request> or
466
L<Mojo::Cookie::Response> objects. Note that this method caches all data, so
467
it should not be called before all headers have been received.
468

            
469
  # Get cookie value
470
  say $msg->cookie('foo')->value;
471

            
472
=head2 cookies
473

            
474
  my $cookies = $msg->cookies;
475

            
476
Access message cookies. Meant to be overloaded in a subclass.
477

            
478
=head2 dom
479

            
480
  my $dom        = $msg->dom;
481
  my $collection = $msg->dom('a[href]');
482

            
483
Turns message body into a L<Mojo::DOM> object and takes an optional selector
484
to perform a C<find> on it right away, which returns a L<Mojo::Collection>
485
object. Note that this method caches all data, so it should not be called
486
before the entire message body has been received. The whole message body needs
487
to be loaded into memory to parse it, so you have to make sure it is not
488
excessively large.
489

            
490
  # Perform "find" right away
491
  say $msg->dom('h1, h2, h3')->text;
492

            
493
  # Use everything else Mojo::DOM has to offer
494
  say $msg->dom->at('title')->text;
495
  say $msg->dom->html->body->children->type->uniq;
496

            
497
=head2 error
498

            
499
  my $err          = $msg->error;
500
  my ($err, $code) = $msg->error;
501
  $msg             = $msg->error('Parser error');
502
  $msg             = $msg->error('Parser error', 500);
503

            
504
Error and code.
505

            
506
=head2 extract_start_line
507

            
508
  my $bool = $msg->extract_start_line(\$str);
509

            
510
Extract start line from string. Meant to be overloaded in a subclass.
511

            
512
=head2 finish
513

            
514
  $msg = $msg->finish;
515

            
516
Finish message parser/generator.
517

            
518
=head2 fix_headers
519

            
520
  $msg = $msg->fix_headers;
521

            
522
Make sure message has all required headers.
523

            
524
=head2 get_body_chunk
525

            
526
  my $bytes = $msg->get_body_chunk($offset);
527

            
528
Get a chunk of body data starting from a specific position.
529

            
530
=head2 get_header_chunk
531

            
532
  my $bytes = $msg->get_header_chunk($offset);
533

            
534
Get a chunk of header data, starting from a specific position.
535

            
536
=head2 get_start_line_chunk
537

            
538
  my $bytes = $msg->get_start_line_chunk($offset);
539

            
540
Get a chunk of start line data starting from a specific position. Meant to be
541
overloaded in a subclass.
542

            
543
=head2 header_size
544

            
545
  my $size = $msg->header_size;
546

            
547
Size of headers in bytes.
548

            
549
=head2 headers
550

            
551
  my $headers = $msg->headers;
552

            
553
Message headers, usually a L<Mojo::Headers> object.
554

            
555
=head2 is_finished
556

            
557
  my $bool = $msg->is_finished;
558

            
559
Check if message parser/generator is finished.
560

            
561
=head2 is_limit_exceeded
562

            
563
  my $bool = $msg->is_limit_exceeded;
564

            
565
Check if message has exceeded L</"max_line_size"> or L</"max_message_size">.
566

            
567
=head2 json
568

            
569
  my $hash  = $msg->json;
570
  my $array = $msg->json;
571
  my $value = $msg->json('/foo/bar');
572

            
573
Decode JSON message body directly using L<Mojo::JSON> if possible, returns
574
C<undef> otherwise. An optional JSON Pointer can be used to extract a specific
575
value with L<Mojo::JSON::Pointer>. Note that this method caches all data, so
576
it should not be called before the entire message body has been received.
577
The whole message body needs to be loaded into memory to parse it, so you have
578
to make sure it is not excessively large.
579

            
580
  # Extract JSON values
581
  say $msg->json->{foo}{bar}[23];
582
  say $msg->json('/foo/bar/23');
583

            
584
=head2 param
585

            
586
  my @names = $msg->param;
587
  my $foo   = $msg->param('foo');
588
  my @foo   = $msg->param('foo');
589

            
590
Access POST parameters. Note that this method caches all data, so it should
591
not be called before the entire message body has been received. Parts of the
592
message body need to be loaded into memory to parse POST parameters, so you
593
have to make sure it is not excessively large.
594

            
595
=head2 parse
596

            
597
  $msg = $msg->parse('HTTP/1.1 200 OK...');
598

            
599
Parse message chunk.
600

            
601
=head2 start_line_size
602

            
603
  my $size = $msg->start_line_size;
604

            
605
Size of the start line in bytes.
606

            
607
=head2 text
608

            
609
  my $str = $msg->text;
610

            
611
Retrieve L</"body"> and try to decode it if a charset could be extracted with
612
L<Mojo::Content/"charset">.
613

            
614
=head2 to_string
615

            
616
  my $str = $msg->to_string;
617

            
618
Render whole message.
619

            
620
=head2 upload
621

            
622
  my $upload  = $msg->upload('foo');
623
  my @uploads = $msg->upload('foo');
624

            
625
Access C<multipart/form-data> file uploads, usually L<Mojo::Upload> objects.
626
Note that this method caches all data, so it should not be called before the
627
entire message body has been received.
628

            
629
  # Get content of uploaded file
630
  say $msg->upload('foo')->asset->slurp;
631

            
632
=head2 uploads
633

            
634
  my $uploads = $msg->uploads;
635

            
636
All C<multipart/form-data> file uploads, usually L<Mojo::Upload> objects.
637

            
638
=head1 SEE ALSO
639

            
640
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>.
641

            
642
=cut