File Coverage

blib/lib/Mojo/Transaction/HTTP.pm
Criterion Covered Total %
statement 64 64 100.0
branch 45 52 86.5
condition 27 28 96.4
subroutine 13 13 100.0
pod 8 8 100.0
total 157 165 95.1


line stmt bran cond sub pod time code
1             package Mojo::Transaction::HTTP;
2 59     59   1778 use Mojo::Base 'Mojo::Transaction';
  59         176  
  59         461  
3              
4             has 'previous';
5              
6             sub client_read {
7 1055     1055 1 3044 my ($self, $chunk) = @_;
8              
9             # Skip body for HEAD request
10 1055         4191 my $res = $self->res;
11 1055 100       3659 $res->content->skip_body(1) if uc $self->req->method eq 'HEAD';
12 1055 100       4782 return undef unless $res->parse($chunk)->is_finished;
13              
14             # Unexpected 1xx response
15 986 100 100     3889 return $self->completed if !$res->is_info || $res->headers->upgrade;
16 2         6 $self->res($res->new)->emit(unexpected => $res);
17 2 50       4 return undef unless length(my $leftovers = $res->content->leftovers);
18 2         7 $self->client_read($leftovers);
19             }
20              
21 2145     2145 1 6934 sub client_write { shift->_write(0) }
22              
23 1032   100 1032 1 4058 sub is_empty { !!(uc $_[0]->req->method eq 'HEAD' || $_[0]->res->is_empty) }
24              
25             sub keep_alive {
26 1942     1942 1 3564 my $self = shift;
27              
28             # Close
29 1942         4758 my $req = $self->req;
30 1942         4852 my $res = $self->res;
31 1942   100     5700 my $req_conn = lc($req->headers->connection // '');
32 1942   100     5320 my $res_conn = lc($res->headers->connection // '');
33 1942 100 100     8752 return undef if $req_conn eq 'close' || $res_conn eq 'close';
34              
35             # Keep-alive is optional for 1.0
36 1878 100       5794 return $res_conn eq 'keep-alive' if $res->version eq '1.0';
37 1869 100       7004 return $req_conn eq 'keep-alive' if $req->version eq '1.0';
38              
39             # Keep-alive is the default for 1.1
40 1866         10455 return 1;
41             }
42              
43             sub redirects {
44 35     35 1 86 my $previous = shift;
45 35         60 my @redirects;
46 35         83 unshift @redirects, $previous while $previous = $previous->previous;
47 35         171 return \@redirects;
48             }
49              
50 1208 50   1208 1 9522 sub resume { ++$_[0]{writing} and return $_[0]->emit('resume') }
51              
52             sub server_read {
53 1087     1087 1 3121 my ($self, $chunk) = @_;
54              
55             # Parse request
56 1087         3635 my $req = $self->req;
57 1087 50       5226 $req->parse($chunk) unless $req->error;
58              
59             # Generate response
60 1087 100 66     3369 $self->emit('request') if $req->is_finished && !$self->{handled}++;
61             }
62              
63 1987     1987 1 6165 sub server_write { shift->_write(1) }
64              
65             sub _body {
66 3020     3020   6726 my ($self, $msg, $finish) = @_;
67              
68             # Prepare body chunk
69 3020         12266 my $buffer = $msg->get_body_chunk($self->{offset});
70 3020 100       8982 $self->{offset} += defined $buffer ? length $buffer : 0;
71              
72             # Delayed
73 3020 100       7320 $self->{writing} = 0 unless defined $buffer;
74              
75             # Finished
76 3020 100 100     16476 $finish ? $self->completed : ($self->{writing} = 0) if defined $buffer && !length $buffer;
    100          
77              
78 3020   100     11419 return $buffer // '';
79             }
80              
81             sub _headers {
82 2024     2024   5075 my ($self, $msg, $head) = @_;
83              
84             # Prepare header chunk
85 2024         8780 my $buffer = $msg->get_header_chunk($self->{offset});
86 2024 50       5756 my $written = defined $buffer ? length $buffer : 0;
87 2024         4502 $self->{write} -= $written;
88 2024         4247 $self->{offset} += $written;
89              
90             # Switch to body
91 2024 50       5596 if ($self->{write} <= 0) {
92 2024         5309 @$self{qw(http_state offset)} = ('body', 0);
93              
94             # Response without body
95 2024 100 100     7865 $self->completed->{http_state} = 'empty' if $head && $self->is_empty;
96             }
97              
98 2024         7149 return $buffer;
99             }
100              
101             sub _start_line {
102 2024     2024   5026 my ($self, $msg) = @_;
103              
104             # Prepare start-line chunk
105 2024         8714 my $buffer = $msg->get_start_line_chunk($self->{offset});
106 2024 50       5592 my $written = defined $buffer ? length $buffer : 0;
107 2024         4358 $self->{write} -= $written;
108 2024         4121 $self->{offset} += $written;
109              
110             # Switch to headers
111 2024 50       11150 @$self{qw(http_state write offset)} = ('headers', $msg->header_size, 0) if $self->{write} <= 0;
112              
113 2024         7255 return $buffer;
114             }
115              
116             sub _write {
117 4132     4132   8349 my ($self, $server) = @_;
118              
119             # Client starts writing right away
120 4132 100 100     20426 return '' unless $server ? $self->{writing} : ($self->{writing} //= 1);
    100          
121              
122             # Nothing written yet
123 3136   100     24181 $self->{$_} ||= 0 for qw(offset write);
124 3136 100       12368 my $msg = $server ? $self->res : $self->req;
125 3136 100       15457 @$self{qw(http_state write)} = ('start_line', $msg->start_line_size) unless $self->{http_state};
126              
127             # Start-line
128 3136         6339 my $chunk = '';
129 3136 100       12351 $chunk .= $self->_start_line($msg) if $self->{http_state} eq 'start_line';
130              
131             # Headers
132 3136 100       12829 $chunk .= $self->_headers($msg, $server) if $self->{http_state} eq 'headers';
133              
134             # Body
135 3136 100       12752 $chunk .= $self->_body($msg, $server) if $self->{http_state} eq 'body';
136              
137 3136         12151 return $chunk;
138             }
139              
140             1;
141              
142             =encoding utf8
143              
144             =head1 NAME
145              
146             Mojo::Transaction::HTTP - HTTP transaction
147              
148             =head1 SYNOPSIS
149              
150             use Mojo::Transaction::HTTP;
151              
152             # Client
153             my $tx = Mojo::Transaction::HTTP->new;
154             $tx->req->method('GET');
155             $tx->req->url->parse('http://example.com');
156             $tx->req->headers->accept('application/json');
157             say $tx->res->code;
158             say $tx->res->headers->content_type;
159             say $tx->res->body;
160             say $tx->remote_address;
161              
162             # Server
163             my $tx = Mojo::Transaction::HTTP->new;
164             say $tx->req->method;
165             say $tx->req->url->to_abs;
166             say $tx->req->headers->accept;
167             say $tx->remote_address;
168             $tx->res->code(200);
169             $tx->res->headers->content_type('text/plain');
170             $tx->res->body('Hello World!');
171              
172             =head1 DESCRIPTION
173              
174             L is a container for HTTP transactions, based on L
175             7230|https://tools.ietf.org/html/rfc7230> and L.
176              
177             =head1 EVENTS
178              
179             L inherits all events from L and can emit the following new ones.
180              
181             =head2 request
182              
183             $tx->on(request => sub ($tx) {...});
184              
185             Emitted when a request is ready and needs to be handled.
186              
187             $tx->on(request => sub ($tx) { $tx->res->headers->header('X-Bender' => 'Bite my shiny metal ass!') });
188              
189             =head2 resume
190              
191             $tx->on(resume => sub ($tx) {...});
192              
193             Emitted when transaction is resumed.
194              
195             =head2 unexpected
196              
197             $tx->on(unexpected => sub ($tx, $res) {...});
198              
199             Emitted for unexpected C<1xx> responses that will be ignored.
200              
201             $tx->on(unexpected => sub ($tx) { $tx->res->on(finish => sub { say 'Follow-up response is finished.' }) });
202              
203             =head1 ATTRIBUTES
204              
205             L inherits all attributes from L and implements the following new ones.
206              
207             =head2 previous
208              
209             my $previous = $tx->previous;
210             $tx = $tx->previous(Mojo::Transaction::HTTP->new);
211              
212             Previous transaction that triggered this follow-up transaction, usually a L object.
213              
214             # Paths of previous requests
215             say $tx->previous->previous->req->url->path;
216             say $tx->previous->req->url->path;
217              
218             =head1 METHODS
219              
220             L inherits all methods from L and implements the following new ones.
221              
222             =head2 client_read
223              
224             $tx->client_read($bytes);
225              
226             Read data client-side, used to implement user agents such as L.
227              
228             =head2 client_write
229              
230             my $bytes = $tx->client_write;
231              
232             Write data client-side, used to implement user agents such as L.
233              
234             =head2 is_empty
235              
236             my $bool = $tx->is_empty;
237              
238             Check transaction for C request and C<1xx>, C<204> or C<304> response.
239              
240             =head2 keep_alive
241              
242             my $bool = $tx->keep_alive;
243              
244             Check if connection can be kept alive.
245              
246             =head2 redirects
247              
248             my $redirects = $tx->redirects;
249              
250             Return an array reference with all previous transactions that preceded this follow-up transaction.
251              
252             # Paths of all previous requests
253             say $_->req->url->path for @{$tx->redirects};
254              
255             =head2 resume
256              
257             $tx = $tx->resume;
258              
259             Resume transaction.
260              
261             =head2 server_read
262              
263             $tx->server_read($bytes);
264              
265             Read data server-side, used to implement web servers such as L.
266              
267             =head2 server_write
268              
269             my $bytes = $tx->server_write;
270              
271             Write data server-side, used to implement web servers such as L.
272              
273             =head1 SEE ALSO
274              
275             L, L, L.
276              
277             =cut