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 58     58   66243 use Mojo::Base 'Mojo::Message';
  58         152  
  58         453  
3              
4 58     58   26799 use Mojo::Cookie::Response;
  58         168  
  58         674  
5 58     58   460 use Mojo::Date;
  58         164  
  58         247  
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 1104     1104 1 1966 my $self = shift;
79              
80             # Parse cookies
81 1104         2809 my $headers = $self->headers;
82 1104 100       3297 return [@{Mojo::Cookie::Response->parse($headers->set_cookie)}] unless @_;
  976         3286  
83              
84             # Add cookies
85 128 100       282 $headers->add('Set-Cookie' => "$_") for map { ref $_ eq 'HASH' ? Mojo::Cookie::Response->new($_) : $_ } @_;
  143         845  
86              
87 128         462 return $self;
88             }
89              
90 1981 100 100 1981 1 9977 sub default_message { $MESSAGES{$_[1] || $_[0]->code // 404} || '' }
      100        
91              
92             sub extract_start_line {
93 1011     1011 1 2499 my ($self, $bufref) = @_;
94              
95             # We have a full response line
96 1011 100       11432 return undef unless $$bufref =~ s/^(.*?)\x0d?\x0a//;
97 995 100       8324 return !$self->error({message => 'Bad response start-line'}) unless $1 =~ m!^\s*HTTP/(\d\.\d)\s+(\d\d\d)\s*(.+)?$!;
98              
99 994         3277 my $content = $self->content;
100 994 100       3463 $content->skip_body(1) if $self->code($2)->is_empty;
101 994   66     5720 defined $content->$_ or $content->$_(1) for qw(auto_decompress auto_relax);
102 994         3114 return !!$self->version($1)->message($3);
103             }
104              
105             sub fix_headers {
106 1946     1946 1 3594 my $self = shift;
107 1946 100       7856 $self->{fix} ? return $self : $self->SUPER::fix_headers(@_);
108              
109             # Date
110 972         2841 my $headers = $self->headers;
111 972 100       3217 $headers->date(Mojo::Date->new->to_string) unless $headers->date;
112              
113             # RFC 7230 3.3.2
114 972 100       4156 $headers->remove('Content-Length') if $self->is_empty;
115              
116 972         3112 return $self;
117             }
118              
119             sub get_start_line_chunk {
120 966     966 1 2010 my ($self, $offset) = @_;
121 966         2076 $self->_start_line->emit(progress => 'start_line', $offset);
122 966         3612 return substr $self->{start_buffer}, $offset, 131072;
123             }
124              
125 3     3 1 9 sub is_client_error { shift->_status_class(400) }
126              
127             sub is_empty {
128 2926     2926 1 4903 my $self = shift;
129 2926 100       6660 return undef unless my $code = $self->code;
130 2923   100     6517 return $self->is_info || $code == 204 || $code == 304;
131             }
132              
133 895     895 1 2577 sub is_error { shift->_status_class(400, 500) }
134 3863     3863 1 8759 sub is_info { shift->_status_class(100) }
135 29     29 1 98 sub is_redirect { shift->_status_class(300) }
136 3     3 1 8 sub is_server_error { shift->_status_class(500) }
137              
138 7     7 1 24 sub is_success { shift->_status_class(200) }
139              
140 944     944 1 2725 sub start_line_size { length shift->_start_line->{start_buffer} }
141              
142             sub _start_line {
143 1910     1910   2989 my $self = shift;
144              
145 1910 100       6777 return $self if defined $self->{start_buffer};
146 954   50     2672 my $code = $self->code || 404;
147 954   66     2819 my $msg = $self->message || $self->default_message;
148 954         1963 $self->{start_buffer} = "HTTP/@{[$self->version]} $code $msg\x0d\x0a";
  954         2843  
149              
150 954         4483 return $self;
151             }
152              
153             sub _status_class {
154 4800     4800   10623 my ($self, @classes) = @_;
155 4800 100       10451 return undef unless my $code = $self->code;
156 4780 100       10124 return !!grep { $code >= $_ && $code < ($_ + 100) } @classes;
  5665         48181  
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