File Coverage

blib/lib/XAS/Lib/Curl/HTTP.pm
Criterion Covered Total %
statement 8 11 72.7
branch n/a
condition n/a
subroutine 4 5 80.0
pod n/a
total 12 16 75.0


line stmt bran cond sub pod time code
1             package XAS::Lib::Curl::HTTP;
2              
3             our $VERSION = '0.01';
4              
5             BEGIN {
6 1     1   2017 no warnings;
  1         1  
  1         49  
7              
8             # this constant is not defined in WWW::Curl on RHEL 5,6,7.
9             # but is, if you compile libcurl on Windows
10              
11 1     1   13 unless (CURLAUTH_ONLY) {
12              
13 0     0     sub CURLAUTH_ONLY { (1 << 31); } # defined in curl.h
14              
15             }
16              
17             }
18              
19 1     1   431 use HTTP::Response;
  1         15358  
  1         26  
20 1     1   190 use WWW::Curl::Easy;
  0            
  0            
21              
22             use XAS::Class
23             debug => 0,
24             version => $VERSION,
25             base => 'XAS::Base',
26             accessors => 'curl',
27             mutators => 'retcode',
28             utils => ':validation dotid',
29             vars => {
30             PARAMS => {
31             -fail_on_error => { optional => 1, default => 0 },
32             -keep_alive => { optional => 1, default => 1 },
33             -follow_location => { optional => 1, default => 1 },
34             -ssl_verify_peer => { optional => 1, default => 1 },
35             -ssl_verify_host => { optional => 1, default => 1 },
36             -max_redirects => { optional => 1, default => 3 },
37             -timeout => { optional => 1, default => 60 },
38             -connect_timeout => { optional => 1, default => 300 },
39             -ssl_cacert => { optional => 1, default => undef },
40             -ssl_keypasswd => { optional => 1, default => undef },
41             -proxy_url => { optional => 1, default => undef },
42             -ssl_cert => { optional => 1, default => undef, depends => [ 'ssl_key' ] },
43             -ssl_key => { optional => 1, default => undef, depends => [ 'ssl_cert' ] },
44             -password => { optional => 1, default => undef, depends => [ 'username' ] },
45             -username => { optional => 1, default => undef, depends => [ 'password' ] },
46             -proxy_password => { optional => 1, default => undef, depends => [ 'proxy_username' ] },
47             -proxy_username => { optional => 1, default => undef, depends => [ 'proxy_password' ] },
48             -auth_method => { optional => 1, default => 'noauth', regex => qr/any|noauth|basic|digest|ntlm|negotiate/ },
49             -proxy_auth => { optional => 1, default => 'noauth', regex => qr/any|noauth|basic|digest|ntlm|negotiate/ },
50             }
51             },
52             ;
53              
54             # ----------------------------------------------------------------------
55             # Public Methods
56             # ----------------------------------------------------------------------
57              
58             sub request {
59             my $self = shift;
60             my ($request) = validate_params(\@_, [
61             { isa => 'HTTP::Request' }
62             ]);
63              
64             my @head;
65             my @buffer;
66             my $response = undef;
67             my $header = $request->headers->as_string("\n");
68             my @headers = split("\n", $header);
69              
70             $self->curl->setopt(CURLOPT_URL, $request->uri);
71             $self->curl->setopt(CURLOPT_HTTPHEADER, \@headers) if (scalar(@headers));
72              
73             # I/O for the request
74              
75             $self->curl->setopt(CURLOPT_WRITEDATA, \@buffer);
76             $self->curl->setopt(CURLOPT_HEADERDATA, \@head);
77             $self->curl->setopt(CURLOPT_READFUNCTION, \&_read_callback);
78             $self->curl->setopt(CURLOPT_WRITEFUNCTION, \&_write_callback);
79              
80             # other options depending on request type
81              
82             if ($request->method eq 'GET') {
83              
84             $self->curl->setopt(CURLOPT_HTTPGET, 1);
85              
86             } elsif ($request->method eq 'POST') {
87              
88             use bytes;
89              
90             my $content = $request->content;
91              
92             $self->curl->setopt(CURLOPT_POST, 1);
93             $self->curl->setopt(CURLOPT_POSTFIELDSIZE, length($content));
94             $self->curl->setopt(CURLOPT_COPYPOSTFIELDS, $content);
95              
96             } elsif ($request->method eq 'PUT') {
97              
98             use bytes;
99              
100             my $content = $request->content;
101              
102             $self->curl->setopt(CURLOPT_UPLOAD, 1);
103             $self->curl->setopt(CURLOPT_READDATA, \$content);
104             $self->curl->setopt(CURLOPT_INFILESIZE, length($content));
105              
106             } elsif ($request->method eq 'HEAD') {
107              
108             $self->curl->setopt(CURLOPT_NOBODY, 1);
109              
110             } else {
111              
112             $self->curl->setopt(CURLOPT_CUSTOMREQUEST, uc $request->method);
113              
114             }
115              
116             # perform the request and create the response
117              
118             if (($self->{'retcode'} = $self->curl->perform) == 0) {
119              
120             my @temp;
121             my $message;
122             my $content;
123              
124             # there may be multiple responses within head, we only
125             # want the last one. so search backwards until a HTTP header
126             # is found.
127              
128             while (my $line = pop(@buffer)) {
129              
130             push(@temp, $line);
131             last if ($line =~ /^HTTP\//);
132              
133             }
134              
135             $content = join('', reverse(@temp));
136              
137             # now let HTTP::Response figure it all out...
138              
139             $response = HTTP::Response->parse($content);
140              
141             # do some fixups
142              
143             $message = $response->message;
144             $response->message($message) if ($message =~ s/\r//g);
145             $response->request($request);
146              
147             } else {
148              
149             $self->throw_msg(
150             dotid($self->class) . '.request.curl',
151             'curl',
152             $self->retcode, lc($self->curl->strerror($self->retcode))
153             );
154              
155             }
156              
157             return $response;
158              
159             }
160              
161             # ----------------------------------------------------------------------
162             # Private Methods
163             # ----------------------------------------------------------------------
164              
165             sub _read_callback {
166             my ($maxlength, $pointer) = @_;
167              
168             my $data = substr($$pointer, 0, $maxlength);
169              
170             $$pointer =
171             length($$pointer) > $maxlength
172             ? scalar substr($$pointer, $maxlength)
173             : '';
174              
175             return $data;
176              
177             }
178              
179             sub _write_callback {
180             my ($data, $pointer) = @_;
181              
182             push(@{$pointer}, $data);
183              
184             return length($data);
185              
186             }
187              
188             sub _authentication {
189             my $self = shift;
190              
191             my $authen = 0;
192              
193             # setup authentication
194              
195             $authen = CURLAUTH_ANY if ($self->auth_method eq 'any');
196             $authen = CURLAUTH_NTLM | CURLAUTH_ONLY if ($self->auth_method eq 'ntlm');
197             $authen = CURLAUTH_BASIC | CURLAUTH_ONLY if ($self->auth_method eq 'basic');
198             $authen = CURLAUTH_DIGEST | CURLAUTH_ONLY if ($self->auth_method eq 'digest');
199             $authen = CURLAUTH_GSSNEGOTIATE | CURLAUTH_ONLY if ($self->auth_method eq 'negotitate');
200              
201             return $authen;
202              
203             }
204              
205             sub init {
206             my $class = shift;
207              
208             my $self = $class->SUPER::init(@_);
209              
210             my $authen = 0;
211             my $timeout = $self->timeout * 1000;
212             my $protocols = (CURLPROTO_HTTP & CURLPROTO_HTTPS);
213             my $connect_timeout = $self->timeout * 1000;
214              
215             $self->{curl} = WWW::Curl::Easy->new();
216              
217             # basic options
218              
219             $self->curl->setopt(CURLOPT_HEADER, 0);
220             $self->curl->setopt(CURLOPT_VERBOSE, $self->xdebug);
221             $self->curl->setopt(CURLOPT_MAXREDIRS, $self->max_redirects);
222             $self->curl->setopt(CURLOPT_PROTOCOLS, $protocols);
223             $self->curl->setopt(CURLOPT_NOPROGRESS, 1);
224             $self->curl->setopt(CURLOPT_TIMEOUT_MS, $timeout);
225             $self->curl->setopt(CURLOPT_FAILONERROR, $self->fail_on_error);
226             $self->curl->setopt(CURLOPT_FORBID_REUSE, $self->keep_alive);
227             $self->curl->setopt(CURLOPT_FOLLOWLOCATION, $self->follow_location);
228             $self->curl->setopt(CURLOPT_CONNECTTIMEOUT_MS, $connect_timeout);
229              
230             # setup authentication
231              
232             $authen = $self->_authentication();
233             $self->curl->setopt(CURLOPT_HTTPAUTH, $authen);
234              
235             if ($self->username) {
236              
237             $self->curl->setopt(CURLOPT_USERNAME, $self->username);
238             $self->curl->setopt(CURLOPT_PASSWORD, $self->password);
239              
240             }
241              
242             # setup proxy stuff
243              
244             if ($self->proxy_url) {
245              
246             $authen = $self->_authentication();
247              
248             $self->curl->setopt(CURLOPT_PROXY, $self->proxy_url);
249             $self->curl->setopt(CURLOPT_PROXYAUTH, $authen);
250             $self->curl->setopt(CURLOPT_PROXYUSERNAME, $self->proxy_username);
251             $self->curl->setopt(CURLOPT_PROXYPASSWORD, $self->proxy_password);
252              
253             }
254              
255             # set up the SSL stuff
256              
257             $self->curl->setopt(CURLOPT_SSL_VERIFYPEER, $self->ssl_verify_peer);
258             $self->curl->setopt(CURLOPT_SSL_VERIFYHOST, $self->ssl_verify_host);
259              
260             if ($self->ssl_keypasswd) {
261              
262             $self->curl->setop(CURLOPT_KEYPASSWD, $self->ssl_keypasswd);
263              
264             }
265              
266             if ($self->ssl_cacert) {
267              
268             $self->curl->setopt(CURLOPT_CAINFO, $self->ssl_cacert);
269              
270             }
271              
272             if ($self->ssl_cert) {
273              
274             $self->curl->setopt(CURLOPT_SSLCERT, $self->ssl_cert);
275             $self->curl->setopt(CURLOPT_SSLKEY, $self->ssl_key);
276              
277             }
278              
279             return $self;
280              
281             }
282              
283             1;
284              
285             __END__
286              
287             =head1 NAME
288              
289             XAS::Lib::Curl::HTTP - A class for the XAS environment
290              
291             =head1 SYNOPSIS
292              
293             use HTTP::Request;
294             use XAS::Lib::Curl::HTTP;
295              
296             my $response;
297             my $request = HTTP::Request->new(GET => 'http://scm.kesteb.us/trac');
298             my $ua = XAS::Lib::Curl::HTTP->new();
299              
300             $response = $ua->request($request);
301             print $response->content;
302              
303             =head1 DESCRIPTION
304              
305             This module uses L<libcurl|http://curl.haxx.se/libcurl/> as the HTTP engine
306             to make requests from a web server.
307              
308             =head1 METHODS
309              
310             All true/false values use 0/1 as the indicator.
311              
312             =head2 new
313              
314             This method initializes the module and takes the following parameters:
315              
316             =over 4
317              
318             =item B<-keep_alive>
319              
320             A toggle to tell curl to forbid the reuse of sockets, defaults to true.
321              
322             =item B<-follow_location>
323              
324             A toggle to tell curl to follow redirects, defaults to true.
325              
326             =item B<-max_redirects>
327              
328             The number of redirects to follow, defaults to 3.
329              
330             =item B<-timeout>
331              
332             The timeout for the connection, defaults to 60 seconds.
333              
334             =item B<-connect_timeout>
335              
336             The timeout for the initial connection, defaults to 300 seconds.
337              
338             =item B<-auth_method>
339              
340             The authentication method to use, defaults to 'noauth'. Possible
341             values are 'any', 'basic', 'digest', 'ntlm', 'negotiate'. If a username
342             and password are supplied, curl defaults to 'basic'.
343              
344             =item B<-password>
345              
346             An optional password to use, implies a username. Wither the password is
347             actually used, depends on -auth_method.
348              
349             =item B<-username>
350              
351             An optional username to use, implies a password.
352              
353             =item B<-ssl_cacert>
354              
355             An optional CA cerificate to use.
356              
357             =item B<-ssl_keypasswd>
358              
359             An optional password for a signed cerificate.
360              
361             =item B<-ssl_cert>
362              
363             An optional certificate to use.
364              
365             =item B<-ssl_key>
366              
367             An optional key for a certificate to use.
368              
369             =item B<-ssl_verify_host>
370              
371             Wither to verify the host certifcate, defaults to true.
372              
373             =item B<-ssl_verify_peer>
374              
375             Wither to verify the peer certificate, defaults to true.
376              
377             =item B<-proxy_url>
378              
379             The url of a proxy that needs to be transversed.
380              
381             =item B<-proxy_auth>
382              
383             The authentication method to use, defaults to 'noauth'. Possible
384             values are 'any', 'basic', 'digest', 'ntlm', 'negotiate'. If a proxy
385             username and a proxy password are supplied, curl defaults to 'basic'.
386              
387             =item B<-proxy_password>
388              
389             An optional password to use, implies a username. Wither the password is
390             actually used, depends on -proxy_auth.
391              
392             =item B<-proxy_username>
393              
394             An optional username to use, implies a password.
395              
396             =back
397              
398             =head2 request($request)
399              
400             This method sends the requset to the web server. The request will return
401             a L<HTTP::Response|https://metacpan.org/pod/HTTP::Response> object. It takes the following parameters:
402              
403             =over 4
404              
405             =item B<$request>
406              
407             A L<HTTP::Request|https://metacpan.org/pod/HTTP::Request> object.
408              
409             =back
410              
411             =head1 SEE ALSO
412              
413             =over 4
414              
415             =item L<XAS|XAS>
416              
417             =item L<WWW::Curl|https://metacpan.org/pod/WWW::Curl>
418              
419             =item L<libcurl|http://curl.haxx.se/libcurl/>
420              
421             =back
422              
423             =head1 AUTHOR
424              
425             Kevin L. Esteb, E<lt>kevin@kesteb.usE<gt>
426              
427             =head1 COPYRIGHT AND LICENSE
428              
429             Copyright (C) 2014 Kevin L. Esteb
430              
431             This is free software; you can redistribute it and/or modify it under
432             the terms of the Artistic License 2.0. For details, see the full text
433             of the license at http://www.perlfoundation.org/artistic_license_2_0.
434              
435             =cut