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 56     56   1401 use Mojo::Base 'Mojo::Transaction';
  56         141  
  56         531  
3              
4             has 'previous';
5              
6             sub client_read {
7 976     976 1 2474 my ($self, $chunk) = @_;
8              
9             # Skip body for HEAD request
10 976         3184 my $res = $self->res;
11 976 100       3454 $res->content->skip_body(1) if uc $self->req->method eq 'HEAD';
12 976 100       3661 return undef unless $res->parse($chunk)->is_finished;
13              
14             # Unexpected 1xx response
15 937 100 100     3121 return $self->completed if !$res->is_info || $res->headers->upgrade;
16 2         11 $self->res($res->new)->emit(unexpected => $res);
17 2 50       7 return undef unless length(my $leftovers = $res->content->leftovers);
18 2         12 $self->client_read($leftovers);
19             }
20              
21 2013     2013 1 4999 sub client_write { shift->_write(0) }
22              
23 966   100 966 1 2984 sub is_empty { !!(uc $_[0]->req->method eq 'HEAD' || $_[0]->res->is_empty) }
24              
25             sub keep_alive {
26 1841     1841 1 3374 my $self = shift;
27              
28             # Close
29 1841         4135 my $req = $self->req;
30 1841         4139 my $res = $self->res;
31 1841   100     4938 my $req_conn = lc($req->headers->connection // '');
32 1841   100     5322 my $res_conn = lc($res->headers->connection // '');
33 1841 100 100     7642 return undef if $req_conn eq 'close' || $res_conn eq 'close';
34              
35             # Keep-alive is optional for 1.0
36 1796 100       4685 return $res_conn eq 'keep-alive' if $res->version eq '1.0';
37 1787 100       4589 return $req_conn eq 'keep-alive' if $req->version eq '1.0';
38              
39             # Keep-alive is the default for 1.1
40 1784         8870 return 1;
41             }
42              
43             sub redirects {
44 31     31 1 56 my $previous = shift;
45 31         57 my @redirects;
46 31         74 unshift @redirects, $previous while $previous = $previous->previous;
47 31         136 return \@redirects;
48             }
49              
50 1084 50   1084 1 5616 sub resume { ++$_[0]{writing} and return $_[0]->emit('resume') }
51              
52             sub server_read {
53 1026     1026 1 2606 my ($self, $chunk) = @_;
54              
55             # Parse request
56 1026         2777 my $req = $self->req;
57 1026 50       3933 $req->parse($chunk) unless $req->error;
58              
59             # Generate response
60 1026 100 66     2757 $self->emit('request') if $req->is_finished && !$self->{handled}++;
61             }
62              
63 1861     1861 1 4714 sub server_write { shift->_write(1) }
64              
65             sub _body {
66 2854     2854   5481 my ($self, $msg, $finish) = @_;
67              
68             # Prepare body chunk
69 2854         8520 my $buffer = $msg->get_body_chunk($self->{offset});
70 2854 100       7680 $self->{offset} += defined $buffer ? length $buffer : 0;
71              
72             # Delayed
73 2854 100       6445 $self->{writing} = 0 unless defined $buffer;
74              
75             # Finished
76 2854 100 100     14205 $finish ? $self->completed : ($self->{writing} = 0) if defined $buffer && !length $buffer;
    100          
77              
78 2854   100     9578 return $buffer // '';
79             }
80              
81             sub _headers {
82 1892     1892   4138 my ($self, $msg, $head) = @_;
83              
84             # Prepare header chunk
85 1892         6309 my $buffer = $msg->get_header_chunk($self->{offset});
86 1892 50       4840 my $written = defined $buffer ? length $buffer : 0;
87 1892         3291 $self->{write} -= $written;
88 1892         3075 $self->{offset} += $written;
89              
90             # Switch to body
91 1892 50       4990 if ($self->{write} <= 0) {
92 1892         3916 @$self{qw(http_state offset)} = ('body', 0);
93              
94             # Response without body
95 1892 100 100     12789 $self->completed->{http_state} = 'empty' if $head && $self->is_empty;
96             }
97              
98 1892         5868 return $buffer;
99             }
100              
101             sub _start_line {
102 1892     1892   3801 my ($self, $msg) = @_;
103              
104             # Prepare start-line chunk
105 1892         5971 my $buffer = $msg->get_start_line_chunk($self->{offset});
106 1892 50       4628 my $written = defined $buffer ? length $buffer : 0;
107 1892         3372 $self->{write} -= $written;
108 1892         3022 $self->{offset} += $written;
109              
110             # Switch to headers
111 1892 50       7509 @$self{qw(http_state write offset)} = ('headers', $msg->header_size, 0) if $self->{write} <= 0;
112              
113 1892         5503 return $buffer;
114             }
115              
116             sub _write {
117 3874     3874   7280 my ($self, $server) = @_;
118              
119             # Client starts writing right away
120 3874 100 100     15600 return '' unless $server ? $self->{writing} : ($self->{writing} //= 1);
    100          
121              
122             # Nothing written yet
123 2946   100     17748 $self->{$_} ||= 0 for qw(offset write);
124 2946 100       9642 my $msg = $server ? $self->res : $self->req;
125 2946 100       12084 @$self{qw(http_state write)} = ('start_line', $msg->start_line_size) unless $self->{http_state};
126              
127             # Start-line
128 2946         5681 my $chunk = '';
129 2946 100       9415 $chunk .= $self->_start_line($msg) if $self->{http_state} eq 'start_line';
130              
131             # Headers
132 2946 100       9935 $chunk .= $self->_headers($msg, $server) if $self->{http_state} eq 'headers';
133              
134             # Body
135 2946 100       9813 $chunk .= $self->_body($msg, $server) if $self->{http_state} eq 'body';
136              
137 2946         10345 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