File Coverage

blib/lib/Net/Async/HTTP/Server/Request.pm
Criterion Covered Total %
statement 99 104 95.1
branch 21 30 70.0
condition n/a
subroutine 33 34 97.0
pod 21 23 91.3
total 174 191 91.1


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2013-2024 -- leonerd@leonerd.org.uk
5              
6             package Net::Async::HTTP::Server::Request 0.15;
7              
8 13     13   170 use v5.14;
  13         61  
9 13     13   118 use warnings;
  13         25  
  13         812  
10              
11 13     13   97 use Carp;
  13         73  
  13         1145  
12              
13 13     13   147 use URI;
  13         23  
  13         448  
14 13     13   5824 use URI::QueryParam;
  13         1648  
  13         19145  
15              
16             my $CRLF = "\x0d\x0a";
17              
18             =head1 NAME
19              
20             C - represents a single outstanding request
21              
22             =head1 DESCRIPTION
23              
24             Objects in this class represent a single outstanding request received by a
25             L instance. It allows access to the data received
26             from the web client and allows responding to it.
27              
28             =cut
29              
30             sub new
31             {
32 25     25 0 78 my $class = shift;
33 25         96 my ( $conn, $request ) = @_;
34              
35 25         291 return bless {
36             conn => $conn,
37             req => $request,
38              
39             pending => [],
40             bytes_written => 0,
41             is_done => 0,
42             is_closed => 0,
43             }, $class;
44             }
45              
46             =head1 METHODS
47              
48             =cut
49              
50             =head2 is_closed
51              
52             $is_closed = $request->is_closed;
53              
54             Returns true if the underlying network connection for this request has already
55             been closed. If this is the case, the application is free to drop the request
56             object and perform no further processing on it.
57              
58             =cut
59              
60             sub _close
61             {
62 1     1   2 my $self = shift;
63 1         5 $self->{is_closed} = 1;
64             }
65              
66             sub is_closed
67             {
68 3     3 1 526 my $self = shift;
69 3         14 return $self->{is_closed};
70             }
71              
72             =head2 method
73              
74             $method = $request->method;
75              
76             Return the method name from the request header.
77              
78             =cut
79              
80             sub method
81             {
82 27     27 1 864 my $self = shift;
83 27         185 return $self->{req}->method;
84             }
85              
86             =head2 path
87              
88             $path = $request->path;
89              
90             Return the path name from the request header.
91              
92             =cut
93              
94             sub path
95             {
96 26     26 1 496 my $self = shift;
97 26         84 return $self->{req}->uri->path;
98             }
99              
100             =head2 query_string
101              
102             $query_string = $request->query_string;
103              
104             Return the query string from the request header.
105              
106             =cut
107              
108             sub query_string
109             {
110 11     11 1 411 my $self = shift;
111 11         54 return $self->{req}->uri->query;
112             }
113              
114             =head2 query_form
115              
116             %params = $request->query_form;
117              
118             I
119              
120             Return an even-sized list of name and value pairs that gives the decoded data
121             in the query string. This is the same format as the same-named method on
122             L.
123              
124             =cut
125              
126             sub query_form
127             {
128 1     1 1 356 my $self = shift;
129 1         4 return $self->{req}->uri->query_form;
130             }
131              
132             =head2 query_param_names
133              
134             @names = $request->query_param_names;
135              
136             I
137              
138             Return a list of the names of all the query parameters.
139              
140             =cut
141              
142             sub query_param_names
143             {
144 1     1 1 761 my $self = shift;
145 1         6 return $self->{req}->uri->query_param;
146             }
147              
148             =head2 query_param
149              
150             $value = $request->query_param( $name );
151              
152             @values = $request->query_param( $name );
153              
154             I
155              
156             Return the value or values of a single decoded query parameter.
157              
158             =cut
159              
160             sub query_param
161             {
162 1     1 1 508 my $self = shift;
163 1         5 return $self->{req}->uri->query_param( @_ );
164             }
165              
166             =head2 protocol
167              
168             $protocol = $request->protocol;
169              
170             Return the protocol version from the request header. This will be the full
171             string, such as C.
172              
173             =cut
174              
175             sub protocol
176             {
177 62     62 1 1247 my $self = shift;
178 62         270 return $self->{req}->protocol;
179             }
180              
181             =head2 header
182              
183             $value = $request->header( $key );
184              
185             Return the value of a request header.
186              
187             =cut
188              
189             sub header
190             {
191 1     1 1 361 my $self = shift;
192 1         3 my ( $key ) = @_;
193 1         15 return $self->{req}->header( $key );
194             }
195              
196             =head2 headers
197              
198             @headers = $request->headers;
199              
200             Returns a list of 2-element C refs containing all the request headers.
201             Each referenced array contains, in order, the name and the value.
202              
203             =cut
204              
205             sub headers
206             {
207 11     11 1 442 my $self = shift;
208 11         21 my @headers;
209              
210             $self->{req}->scan( sub {
211 5     5   278 my ( $name, $value ) = @_;
212 5         22 push @headers, [ $name, $value ];
213 11         238 } );
214              
215 11         394 return @headers;
216             }
217              
218             =head2 body
219              
220             $body = $request->body;
221              
222             Return the body content from the request as a string of bytes.
223              
224             =cut
225              
226             sub body
227             {
228 15     15 1 102 my $self = shift;
229 15         90 return $self->{req}->content;
230             }
231              
232             # Called by NaHTTP::Server::Protocol
233             sub _write_to_stream
234             {
235 62     62   143 my $self = shift;
236 62         152 my ( $stream ) = @_;
237              
238 62         152 while( defined( my $next = shift @{ $self->{pending} } ) ) {
  101         12510  
239             $stream->write( $next,
240             on_write => sub {
241 42     42   42092 $self->{bytes_written} += $_[1];
242             },
243             $self->protocol eq "HTTP/1.0" ?
244 39 100   1   319 ( on_flush => sub { $stream->close } ) :
  1         71  
245             (),
246             );
247             }
248              
249             # An empty ->write to ensure we capture the written byte count correctly
250             $stream->write( "",
251             on_write => sub {
252 19     19   9363 $self->{conn}->parent->_done_request( $self );
253             }
254 62 100       306 ) if $self->{is_done};
255              
256 62         2409 return $self->{is_done};
257             }
258              
259             =head2 write
260              
261             $request->write( $data );
262              
263             Append more data to the response to be written to the client. C<$data> can
264             either be a plain string, or a C reference to be used in the underlying
265             L's C method.
266              
267             =cut
268              
269             sub write
270             {
271 42     42 1 2173 my $self = shift;
272 42         107 my ( $data ) = @_;
273              
274 42 100       146 unless( defined $self->{response_status_line} ) {
275 22         292 ( $self->{response_status_line} ) = split m/$CRLF/, $data;
276             }
277              
278 42 100       220 return if $self->{is_closed};
279              
280 40 50       119 $self->{is_done} and croak "This request has already been completed";
281              
282 40         97 push @{ $self->{pending} }, $data;
  40         136  
283 40         178 $self->{conn}->_flush_requests;
284             }
285              
286             =head2 write_chunk
287              
288             $request->write_chunk( $data );
289              
290             Append more data to the response in the form of an HTTP chunked-transfer
291             chunk. This convenience is a shortcut wrapper for prepending the chunk header.
292              
293             =cut
294              
295             sub write_chunk
296             {
297 6     6 1 10621 my $self = shift;
298 6         19 my ( $data ) = @_;
299              
300 6 50       26 return if $self->{is_closed};
301 6 100       23 return unless my $len = length $data; # Must not write zero-byte chunks
302              
303 5         42 $self->write( sprintf "%X$CRLF%s$CRLF", $len, $data );
304             }
305              
306             =head2 done
307              
308             $request->done;
309              
310             Marks this response as completed.
311              
312             =cut
313              
314             sub done
315             {
316 23     23 1 79 my $self = shift;
317              
318 23 100       94 return if $self->{is_closed};
319              
320 21 50       65 $self->{is_done} and croak "This request has already been completed";
321              
322 21         40 $self->{is_done} = 1;
323 21         71 $self->{conn}->_flush_requests;
324             }
325              
326             =head2 write_chunk_eof
327              
328             $request->write_chunk_eof;
329              
330             Sends the final EOF chunk and marks this response as completed.
331              
332             =cut
333              
334             sub write_chunk_eof
335             {
336 3     3 1 3119 my $self = shift;
337              
338 3 50       15 return if $self->{is_closed};
339              
340 3         15 $self->write( "0$CRLF$CRLF" );
341 3         19 $self->done;
342             }
343              
344             =head2 as_http_request
345              
346             $req = $request->as_http_request;
347              
348             Returns the data of the request as an L object.
349              
350             =cut
351              
352             sub as_http_request
353             {
354 1     1 1 143 my $self = shift;
355 1         4 return $self->{req};
356             }
357              
358             =head2 respond
359              
360             $request->respond( $response );
361              
362             Respond to the request using the given L object.
363              
364             =cut
365              
366             sub respond
367             {
368 0     0 1 0 my $self = shift;
369 0         0 my ( $response ) = @_;
370              
371 0 0       0 defined $response->protocol or
372             $response->protocol( $self->protocol );
373              
374 0         0 $self->write( $response->as_string( $CRLF ) );
375 0         0 $self->done;
376             }
377              
378             =head2 respond_chunk_header
379              
380             $request->respond_chunk_header( $response );
381              
382             Respond to the request using the given L object to send in
383             HTTP/1.1 chunked encoding mode.
384              
385             The headers in the C<$response> will be sent (which will be modified to set
386             the C header). Each call to C will send
387             another chunk of data. C will send the final EOF chunk and
388             mark the request as complete.
389              
390             If the C<$response> already contained content, that will be sent as one chunk
391             immediately after the header is sent.
392              
393             =cut
394              
395             sub respond_chunk_header
396             {
397 2     2 1 592 my $self = shift;
398 2         7 my ( $response ) = @_;
399              
400 2 50       10 defined $response->protocol or
401             $response->protocol( $self->protocol );
402 2 50       50 defined $response->header( "Transfer-Encoding" ) or
403             $response->header( "Transfer-Encoding" => "chunked" );
404              
405 2         331 my $content = $response->content;
406              
407 2         39 my $header = $response->as_string( $CRLF );
408             # Trim any content from the header as it would need to be chunked
409 2         387 $header =~ s/$CRLF$CRLF.*$/$CRLF$CRLF/s;
410              
411 2         13 $self->write( $header );
412              
413 2 100       8 $self->write_chunk( $response->content ) if length $response->content;
414             }
415              
416             =head2 stream
417              
418             $stream = $request->stream;
419              
420             Returns the L object representing this connection. Usually
421             this would be used for such things as inspecting the client's connection
422             address on the C of the stream. It should not be necessary to
423             directly perform IO operations on this stream itself.
424              
425             =cut
426              
427             sub stream
428             {
429 10     10 1 23 my $self = shift;
430 10         74 return $self->{conn};
431             }
432              
433             =head2 response_status_line
434              
435             $status = $request->response_status_line;
436              
437             If a response header has been written by calling the C method, returns
438             the first line of it.
439              
440             =cut
441              
442             sub response_status_line
443             {
444 1     1 1 2 my $self = shift;
445 1         5 return $self->{response_status_line};
446             }
447              
448             =head2 response_status_code
449              
450             $code = $request->response_status_code;
451              
452             If a response header has been written by calling the C method, returns
453             the status code from it.
454              
455             =cut
456              
457             sub response_status_code
458             {
459 2     2 1 40 my $self = shift;
460 2 50       15 my $line = $self->{response_status_line} or return undef;
461 2         19 return +( split m/ /, $line )[1];
462             }
463              
464             # For metrics
465             sub bytes_written
466             {
467 1     1 0 2 my $self = shift;
468 1         5 return $self->{bytes_written};
469             }
470              
471             =head1 AUTHOR
472              
473             Paul Evans
474              
475             =cut
476              
477             0x55AA;