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-2023 -- leonerd@leonerd.org.uk
5              
6             package Net::Async::HTTP::Server::Request 0.14;
7              
8 13     13   179 use v5.14;
  13         81  
9 13     13   110 use warnings;
  13         50  
  13         397  
10              
11 13     13   86 use Carp;
  13         35  
  13         771  
12              
13 13     13   90 use URI;
  13         40  
  13         501  
14 13     13   5952 use URI::QueryParam;
  13         10187  
  13         17945  
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 60 my $class = shift;
33 25         65 my ( $conn, $request ) = @_;
34              
35 25         193 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         4 $self->{is_closed} = 1;
64             }
65              
66             sub is_closed
67             {
68 3     3 1 598 my $self = shift;
69 3         13 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 808 my $self = shift;
83 27         124 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 646 my $self = shift;
97 26         72 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 539 my $self = shift;
111 11         32 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 406 my $self = shift;
129 1         5 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 870 my $self = shift;
145 1         36 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 662 my $self = shift;
163 1         7 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 976 my $self = shift;
178 62         190 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 457 my $self = shift;
192 1         3 my ( $key ) = @_;
193 1         13 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 595 my $self = shift;
208 11         29 my @headers;
209              
210             $self->{req}->scan( sub {
211 5     5   241 my ( $name, $value ) = @_;
212 5         17 push @headers, [ $name, $value ];
213 11         110 } );
214              
215 11         322 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 116 my $self = shift;
229 15         57 return $self->{req}->content;
230             }
231              
232             # Called by NaHTTP::Server::Protocol
233             sub _write_to_stream
234             {
235 62     62   103 my $self = shift;
236 62         114 my ( $stream ) = @_;
237              
238 62         108 while( defined( my $next = shift @{ $self->{pending} } ) ) {
  101         6020  
239             $stream->write( $next,
240             on_write => sub {
241 42     42   30133 $self->{bytes_written} += $_[1];
242             },
243             $self->protocol eq "HTTP/1.0" ?
244 39 100   1   188 ( on_flush => sub { $stream->close } ) :
  1         22  
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   5588 $self->{conn}->parent->_done_request( $self );
253             }
254 62 100       271 ) if $self->{is_done};
255              
256 62         1214 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 2089 my $self = shift;
272 42         98 my ( $data ) = @_;
273              
274 42 100       155 unless( defined $self->{response_status_line} ) {
275 22         197 ( $self->{response_status_line} ) = split m/$CRLF/, $data;
276             }
277              
278 42 100       119 return if $self->{is_closed};
279              
280 40 50       108 $self->{is_done} and croak "This request has already been completed";
281              
282 40         67 push @{ $self->{pending} }, $data;
  40         102  
283 40         149 $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 7901 my $self = shift;
298 6         14 my ( $data ) = @_;
299              
300 6 50       20 return if $self->{is_closed};
301 6 100       19 return unless my $len = length $data; # Must not write zero-byte chunks
302              
303 5         32 $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 123 my $self = shift;
317              
318 23 100       88 return if $self->{is_closed};
319              
320 21 50       54 $self->{is_done} and croak "This request has already been completed";
321              
322 21         44 $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 2350 my $self = shift;
337              
338 3 50       12 return if $self->{is_closed};
339              
340 3         13 $self->write( "0$CRLF$CRLF" );
341 3         17 $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 166 my $self = shift;
355 1         2 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 556 my $self = shift;
398 2         5 my ( $response ) = @_;
399              
400 2 50       8 defined $response->protocol or
401             $response->protocol( $self->protocol );
402 2 50       58 defined $response->header( "Transfer-Encoding" ) or
403             $response->header( "Transfer-Encoding" => "chunked" );
404              
405 2         245 my $content = $response->content;
406              
407 2         36 my $header = $response->as_string( $CRLF );
408             # Trim any content from the header as it would need to be chunked
409 2         288 $header =~ s/$CRLF$CRLF.*$/$CRLF$CRLF/s;
410              
411 2         14 $self->write( $header );
412              
413 2 100       13 $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 22 my $self = shift;
430 10         34 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 3 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 20 my $self = shift;
460 2 50       11 my $line = $self->{response_status_line} or return undef;
461 2         17 return +( split m/ /, $line )[1];
462             }
463              
464             # For metrics
465             sub bytes_written
466             {
467 1     1 0 37 my $self = shift;
468 1         9 return $self->{bytes_written};
469             }
470              
471             =head1 AUTHOR
472              
473             Paul Evans
474              
475             =cut
476              
477             0x55AA;