File Coverage

blib/lib/Mojo/Message/Response.pm
Criterion Covered Total %
statement 54 54 100.0
branch 26 26 100.0
condition 13 16 81.2
subroutine 18 18 100.0
pod 13 13 100.0
total 124 127 97.6


line stmt bran cond sub pod time code
1             package Mojo::Message::Response;
2 57     57   67488 use Mojo::Base 'Mojo::Message';
  57         167  
  57         466  
3              
4 57     57   25881 use Mojo::Cookie::Response;
  57         194  
  57         661  
5 57     57   472 use Mojo::Date;
  57         156  
  57         308  
6              
7             has [qw(code message)];
8             has max_message_size => sub { $ENV{MOJO_MAX_MESSAGE_SIZE} // 2147483648 };
9              
10             # Unmarked codes are from RFC 7231
11             my %MESSAGES = (
12             100 => 'Continue',
13             101 => 'Switching Protocols',
14             102 => 'Processing', # RFC 2518 (WebDAV)
15             103 => 'Early Hints', # RFC 8297
16             200 => 'OK',
17             201 => 'Created',
18             202 => 'Accepted',
19             203 => 'Non-Authoritative Information',
20             204 => 'No Content',
21             205 => 'Reset Content',
22             206 => 'Partial Content',
23             207 => 'Multi-Status', # RFC 2518 (WebDAV)
24             208 => 'Already Reported', # RFC 5842
25             226 => 'IM Used', # RFC 3229
26             300 => 'Multiple Choices',
27             301 => 'Moved Permanently',
28             302 => 'Found',
29             303 => 'See Other',
30             304 => 'Not Modified',
31             305 => 'Use Proxy',
32             307 => 'Temporary Redirect',
33             308 => 'Permanent Redirect', # RFC 7538
34             400 => 'Bad Request',
35             401 => 'Unauthorized',
36             402 => 'Payment Required',
37             403 => 'Forbidden',
38             404 => 'Not Found',
39             405 => 'Method Not Allowed',
40             406 => 'Not Acceptable',
41             407 => 'Proxy Authentication Required',
42             408 => 'Request Timeout',
43             409 => 'Conflict',
44             410 => 'Gone',
45             411 => 'Length Required',
46             412 => 'Precondition Failed',
47             413 => 'Request Entity Too Large',
48             414 => 'Request-URI Too Long',
49             415 => 'Unsupported Media Type',
50             416 => 'Request Range Not Satisfiable',
51             417 => 'Expectation Failed',
52             418 => "I'm a teapot", # RFC 2324 :)
53             421 => 'Misdirected Request', # RFC 7540
54             422 => 'Unprocessable Entity', # RFC 2518 (WebDAV)
55             423 => 'Locked', # RFC 2518 (WebDAV)
56             424 => 'Failed Dependency', # RFC 2518 (WebDAV)
57             425 => 'Too Early', # RFC 8470
58             426 => 'Upgrade Required', # RFC 2817
59             428 => 'Precondition Required', # RFC 6585
60             429 => 'Too Many Requests', # RFC 6585
61             431 => 'Request Header Fields Too Large', # RFC 6585
62             451 => 'Unavailable For Legal Reasons', # RFC 7725
63             500 => 'Internal Server Error',
64             501 => 'Not Implemented',
65             502 => 'Bad Gateway',
66             503 => 'Service Unavailable',
67             504 => 'Gateway Timeout',
68             505 => 'HTTP Version Not Supported',
69             506 => 'Variant Also Negotiates', # RFC 2295
70             507 => 'Insufficient Storage', # RFC 2518 (WebDAV)
71             508 => 'Loop Detected', # RFC 5842
72             509 => 'Bandwidth Limit Exceeded', # Unofficial
73             510 => 'Not Extended', # RFC 2774
74             511 => 'Network Authentication Required' # RFC 6585
75             );
76              
77             sub cookies {
78 1092     1092 1 2078 my $self = shift;
79              
80             # Parse cookies
81 1092         2777 my $headers = $self->headers;
82 1092 100       3312 return [@{Mojo::Cookie::Response->parse($headers->set_cookie)}] unless @_;
  964         3307  
83              
84             # Add cookies
85 128 100       347 $headers->add('Set-Cookie' => "$_") for map { ref $_ eq 'HASH' ? Mojo::Cookie::Response->new($_) : $_ } @_;
  143         776  
86              
87 128         456 return $self;
88             }
89              
90 1957 100 100 1957 1 9932 sub default_message { $MESSAGES{$_[1] || $_[0]->code // 404} || '' }
      100        
91              
92             sub extract_start_line {
93 999     999 1 2533 my ($self, $bufref) = @_;
94              
95             # We have a full response line
96 999 100       11075 return undef unless $$bufref =~ s/^(.*?)\x0d?\x0a//;
97 983 100       8244 return !$self->error({message => 'Bad response start-line'}) unless $1 =~ m!^\s*HTTP/(\d\.\d)\s+(\d\d\d)\s*(.+)?$!;
98              
99 982         3135 my $content = $self->content;
100 982 100       3551 $content->skip_body(1) if $self->code($2)->is_empty;
101 982   66     5851 defined $content->$_ or $content->$_(1) for qw(auto_decompress auto_relax);
102 982         3249 return !!$self->version($1)->message($3);
103             }
104              
105             sub fix_headers {
106 1922     1922 1 3280 my $self = shift;
107 1922 100       7649 $self->{fix} ? return $self : $self->SUPER::fix_headers(@_);
108              
109             # Date
110 960         2862 my $headers = $self->headers;
111 960 100       3384 $headers->date(Mojo::Date->new->to_string) unless $headers->date;
112              
113             # RFC 7230 3.3.2
114 960 100       4362 $headers->remove('Content-Length') if $self->is_empty;
115              
116 960         3216 return $self;
117             }
118              
119             sub get_start_line_chunk {
120 954     954 1 2144 my ($self, $offset) = @_;
121 954         2174 $self->_start_line->emit(progress => 'start_line', $offset);
122 954         3674 return substr $self->{start_buffer}, $offset, 131072;
123             }
124              
125 3     3 1 10 sub is_client_error { shift->_status_class(400) }
126              
127             sub is_empty {
128 2890     2890 1 4810 my $self = shift;
129 2890 100       6873 return undef unless my $code = $self->code;
130 2887   100     6855 return $self->is_info || $code == 204 || $code == 304;
131             }
132              
133 883     883 1 2612 sub is_error { shift->_status_class(400, 500) }
134 3815     3815 1 8648 sub is_info { shift->_status_class(100) }
135 29     29 1 371 sub is_redirect { shift->_status_class(300) }
136 3     3 1 11 sub is_server_error { shift->_status_class(500) }
137              
138 7     7 1 17 sub is_success { shift->_status_class(200) }
139              
140 932     932 1 2934 sub start_line_size { length shift->_start_line->{start_buffer} }
141              
142             sub _start_line {
143 1886     1886   2843 my $self = shift;
144              
145 1886 100       6715 return $self if defined $self->{start_buffer};
146 942   50     2507 my $code = $self->code || 404;
147 942   66     2736 my $msg = $self->message || $self->default_message;
148 942         2144 $self->{start_buffer} = "HTTP/@{[$self->version]} $code $msg\x0d\x0a";
  942         2841  
149              
150 942         4671 return $self;
151             }
152              
153             sub _status_class {
154 4740     4740   10322 my ($self, @classes) = @_;
155 4740 100       10029 return undef unless my $code = $self->code;
156 4720 100       9976 return !!grep { $code >= $_ && $code < ($_ + 100) } @classes;
  5593         48818  
157             }
158              
159             1;
160              
161             =encoding utf8
162              
163             =head1 NAME
164              
165             Mojo::Message::Response - HTTP response
166              
167             =head1 SYNOPSIS
168              
169             use Mojo::Message::Response;
170              
171             # Parse
172             my $res = Mojo::Message::Response->new;
173             $res->parse("HTTP/1.0 200 OK\x0d\x0a");
174             $res->parse("Content-Length: 12\x0d\x0a");
175             $res->parse("Content-Type: text/plain\x0d\x0a\x0d\x0a");
176             $res->parse('Hello World!');
177             say $res->code;
178             say $res->headers->content_type;
179             say $res->body;
180              
181             # Build
182             my $res = Mojo::Message::Response->new;
183             $res->code(200);
184             $res->headers->content_type('text/plain');
185             $res->body('Hello World!');
186             say $res->to_string;
187              
188             =head1 DESCRIPTION
189              
190             L is a container for HTTP responses, based on L
191             and L.
192              
193             =head1 EVENTS
194              
195             L inherits all events from L.
196              
197             =head1 ATTRIBUTES
198              
199             L inherits all attributes from L and implements the following new ones.
200              
201             =head2 code
202              
203             my $code = $res->code;
204             $res = $res->code(200);
205              
206             HTTP response status code.
207              
208             =head2 max_message_size
209              
210             my $size = $res->max_message_size;
211             $res = $res->max_message_size(1024);
212              
213             Maximum message size in bytes, defaults to the value of the C environment variable or
214             C<2147483648> (2GiB). Setting the value to C<0> will allow messages of indefinite size.
215              
216             =head2 message
217              
218             my $msg = $res->message;
219             $res = $res->message('OK');
220              
221             HTTP response status message.
222              
223             =head1 METHODS
224              
225             L inherits all methods from L and implements the following new ones.
226              
227             =head2 cookies
228              
229             my $cookies = $res->cookies;
230             $res = $res->cookies(Mojo::Cookie::Response->new);
231             $res = $res->cookies({name => 'foo', value => 'bar'});
232              
233             Access response cookies, usually L objects.
234              
235             # Names of all cookies
236             say $_->name for @{$res->cookies};
237              
238             =head2 default_message
239              
240             my $msg = $res->default_message;
241             my $msg = $res->default_message(418);
242              
243             Generate default response message for status code, defaults to using L.
244              
245             =head2 extract_start_line
246              
247             my $bool = $res->extract_start_line(\$str);
248              
249             Extract status-line from string.
250              
251             =head2 fix_headers
252              
253             $res = $res->fix_headers;
254              
255             Make sure response has all required headers.
256              
257             =head2 get_start_line_chunk
258              
259             my $bytes = $res->get_start_line_chunk($offset);
260              
261             Get a chunk of status-line data starting from a specific position. Note that this method finalizes the response.
262              
263             =head2 is_client_error
264              
265             my $bool = $res->is_client_error;
266              
267             Check if this response has a C<4xx> status L.
268              
269             =head2 is_empty
270              
271             my $bool = $res->is_empty;
272              
273             Check if this response has a C<1xx>, C<204> or C<304> status L.
274              
275             =head2 is_error
276              
277             my $bool = $res->is_error;
278              
279             Check if this response has a C<4xx> or C<5xx> status L.
280              
281             =head2 is_info
282              
283             my $bool = $res->is_info;
284              
285             Check if this response has a C<1xx> status L.
286              
287             =head2 is_redirect
288              
289             my $bool = $res->is_redirect;
290              
291             Check if this response has a C<3xx> status L.
292              
293             =head2 is_server_error
294              
295             my $bool = $res->is_server_error;
296              
297             Check if this response has a C<5xx> status L.
298              
299             =head2 is_success
300              
301             my $bool = $res->is_success;
302              
303             Check if this response has a C<2xx> status L.
304              
305             =head2 start_line_size
306              
307             my $size = $req->start_line_size;
308              
309             Size of the status-line in bytes. Note that this method finalizes the response.
310              
311             =head1 SEE ALSO
312              
313             L, L, L.
314              
315             =cut