File Coverage

blib/lib/REST/Client.pm
Criterion Covered Total %
statement 161 207 77.7
branch 22 52 42.3
condition 6 16 37.5
subroutine 26 30 86.6
pod 15 15 100.0
total 230 320 71.8


line stmt bran cond sub pod time code
1             package REST::Client;
2             #ABSTRACT: A simple client for interacting with RESTful http/https resources
3              
4             our $VERSION = '280';
5              
6              
7              
8 1     1   987 use strict;
  1         2  
  1         33  
9 1     1   5 use warnings;
  1         1  
  1         24  
10 1     1   18 use 5.008_000;
  1         3  
11              
12 1     1   7 use constant TRUE => 1;
  1         2  
  1         93  
13 1     1   7 use constant FALSE => 0;
  1         2  
  1         58  
14              
15 1     1   650 use URI;
  1         5343  
  1         31  
16 1     1   817 use LWP::UserAgent;
  1         44915  
  1         43  
17 1     1   9 use Carp qw(croak carp);
  1         2  
  1         1719  
18              
19              
20             sub new {
21 3     3 1 4060 my $class = shift;
22 3         23 my $config;
23              
24 3         71 $class->_buildAccessors();
25              
26 3 100 33     21 if(ref $_[0] eq 'HASH'){
    50          
27 2         4 $config = shift;
28             }elsif(scalar @_ && scalar @_ % 2 == 0){
29 0         0 $config = {@_};
30             }else{
31 1         3 $config = {};
32             }
33              
34 3         16 my $self = bless({}, $class);
35 3         11 $self->{'_config'} = $config;
36              
37 3         10 $self->_buildUseragent();
38              
39 3         74 return $self;
40             }
41              
42              
43             sub addHeader {
44 0     0 1 0 my $self = shift;
45 0         0 my $header = shift;
46 0         0 my $value = shift;
47            
48 0   0     0 my $headers = $self->{'_headers'} || {};
49 0         0 $headers->{$header} = $value;
50 0         0 $self->{'_headers'} = $headers;
51 0         0 return;
52             }
53              
54              
55             sub buildQuery {
56 0     0 1 0 my $self = shift;
57              
58 0         0 my $uri = URI->new();
59 0         0 $uri->query_form(@_);
60 0         0 return $uri->as_string();
61             }
62              
63              
64              
65              
66             sub GET {
67 7     7 1 1515 my $self = shift;
68 7         30 my $url = shift;
69 7         17 my $headers = shift;
70 7         34 return $self->request('GET', $url, undef, $headers);
71             }
72              
73              
74             sub PUT {
75 2     2 1 651 my $self = shift;
76 2         9 return $self->request('PUT', @_);
77             }
78              
79              
80             sub PATCH {
81 2     2 1 654 my $self = shift;
82 2         6 return $self->request('PATCH', @_);
83             }
84              
85              
86             sub POST {
87 2     2 1 695 my $self = shift;
88 2         7188 return $self->request('POST', @_);
89             }
90              
91              
92             sub DELETE {
93 2     2   645 my $self = shift;
94 2         4 my $url = shift;
95 2         5 my $headers = shift;
96 2         8 return $self->request('DELETE', $url, undef, $headers);
97             }
98              
99              
100             sub OPTIONS {
101 2     2 1 631 my $self = shift;
102 2         6 my $url = shift;
103 2         3 my $headers = shift;
104 2         6 return $self->request('OPTIONS', $url, undef, $headers);
105             }
106              
107              
108             sub HEAD {
109 2     2 1 654 my $self = shift;
110 2         4 my $url = shift;
111 2         5 my $headers = shift;
112 2         6 return $self->request('HEAD', $url, undef, $headers);
113             }
114              
115              
116             sub request {
117 21     21 1 675 my $self = shift;
118 21         68 my $method = shift;
119 21         44 my $url = shift;
120 21         42 my $content = shift;
121 21         33 my $headers = shift;
122              
123 21         310 $self->{'_res'} = undef;
124 21         74 $self->_buildUseragent();
125              
126              
127             #error check
128 21 50       136 croak "REST::Client exception: First argument to request must be one of GET, PATCH, PUT, POST, DELETE, OPTIONS, HEAD" unless $method =~ /^(get|patch|put|post|delete|options|head)$/i;
129 21 50       56 croak "REST::Client exception: Must provide a url to $method" unless $url;
130 21 50 66     58 croak "REST::Client exception: headers must be presented as a hashref" if $headers && ref $headers ne 'HASH';
131              
132              
133 21         61 $url = $self->_prepareURL($url);
134              
135 21         344 my $ua = $self->getUseragent();
136 21 50       389 if(defined $self->getTimeout()){
137 0         0 $ua->timeout($self->getTimeout);
138             }else{
139 21         85 $ua->timeout(300);
140             }
141 21         502 my $req = HTTP::Request->new( $method => $url );
142              
143             #build headers
144 21 50 66     12580 if(defined $content && length($content)){
145 0         0 $req->content($content);
146 0         0 $req->header('Content-Length', length($content));
147             }else{
148 21         97 $req->header('Content-Length', 0);
149             }
150              
151 21   50     1698 my $custom_headers = $self->{'_headers'} || {};
152 21         122 for my $header (keys %$custom_headers){
153 0         0 $req->header($header, $custom_headers->{$header});
154             }
155              
156 21         56 for my $header (keys %$headers){
157 0         0 $req->header($header, $headers->{$header});
158             }
159              
160              
161             #prime LWP with ssl certfile if we have values
162 21 50       563 if($self->getCert){
163 0 0       0 carp "REST::Client exception: Certs defined but not using https" unless $url =~ /^https/;
164 0 0 0     0 croak "REST::Client exception: Cannot read cert and key file" unless -f $self->getCert && -f $self->getKey;
165              
166 0         0 $ua->ssl_opts(SSL_cert_file => $self->getCert);
167 0         0 $ua->ssl_opts(SSL_key_file => $self->getKey);
168             }
169            
170             #prime LWP with CA file if we have one
171 21 50       389 if(my $ca = $self->getCa){
172 0 0       0 croak "REST::Client exception: Cannot read CA file" unless -f $ca;
173 0         0 $ua->ssl_opts(SSL_ca_file => $ca);
174             }
175              
176             #prime LWP with PKCS12 certificate if we have one
177 21 50       351 if($self->getPkcs12){
178 0 0       0 carp "REST::Client exception: PKCS12 cert defined but not using https" unless $url =~ /^https/;
179 0 0       0 croak "REST::Client exception: Cannot read PKCS12 cert" unless -f $self->getPkcs12;
180              
181 0         0 $ENV{HTTPS_PKCS12_FILE} = $self->getPkcs12;
182 0 0       0 if($self->getPkcs12password){
183 0         0 $ENV{HTTPS_PKCS12_PASSWORD} = $self->getPkcs12password;
184             }
185             }
186              
187 21 50       425 my $res = $self->getFollow ?
188             $ua->request( $req, $self->getContentFile ) :
189             $ua->simple_request( $req, $self->getContentFile );
190              
191 21         261725 $self->{_res} = $res;
192              
193 21         147 return $self;
194             }
195              
196              
197             sub responseCode {
198 3     3 1 7 my $self = shift;
199 3         49 return $self->{_res}->code;
200             }
201              
202              
203             sub responseContent {
204 7     7 1 12 my $self = shift;
205 7         31 return $self->{_res}->content;
206             }
207              
208              
209             sub responseHeaders {
210 1     1 1 665 my $self = shift;
211 1         6 return $self->{_res}->headers()->header_field_names();
212             }
213              
214              
215              
216              
217             sub responseHeader {
218 1     1 1 320 my $self = shift;
219 1         2 my $header = shift;
220 1 50       5 croak "REST::Client exception: no header provided to responseHeader" unless $header;
221 1         6 return $self->{_res}->header($header);
222             }
223              
224              
225             sub responseXpath {
226 0     0 1 0 my $self = shift;
227              
228 0         0 require XML::LibXML;
229              
230 0         0 my $xml= XML::LibXML->new();
231 0         0 $xml->load_ext_dtd(0);
232              
233 0 0       0 if($self->responseHeader('Content-type') =~ /html/){
234 0         0 return XML::LibXML::XPathContext->new($xml->parse_html_string( $self->responseContent() ));
235             }else{
236 0         0 return XML::LibXML::XPathContext->new($xml->parse_string( $self->responseContent() ));
237             }
238             }
239              
240             # Private methods
241              
242             sub _prepareURL {
243 21     21   39 my $self = shift;
244 21         33 my $url = shift;
245              
246             # Do not prepend default host to absolute URLs.
247 21 50       58 return $url if $url =~ /^https?:/;
248              
249 21         450 my $host = $self->getHost;
250 21 50       54 if($host){
251 21 50       68 $url = '/'.$url unless $url =~ /^\//;
252 21         63 $url = $host . $url;
253             }
254 21 50       61 unless($url =~ /^\w+:\/\//){
255 21 50       377 $url = ($self->getCert ? 'https://' : 'http://') . $url;
256             }
257              
258 21         62 return $url;
259             }
260              
261             sub _buildUseragent {
262 24     24   40 my $self = shift;
263              
264 24 100       670 return if $self->getUseragent();
265              
266 3         37 my $ua = LWP::UserAgent->new;
267 3         5177 $ua->agent("REST::Client/$VERSION");
268 3         297 $self->setUseragent($ua);
269              
270 3         5 return;
271             }
272              
273             sub _buildAccessors {
274 3     3   21 my $self = shift;
275              
276 3 100       162 return if $self->can('setHost');
277              
278 1         25 my @attributes = qw(Host Key Cert Ca Timeout Follow Useragent Pkcs12 Pkcs12password ContentFile);
279              
280 1         14 for my $attribute (@attributes){
281 10         174 my $set_method = "
282             sub {
283             my \$self = shift;
284             \$self->{'_config'}{lc('$attribute')} = shift;
285             return \$self->{'_config'}{lc('$attribute')};
286             }";
287              
288 10         47 my $get_method = "
289             sub {
290             my \$self = shift;
291             return \$self->{'_config'}{lc('$attribute')};
292             }";
293              
294              
295             {
296 1     1   10 no strict 'refs';
  1         2  
  1         94  
  10         18  
297 10     0   1504 *{'REST::Client::set'.$attribute} = eval $set_method ;
  10         103  
  0         0  
  0         0  
  0         0  
  3         9  
  3         7  
  3         6  
  1         3  
  1         3  
  1         6  
  1         3  
  1         2  
  1         6  
  3         7996  
  3         17  
  3         176  
  1         3  
  1         2  
  1         5  
  0         0  
  0         0  
  0         0  
  1         3  
  1         3  
  1         4  
  1         5  
  1         2  
  1         5  
  0         0  
  0         0  
  0         0  
298 10     45   933 *{'REST::Client::get'.$attribute} = eval $get_method ;
  10         101  
  45         113  
  45         137  
  21         44  
  21         59  
  21         67  
  21         134  
  23         52  
  23         77  
  23         88  
  23         73  
  21         73  
  21         488  
  2         6  
  2         16  
  0         0  
  0         0  
  44         477  
  44         202  
  23         62  
  23         89  
299             }
300              
301             }
302              
303 1         3 return;
304             }
305              
306             1;
307              
308             __END__