File Coverage

blib/lib/HTTP/Simple.pm
Criterion Covered Total %
statement 91 91 100.0
branch 35 40 87.5
condition 18 18 100.0
subroutine 26 26 100.0
pod 15 15 100.0
total 185 190 97.3


line stmt bran cond sub pod time code
1             package HTTP::Simple;
2              
3 2     2   137314 use strict;
  2         21  
  2         63  
4 2     2   9 use warnings;
  2         4  
  2         56  
5 2     2   11 use Carp 'croak';
  2         4  
  2         103  
6 2     2   375 use Exporter 'import';
  2         5  
  2         98  
7 2     2   15 use File::Basename 'dirname';
  2         12  
  2         185  
8 2     2   1524 use File::Temp;
  2         42971  
  2         147  
9 2     2   1395 use HTTP::Tiny;
  2         83525  
  2         87  
10 2     2   1522 use JSON::PP;
  2         27716  
  2         2728  
11              
12             our $VERSION = '0.002';
13              
14             my @request_functions = qw(get getjson head getprint getstore mirror postform postjson postfile);
15             my @status_functions = qw(is_info is_success is_redirect is_error is_client_error is_server_error);
16             our @EXPORT = (@request_functions, @status_functions);
17             our %EXPORT_TAGS = (
18             all => [@request_functions, @status_functions],
19             request => \@request_functions,
20             status => \@status_functions,
21             );
22              
23             our $UA = HTTP::Tiny->new(agent => "HTTP::Simple/$VERSION");
24             our $JSON = JSON::PP->new->utf8->canonical->allow_nonref->convert_blessed;
25              
26             sub get {
27 3     3 1 2601 my ($url) = @_;
28 3         10 my $res = $UA->get($url);
29 3 100       33 return $res->{content} if $res->{success};
30 2 100       94 croak $res->{content} if $res->{status} == 599;
31 1         183 croak "$res->{status} $res->{reason}";
32             }
33              
34             sub getjson {
35 3     3 1 9 my ($url) = @_;
36 3         18 my $res = $UA->get($url);
37 3 100       26 return $JSON->decode($res->{content}) if $res->{success};
38 2 100       102 croak $res->{content} if $res->{status} == 599;
39 1         91 croak "$res->{status} $res->{reason}";
40             }
41              
42             sub head {
43 3     3 1 1025 my ($url) = @_;
44 3         13 my $res = $UA->head($url);
45 3 100       28 return $res->{headers} if $res->{success};
46 2 100       92 croak $res->{content} if $res->{status} == 599;
47 1         88 croak "$res->{status} $res->{reason}";
48             }
49              
50             sub getprint {
51 3     3 1 3256 my ($url) = @_;
52 3     2   29 my $res = $UA->get($url, {data_callback => sub { print $_[0] }});
  2         14  
53 3 100       122 croak $res->{content} if $res->{status} == 599;
54 2         10 return $res->{status};
55             }
56              
57             sub getstore {
58 2     2 1 3021 my ($url, $file) = @_;
59 2         139 my $temp = File::Temp->new(DIR => dirname $file);
60 2     2   1242 my $res = $UA->get($url, {data_callback => sub { print {$temp} $_[0] }});
  2         12  
  2         15  
61 2 50       27 croak $res->{content} if $res->{status} == 599;
62 2 50       94 close $temp or croak "Failed to close $temp: $!";
63 2 50       22 rename $temp->filename, $file or croak "Failed to rename $temp to $file: $!";
64 2         313 $temp->unlink_on_destroy(0);
65 2         47 return $res->{status};
66             }
67              
68             sub mirror {
69 3     3 1 12 my ($url, $file) = @_;
70 3         12 my $res = $UA->mirror($url, $file);
71 3 100       24 return $res->{status} if $res->{success};
72 2 100       95 croak $res->{content} if $res->{status} == 599;
73 1         91 croak "$res->{status} $res->{reason}";
74             }
75              
76             sub postform {
77 3     3 1 9 my ($url, $form) = @_;
78 3         11 my $res = $UA->post_form($url, $form);
79 3 100       27 return $res->{content} if $res->{success};
80 2 100       96 croak $res->{content} if $res->{status} == 599;
81 1         91 croak "$res->{status} $res->{reason}";
82             }
83              
84             sub postjson {
85 3     3 1 10 my ($url, $data) = @_;
86 3         6 my %options;
87 3         12 $options{headers} = {'Content-Type' => 'application/json; charset=UTF-8'};
88 3         15 $options{content} = $JSON->encode($data);
89 3         369 my $res = $UA->post($url, \%options);
90 3 100       27 return $res->{content} if $res->{success};
91 2 100       96 croak $res->{content} if $res->{status} == 599;
92 1         90 croak "$res->{status} $res->{reason}";
93             }
94              
95             sub postfile {
96 5     5 1 24 my ($url, $file, $content_type) = @_;
97 5 50       233 open my $fh, '<:raw', $file or croak "Failed to open $file: $!";
98 5         18 my %options;
99 5 50       18 $options{headers} = {'Content-Type' => $content_type} if defined $content_type;
100 5         9 my $chunk = 131072;
101 5     13   40 $options{content} = sub { my $buffer; sysread $fh, $buffer, $chunk; $buffer };
  13         83  
  13         589  
  13         68  
102 5         27 my $res = $UA->post($url, \%options);
103 5 100       129 return $res->{content} if $res->{success};
104 2 100       119 croak $res->{content} if $res->{status} == 599;
105 1         122 croak "$res->{status} $res->{reason}";
106             }
107              
108 104   100 104 1 29469 sub is_info { !!($_[0] >= 100 && $_[0] < 200) }
109 104   100 104 1 28839 sub is_success { !!($_[0] >= 200 && $_[0] < 300) }
110 104   100 104 1 28968 sub is_redirect { !!($_[0] >= 300 && $_[0] < 400) }
111 205   100 205 1 57173 sub is_error { !!($_[0] >= 400 && $_[0] < 600) }
112 104   100 104 1 28761 sub is_client_error { !!($_[0] >= 400 && $_[0] < 500) }
113 104   100 104 1 28871 sub is_server_error { !!($_[0] >= 500 && $_[0] < 600) }
114              
115             1;
116              
117             =head1 NAME
118              
119             HTTP::Simple - Simple procedural interface to HTTP::Tiny
120              
121             =head1 SYNOPSIS
122              
123             perl -MHTTP::Simple -e'getprint(shift)' 'https://example.com'
124              
125             use HTTP::Simple;
126              
127             my $content = get 'https://example.com';
128              
129             if (mirror('https://example.com', '/path/to/file.html') == 304) { ... }
130              
131             if (is_success(getprint 'https://example.com')) { ... }
132              
133             postform('https://example.com', {foo => ['bar', 'baz']});
134              
135             postjson('https://example.com', [{bar => 'baz'}]);
136              
137             postfile('https://example.com', '/path/to/file.png');
138              
139             =head1 DESCRIPTION
140              
141             This module is a wrapper of L that provides simplified functions
142             for performing HTTP requests in a similar manner to L, but with
143             slightly more useful error handling. For full control of the request process
144             and response handling, use L directly.
145              
146             L is required for HTTPS requests with L.
147              
148             Request methods that return the body content of the response will return a byte
149             string suitable for directly printing, but that may need to be
150             L for text operations.
151              
152             The L object used by these functions to make requests can be
153             accessed as C<$HTTP::Simple::UA> (for example, to configure the timeout, or
154             replace it with a compatible object like L).
155              
156             The JSON encoder used by the JSON functions defaults to a L instance,
157             and can be accessed as C<$HTTP::Simple::JSON>.
158              
159             =head1 FUNCTIONS
160              
161             All functions are exported by default. Functions can also be requested
162             individually or with the tags C<:request>, C<:status>, or C<:all>.
163              
164             =head2 get
165              
166             my $contents = get($url);
167              
168             Retrieves the document at the given URL with a GET request and returns it as a
169             byte string. Throws an exception on connection or HTTP errors.
170              
171             =head2 getjson
172              
173             my $data = getjson($url);
174              
175             Retrieves the JSON document at the given URL with a GET request and decodes it
176             from JSON to a Perl structure. Throws an exception on connection, HTTP, or JSON
177             errors.
178              
179             =head2 head
180              
181             my $headers = head($url);
182              
183             Retrieves the headers at the given URL with a HEAD request and returns them as
184             a hash reference. Header field names are normalized to lower case, and values
185             may be an array reference if the header is repeated. Throws an exception on
186             connection or HTTP errors.
187              
188             =head2 getprint
189              
190             my $status = getprint($url);
191              
192             Retrieves the document at the given URL with a GET request and prints it as it
193             is received. Returns the HTTP status code. Throws an exception on connection
194             errors.
195              
196             =head2 getstore
197              
198             my $status = getstore($url, $path);
199              
200             Retrieves the document at the given URL with a GET request and stores it to the
201             given file path. Returns the HTTP status code. Throws an exception on
202             connection or filesystem errors.
203              
204             =head2 mirror
205              
206             my $status = mirror($url, $path);
207              
208             Retrieves the document at the given URL with a GET request and mirrors it to
209             the given file path, using the C headers to short-circuit if
210             the file exists and is new enough, and the C header to set its
211             modification time. Returns the HTTP status code. Throws an exception on
212             connection, HTTP, or filesystem errors.
213              
214             =head2 postform
215              
216             my $contents = postform($url, $form);
217              
218             Sends a POST request to the given URL with the given hash or array reference of
219             form data serialized to C. Returns the
220             response body as a byte string. Throws an exception on connection or HTTP
221             errors.
222              
223             =head2 postjson
224              
225             my $contents = postjson($url, $data);
226              
227             Sends a POST request to the given URL with the given data structure encoded to
228             JSON. Returns the response body as a byte string. Throws an exception on
229             connection, HTTP, or JSON errors.
230              
231             =head2 postfile
232              
233             my $contents = postfile($url, $path);
234             my $contents = postfile($url, $path, $content_type);
235              
236             Sends a POST request to the given URL, streaming the contents of the given
237             file. The content type is passed as C if not
238             specified. Returns the response body as a byte string. Throws an exception on
239             connection, HTTP, or filesystem errors.
240              
241             =head2 is_info
242              
243             my $bool = is_info($status);
244              
245             Returns true if the status code indicates an informational response (C<1xx>).
246              
247             =head2 is_success
248              
249             my $bool = is_success($status);
250              
251             Returns true if the status code indicates a successful response (C<2xx>).
252              
253             =head2 is_redirect
254              
255             my $bool = is_redirect($status);
256              
257             Returns true if the status code indicates a redirection response (C<3xx>).
258              
259             =head2 is_error
260              
261             my $bool = is_error($status);
262              
263             Returns true if the status code indicates an error response (C<4xx> or C<5xx>).
264              
265             =head2 is_client_error
266              
267             my $bool = is_client_error($status);
268              
269             Returns true if the status code indicates a client error response (C<4xx>).
270              
271             =head2 is_server_error
272              
273             my $bool = is_server_error($status);
274              
275             Returns true if the status code indicates a server error response (C<5xx>).
276              
277             =head1 BUGS
278              
279             Report any issues on the public bugtracker.
280              
281             =head1 AUTHOR
282              
283             Dan Book
284              
285             =head1 COPYRIGHT AND LICENSE
286              
287             This software is Copyright (c) 2019 by Dan Book.
288              
289             This is free software, licensed under:
290              
291             The Artistic License 2.0 (GPL Compatible)
292              
293             =head1 SEE ALSO
294              
295             L, L, L