File Coverage

blib/lib/Mojo/Message.pm
Criterion Covered Total %
statement 171 171 100.0
branch 81 88 92.0
condition 45 56 80.3
subroutine 44 44 100.0
pod 30 30 100.0
total 371 389 95.3


line stmt bran cond sub pod time code
1             package Mojo::Message;
2 63     63   476 use Mojo::Base 'Mojo::EventEmitter';
  63         130  
  63         623  
3              
4 63     63   536 use Carp qw(croak);
  63         196  
  63         4184  
5 63     63   33117 use Mojo::Asset::Memory;
  63         265  
  63         524  
6 63     63   35927 use Mojo::Content::Single;
  63         265  
  63         1575  
7 63     63   43839 use Mojo::DOM;
  63         337  
  63         5657  
8 63     63   4345 use Mojo::JSON qw(j);
  63         170  
  63         5081  
9 63     63   20864 use Mojo::JSON::Pointer;
  63         306  
  63         880  
10 63     63   12015 use Mojo::Parameters;
  63         168  
  63         590  
11 63     63   40873 use Mojo::Upload;
  63         232  
  63         533  
12 63     63   513 use Mojo::Util qw(decode);
  63         143  
  63         267857  
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} || 8192 };
17             has max_message_size => sub { $ENV{MOJO_MAX_MESSAGE_SIZE} // 16777216 };
18             has version => '1.1';
19              
20             sub body {
21 1867     1867 1 13809 my $self = shift;
22              
23             # Get
24 1867         6749 my $content = $self->content;
25 1867 100       9436 return $content->is_multipart ? '' : $content->asset->slurp unless @_;
    100          
26              
27             # Set (multipart content needs to be downgraded)
28 876 100       4758 $content = $self->content(Mojo::Content::Single->new)->content if $content->is_multipart;
29 876         5898 $content->asset(Mojo::Asset::Memory->new->add_chunk(@_));
30              
31 876         2324 return $self;
32             }
33              
34             sub body_params {
35 325     325 1 669 my $self = shift;
36              
37 325 100       1250 return $self->{body_params} if $self->{body_params};
38 317         1846 my $params = $self->{body_params} = Mojo::Parameters->new;
39 317   66     1228 $params->charset($self->content->charset || $self->default_charset);
40              
41             # "application/x-www-form-urlencoded"
42 317   100     1158 my $type = $self->headers->content_type // '';
43 317 100       1932 if ($type =~ m!application/x-www-form-urlencoded!i) {
    100          
44 42         133 $params->parse($self->content->asset->slurp);
45             }
46              
47             # "multipart/form-data"
48             elsif ($type =~ m!multipart/form-data!i) {
49 30         65 $params->append(@$_[0, 1]) for @{$self->_parse_formdata};
  30         151  
50             }
51              
52 317         1487 return $params;
53             }
54              
55 2003     2003 1 7189 sub body_size { shift->content->body_size }
56              
57 45     45 1 140 sub build_body { shift->_build('get_body_chunk') }
58 43     43 1 133 sub build_headers { shift->_build('get_header_chunk') }
59 43     43 1 205 sub build_start_line { shift->_build('get_start_line_chunk') }
60              
61 120     120 1 480 sub cookie { shift->_cache('cookies', 0, @_) }
62              
63 1     1 1 3151 sub cookies { croak 'Method "cookies" not implemented by subclass' }
64              
65             sub dom {
66 133     133 1 1449 my $self = shift;
67 133 50       515 return undef if $self->content->is_multipart;
68 133   66     1144 my $dom = $self->{dom} ||= Mojo::DOM->new($self->text);
69 133 100       847 return @_ ? $dom->find(@_) : $dom;
70             }
71              
72             sub error {
73 8994     8994 1 14583 my $self = shift;
74 8994 100       57379 return $self->{error} unless @_;
75 197         886 $self->{error} = shift;
76 197         712 return $self->finish;
77             }
78              
79 265     265 1 1007 sub every_cookie { shift->_cache('cookies', 1, @_) }
80 316     316 1 1168 sub every_upload { shift->_cache('uploads', 1, @_) }
81              
82 1     1 1 727 sub extract_start_line { croak 'Method "extract_start_line" not implemented by subclass' }
83              
84             sub finish {
85 5411     5411 1 9192 my $self = shift;
86 5411         13577 $self->{state} = 'finished';
87 5411 100       29482 return $self->{finished}++ ? $self : $self->emit('finish');
88             }
89              
90             sub fix_headers {
91 2088     2088 1 7210 my $self = shift;
92 2088 50       11549 return $self if $self->{fix}++;
93              
94             # Content-Length or Connection (unless chunked transfer encoding is used)
95 2088         7186 my $content = $self->content;
96 2088         7886 my $headers = $content->headers;
97 2088 100 100     9470 if ($content->is_multipart) { $headers->remove('Content-Length') }
  22 100       142  
98 50         169 elsif ($content->is_chunked || $headers->content_length) { return $self }
99 2038 100       7985 if ($content->is_dynamic) { $headers->connection('close') }
  38         150  
100 2000         8495 else { $headers->content_length($self->body_size) }
101              
102 2038         5816 return $self;
103             }
104              
105             sub get_body_chunk {
106 3185     3185 1 7029 my ($self, $offset) = @_;
107              
108 3185         11811 $self->emit('progress', 'body', $offset);
109 3185         11648 my $chunk = $self->content->get_body_chunk($offset);
110 3184 100 100     17619 return $chunk if !defined $chunk || length $chunk;
111 1973         8545 $self->finish;
112              
113 1973         5953 return $chunk;
114             }
115              
116             sub get_header_chunk {
117 2128     2128 1 5095 my ($self, $offset) = @_;
118 2128         9698 $self->emit('progress', 'headers', $offset);
119 2128         7365 return $self->fix_headers->content->get_header_chunk($offset);
120             }
121              
122 1     1 1 684 sub get_start_line_chunk { croak 'Method "get_start_line_chunk" not implemented by subclass' }
123              
124 2024     2024 1 9168 sub header_size { shift->fix_headers->content->header_size }
125              
126 23068     23068 1 60694 sub headers { shift->content->headers }
127              
128 3930   100 3930 1 27011 sub is_finished { (shift->{state} // '') eq 'finished' }
129              
130 23     23 1 164 sub is_limit_exceeded { !!shift->{limit} }
131              
132             sub json {
133 74     74 1 221 my ($self, $pointer) = @_;
134 74 50       279 return undef if $self->content->is_multipart;
135 74   100     651 my $data = $self->{json} //= j($self->body);
136 74 100       509 return $pointer ? Mojo::JSON::Pointer->new($data)->get($pointer) : $data;
137             }
138              
139             sub parse {
140 2825     2825 1 9334 my ($self, $chunk) = @_;
141              
142 2825 100       8789 return $self if $self->{error};
143 2820         8465 $self->{raw_size} += length $chunk;
144 2820         13621 $self->{buffer} .= $chunk;
145              
146             # Start-line
147 2820 100       8240 unless ($self->{state}) {
148              
149             # Check start-line size
150 2206         7151 my $len = index $self->{buffer}, "\x0a";
151 2206 100       6484 $len = length $self->{buffer} if $len < 0;
152 2206 100       10182 return $self->_limit('Maximum start-line size exceeded') if $len > $self->max_line_size;
153              
154 2203 100       11286 $self->{state} = 'content' if $self->extract_start_line(\$self->{buffer});
155             }
156              
157             # Content
158 2817   100     10220 my $state = $self->{state} // '';
159 2817 100 100     14695 $self->content($self->content->parse(delete $self->{buffer})) if $state eq 'content' || $state eq 'finished';
160              
161             # Check message size
162 2817         11432 my $max = $self->max_message_size;
163 2817 100 100     13720 return $self->_limit('Maximum message size exceeded') if $max && $max < $self->{raw_size};
164              
165             # Check header size
166 2810 100       8327 return $self->_limit('Maximum header size exceeded') if $self->headers->is_limit_exceeded;
167              
168             # Check buffer size
169 2805 100       8371 return $self->_limit('Maximum buffer size exceeded') if $self->content->is_limit_exceeded;
170              
171 2802 100       11026 return $self->emit('progress')->content->is_finished ? $self->finish : $self;
172             }
173              
174             sub save_to {
175 2     2 1 21 my ($self, $path) = @_;
176 2         11 my $content = $self->content;
177 2 100       11 croak 'Multipart content cannot be saved to files' if $content->is_multipart;
178 1         5 $content->asset->move_to($path);
179 1         6 return $self;
180             }
181              
182 1     1 1 709 sub start_line_size { croak 'Method "start_line_size" not implemented by subclass' }
183              
184             sub text {
185 695     695 1 1649 my $self = shift;
186 695         3012 my $body = $self->body;
187 695   66     2663 my $charset = $self->content->charset || $self->default_charset;
188 695 50 66     6034 return $charset ? decode($charset, $body) // $body : $body;
189             }
190              
191             sub to_string {
192 41     41 1 110 my $self = shift;
193 41         179 return $self->build_start_line . $self->build_headers . $self->build_body;
194             }
195              
196 32     32 1 183 sub upload { shift->_cache('uploads', 0, @_) }
197              
198             sub uploads {
199 268     268 1 543 my $self = shift;
200              
201 268         517 my @uploads;
202 268         525 for my $data (@{$self->_parse_formdata(1)}) {
  268         1163  
203 34         207 my $upload = Mojo::Upload->new(
204             name => $data->[0],
205             filename => $data->[2],
206             asset => $data->[1]->asset,
207             headers => $data->[1]->headers
208             );
209 34         165 push @uploads, $upload;
210             }
211              
212 268         1040 return \@uploads;
213             }
214              
215             sub _build {
216 131     131   278 my ($self, $method) = @_;
217              
218 131         265 my ($buffer, $offset) = ('', 0);
219 131         218 while (1) {
220              
221             # No chunk yet, try again
222 278 100       1101 next unless defined(my $chunk = $self->$method($offset));
223              
224             # End of part
225 277 100       766 last unless my $len = length $chunk;
226              
227 146         231 $offset += $len;
228 146         351 $buffer .= $chunk;
229             }
230              
231 131         745 return $buffer;
232             }
233              
234             sub _cache {
235 733     733   2206 my ($self, $method, $all, $name) = @_;
236              
237             # Cache objects by name
238 733 100       2274 unless ($self->{$method}) {
239 357         1359 $self->{$method} = {};
240 357         723 push @{$self->{$method}{$_->name}}, $_ for @{$self->$method};
  357         1701  
  191         620  
241             }
242              
243 733   100     3136 my $objects = $self->{$method}{$name} // [];
244 733 100       3500 return $all ? $objects : $objects->[-1];
245             }
246              
247 18 50   18   163 sub _limit { ++$_[0]{limit} and return $_[0]->error({message => $_[1]}) }
248              
249             sub _parse_formdata {
250 298     298   785 my ($self, $upload) = @_;
251              
252 298         603 my @formdata;
253 298         1021 my $content = $self->content;
254 298 100       1512 return \@formdata unless $content->is_multipart;
255 61   66     307 my $charset = $content->charset || $self->default_charset;
256              
257             # Check all parts recursively
258 61         219 my @parts = ($content);
259 61         263 while (my $part = shift @parts) {
260              
261 212 100       652 if ($part->is_multipart) {
262 61         143 unshift @parts, @{$part->parts};
  61         211  
263 61         240 next;
264             }
265              
266 151 50       412 next unless my $disposition = $part->headers->content_disposition;
267 151         838 my ($filename) = $disposition =~ /[; ]filename="((?:\\"|[^"])*)"/;
268 151 100 100     1068 next if $upload && !defined $filename || !$upload && defined $filename;
      100        
      100        
269 89         653 my ($name) = $disposition =~ /[; ]name="((?:\\"|[^;"])*)"/;
270 89 100       262 next if !defined $name;
271 88 100       356 $part = $part->asset->slurp unless $upload;
272              
273 88 50       243 if ($charset) {
274 88 100 33     440 $name = decode($charset, $name) // $name if $name;
275 88 100 33     268 $filename = decode($charset, $filename) // $filename if $filename;
276 88 100 33     315 $part = decode($charset, $part) // $part unless $upload;
277             }
278              
279 88         553 push @formdata, [$name, $part, $filename];
280             }
281              
282 61         1738 return \@formdata;
283             }
284              
285             1;
286              
287             =encoding utf8
288              
289             =head1 NAME
290              
291             Mojo::Message - HTTP message base class
292              
293             =head1 SYNOPSIS
294              
295             package Mojo::Message::MyMessage;
296             use Mojo::Base 'Mojo::Message';
297              
298             sub cookies {...}
299             sub extract_start_line {...}
300             sub get_start_line_chunk {...}
301             sub start_line_size {...}
302              
303             =head1 DESCRIPTION
304              
305             L is an abstract base class for HTTP message containers, based on L
306             7230|https://tools.ietf.org/html/rfc7230>, L and L
307             2388|https://tools.ietf.org/html/rfc2388>, like L and L.
308              
309             =head1 EVENTS
310              
311             L inherits all events from L and can emit the following new ones.
312              
313             =head2 finish
314              
315             $msg->on(finish => sub ($msg) {...});
316              
317             Emitted after message building or parsing is finished.
318              
319             my $before = time;
320             $msg->on(finish => sub ($msg) { $msg->headers->header('X-Parser-Time' => time - $before) });
321              
322             =head2 progress
323              
324             $msg->on(progress => sub ($msg) {...});
325              
326             Emitted when message building or parsing makes progress.
327              
328             # Building
329             $msg->on(progress => sub ($msg, $state, $offset) { say qq{Building "$state" at offset $offset} });
330              
331             # Parsing
332             $msg->on(progress => sub ($msg) {
333             return unless my $len = $msg->headers->content_length;
334             my $size = $msg->content->progress;
335             say 'Progress: ', $size == $len ? 100 : int($size / ($len / 100)), '%';
336             });
337              
338             =head1 ATTRIBUTES
339              
340             L implements the following attributes.
341              
342             =head2 content
343              
344             my $msg = $msg->content;
345             $msg = $msg->content(Mojo::Content::Single->new);
346              
347             Message content, defaults to a L object.
348              
349             =head2 default_charset
350              
351             my $charset = $msg->default_charset;
352             $msg = $msg->default_charset('UTF-8');
353              
354             Default charset used by L and to extract data from C or
355             C message body, defaults to C.
356              
357             =head2 max_line_size
358              
359             my $size = $msg->max_line_size;
360             $msg = $msg->max_line_size(1024);
361              
362             Maximum start-line size in bytes, defaults to the value of the C environment variable or C<8192>
363             (8KiB).
364              
365             =head2 max_message_size
366              
367             my $size = $msg->max_message_size;
368             $msg = $msg->max_message_size(1024);
369              
370             Maximum message size in bytes, defaults to the value of the C environment variable or
371             C<16777216> (16MiB). Setting the value to C<0> will allow messages of indefinite size.
372              
373             =head2 version
374              
375             my $version = $msg->version;
376             $msg = $msg->version('1.1');
377              
378             HTTP version of message, defaults to C<1.1>.
379              
380             =head1 METHODS
381              
382             L inherits all methods from L and implements the following new ones.
383              
384             =head2 body
385              
386             my $bytes = $msg->body;
387             $msg = $msg->body('Hello!');
388              
389             Slurp or replace L.
390              
391             =head2 body_params
392              
393             my $params = $msg->body_params;
394              
395             C parameters extracted from C or C message body, usually
396             a L object. Note that this method caches all data, so it should not be called before the entire
397             message body has been received. Parts of the message body need to be loaded into memory to parse C parameters, so
398             you have to make sure it is not excessively large. There's a 16MiB limit for requests and a 2GiB limit for responses by
399             default.
400              
401             # Get POST parameter names and values
402             my $hash = $msg->body_params->to_hash;
403              
404             =head2 body_size
405              
406             my $size = $msg->body_size;
407              
408             Content size in bytes.
409              
410             =head2 build_body
411              
412             my $bytes = $msg->build_body;
413              
414             Render whole body with L.
415              
416             =head2 build_headers
417              
418             my $bytes = $msg->build_headers;
419              
420             Render all headers with L.
421              
422             =head2 build_start_line
423              
424             my $bytes = $msg->build_start_line;
425              
426             Render start-line with L.
427              
428             =head2 cookie
429              
430             my $cookie = $msg->cookie('foo');
431              
432             Access message cookies, usually L or L objects. If there are multiple
433             cookies sharing the same name, and you want to access more than just the last one, you can use L. Note
434             that this method caches all data, so it should not be called before all headers have been received.
435              
436             # Get cookie value
437             say $msg->cookie('foo')->value;
438              
439             =head2 cookies
440              
441             my $cookies = $msg->cookies;
442              
443             Access message cookies. Meant to be overloaded in a subclass.
444              
445             =head2 dom
446              
447             my $dom = $msg->dom;
448             my $collection = $msg->dom('a[href]');
449              
450             Retrieve message body from L and turn it into a L object, an optional selector can be used to call
451             the method L on it right away, which then returns a L object. Note that this method
452             caches all data, so it should not be called before the entire message body has been received. The whole message body
453             needs to be loaded into memory to parse it, so you have to make sure it is not excessively large. There's a 16MiB limit
454             for requests and a 2GiB limit for responses by default.
455              
456             # Perform "find" right away
457             say $msg->dom('h1, h2, h3')->map('text')->join("\n");
458              
459             # Use everything else Mojo::DOM has to offer
460             say $msg->dom->at('title')->text;
461             say $msg->dom->at('body')->children->map('tag')->uniq->join("\n");
462              
463             =head2 error
464              
465             my $err = $msg->error;
466             $msg = $msg->error({message => 'Parser error'});
467              
468             Get or set message error, an C return value indicates that there is no error.
469              
470             # Connection or parser error
471             $msg->error({message => 'Connection refused'});
472              
473             # 4xx/5xx response
474             $msg->error({message => 'Internal Server Error', code => 500});
475              
476             =head2 every_cookie
477              
478             my $cookies = $msg->every_cookie('foo');
479              
480             Similar to L, but returns all message cookies sharing the same name as an array reference.
481              
482             # Get first cookie value
483             say $msg->every_cookie('foo')->[0]->value;
484              
485             =head2 every_upload
486              
487             my $uploads = $msg->every_upload('foo');
488              
489             Similar to L, but returns all file uploads sharing the same name as an array reference.
490              
491             # Get content of first uploaded file
492             say $msg->every_upload('foo')->[0]->asset->slurp;
493              
494             =head2 extract_start_line
495              
496             my $bool = $msg->extract_start_line(\$str);
497              
498             Extract start-line from string. Meant to be overloaded in a subclass.
499              
500             =head2 finish
501              
502             $msg = $msg->finish;
503              
504             Finish message parser/generator.
505              
506             =head2 fix_headers
507              
508             $msg = $msg->fix_headers;
509              
510             Make sure message has all required headers.
511              
512             =head2 get_body_chunk
513              
514             my $bytes = $msg->get_body_chunk($offset);
515              
516             Get a chunk of body data starting from a specific position. Note that it might not be possible to get the same chunk
517             twice if content was generated dynamically.
518              
519             =head2 get_header_chunk
520              
521             my $bytes = $msg->get_header_chunk($offset);
522              
523             Get a chunk of header data, starting from a specific position. Note that this method finalizes the message.
524              
525             =head2 get_start_line_chunk
526              
527             my $bytes = $msg->get_start_line_chunk($offset);
528              
529             Get a chunk of start-line data starting from a specific position. Meant to be overloaded in a subclass.
530              
531             =head2 header_size
532              
533             my $size = $msg->header_size;
534              
535             Size of headers in bytes. Note that this method finalizes the message.
536              
537             =head2 headers
538              
539             my $headers = $msg->headers;
540              
541             Message headers, usually a L object.
542              
543             # Longer version
544             my $headers = $msg->content->headers;
545              
546             =head2 is_finished
547              
548             my $bool = $msg->is_finished;
549              
550             Check if message parser/generator is finished.
551              
552             =head2 is_limit_exceeded
553              
554             my $bool = $msg->is_limit_exceeded;
555              
556             Check if message has exceeded L, L, L or
557             L.
558              
559             =head2 json
560              
561             my $value = $msg->json;
562             my $value = $msg->json('/foo/bar');
563              
564             Decode JSON message body directly using L if possible, an C return value indicates a bare C or
565             that decoding failed. An optional JSON Pointer can be used to extract a specific value with L.
566             Note that this method caches all data, so it should not be called before the entire message body has been received. The
567             whole message body needs to be loaded into memory to parse it, so you have to make sure it is not excessively large.
568             There's a 16MiB limit for requests and a 2GiB limit for responses by default.
569              
570             # Extract JSON values
571             say $msg->json->{foo}{bar}[23];
572             say $msg->json('/foo/bar/23');
573              
574             =head2 parse
575              
576             $msg = $msg->parse('HTTP/1.1 200 OK...');
577              
578             Parse message chunk.
579              
580             =head2 save_to
581              
582             $msg = $msg->save_to('/some/path/index.html');
583              
584             Save message body to a file.
585              
586             =head2 start_line_size
587              
588             my $size = $msg->start_line_size;
589              
590             Size of the start-line in bytes. Meant to be overloaded in a subclass.
591              
592             =head2 text
593              
594             my $str = $msg->text;
595              
596             Retrieve L and try to decode it with L or L.
597              
598             =head2 to_string
599              
600             my $str = $msg->to_string;
601              
602             Render whole message. Note that this method finalizes the message, and that it might not be possible to render the same
603             message twice if content was generated dynamically.
604              
605             =head2 upload
606              
607             my $upload = $msg->upload('foo');
608              
609             Access C file uploads, usually L objects. If there are multiple uploads sharing the
610             same name, and you want to access more than just the last one, you can use L. Note that this method
611             caches all data, so it should not be called before the entire message body has been received.
612              
613             # Get content of uploaded file
614             say $msg->upload('foo')->asset->slurp;
615              
616             =head2 uploads
617              
618             my $uploads = $msg->uploads;
619              
620             All C file uploads, usually L objects.
621              
622             # Names of all uploads
623             say $_->name for @{$msg->uploads};
624              
625             =head1 SEE ALSO
626              
627             L, L, L.
628              
629             =cut