File Coverage

blib/lib/HTTP/Simple.pm
Criterion Covered Total %
statement 93 96 96.8
branch 41 48 85.4
condition 18 18 100.0
subroutine 26 26 100.0
pod 15 15 100.0
total 193 203 95.0


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