File Coverage

blib/lib/LWP/UserAgent/Paranoid.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1 2     2   11 use strict;
  2         4  
  2         69  
2 2     2   11 use warnings;
  2         6  
  2         55  
3 2     2   10 no warnings "void";
  2         4  
  2         87  
4              
5             =head1 NAME
6              
7             LWP::UserAgent::Paranoid - A modern LWPx::ParanoidAgent for safer requests
8              
9             =cut
10              
11             package LWP::UserAgent::Paranoid;
12 2     2   11 use base 'LWP::UserAgent';
  2         3  
  2         19501  
13              
14             our $VERSION = "0.96";
15              
16 2     2   80562 use Scalar::Util qw/ refaddr /;
  2         6  
  2         126  
17 2     2   10 use Time::HiRes qw/ alarm /;
  2         4  
  2         16  
18 2     2   4150 use LWPx::ParanoidHandler qw//;
  0            
  0            
19             use Net::DNS::Paranoid qw//;
20             use Carp qw//;
21              
22             =head1 SYNOPSIS
23              
24             use LWP::UserAgent::Paranoid;
25             my $ua = LWP::UserAgent::Paranoid->new(
26             request_timeout => 5 # seconds; may be fractional
27             );
28              
29             # use $ua as a normal LWP::UserAgent...
30             my $response = $ua->get("http://example.com");
31              
32             # allow requests to localhost and 127.0.0.1
33             $ua->resolver->whitelisted_hosts(['localhost', '127.0.0.1']);
34              
35             =head1 DESCRIPTION
36              
37             This module is a more modern L with cleaner internals and
38             a very similar feature set. It is a not a drop-in replacement, however, since
39             the API differs.
40              
41             The primary features provided by this module:
42              
43             =head2 Overall request timeout
44              
45             A configurable timeout from start to finish of a "logical" request made by
46             calling one of L's request methods. It encompasses all
47             followed redirects to ensure that you can't be tarpitted by a series of
48             stalling redirects. The default is 5 seconds.
49              
50             =head2 Blocked private hosts and IP address ranges
51              
52             All new agents are automatically made paranoid of private hostnames and IP
53             address ranges using L. You may access the
54             L resolver via the L method in order to
55             customize the blocked or whitelisted hosts.
56              
57             =head1 EVEN MORE PARANOIA
58              
59             You may also wish to tune standard L parameters for greater
60             paranoria depending on your requirements:
61              
62             =head2 Maximum number of redirects
63              
64             Although generally unnecessary given the request timeout, you can tune
65             L down from the default of 7.
66              
67             =head2 Protocols/URI schemes allowed
68              
69             If you don't want to allow requests for schemes other than http and https, you
70             may use L either as a method or as an option
71             to I.
72              
73             $ua->protocols_allowed(["http", "https"]);
74              
75             =head1 WHY NOT LWPx::ParanoidAgent?
76              
77             L's implemention involves a 2009-era fork of LWP's http
78             and https protocol handlers, and it is no longer maintained. A more
79             maintainable approach is taken by this module and L.
80              
81             =head1 METHODS
82              
83             All methods from L are available via inheritence. In addition,
84             the following methods are available:
85              
86             =head2 request_timeout
87              
88             Gets/sets the timeout which encapsulates each logical request, including any
89             redirects which are followed. The default is 5 seconds. Fractional seconds
90             are OK.
91              
92             =head2 resolver
93              
94             Gets the DNS resolver which is used to block private hosts. There is little
95             need to set your own but if you do it should be an L
96             object. This attribute is read-only, so if you want to replace the resolver
97             you need to call L again to create a new L.
98              
99             Use the blocking and whitelisting methods on the resolver to customize the
100             behaviour.
101              
102             =cut
103              
104             sub new {
105             my ($class, %opts) = @_;
106              
107             my $timeout = delete $opts{request_timeout};
108             $timeout = 5 unless $timeout;
109              
110             my $resolver = delete $opts{resolver};
111             $resolver = Net::DNS::Paranoid->new unless $resolver;
112              
113             my $self = $class->SUPER::new(%opts);
114             $self->request_timeout($timeout);
115             $self->_elem("resolver", $resolver);
116              
117             LWPx::ParanoidHandler::make_paranoid($self, $self->resolver);
118              
119             return $self;
120             }
121              
122             sub request_timeout { shift->_elem("request_timeout", @_) }
123             sub resolver {
124             my $self = shift;
125             Carp::croak("resolver is read-only; to use a new resolver, create a new user agent")
126             if @_;
127             return $self->_elem("resolver");
128             }
129              
130             sub __timed_out { Carp::croak("Client timed out request") }
131             sub __with_timeout {
132             my $method = shift;
133             my $self = shift;
134             my $SUPER = $self->can("SUPER::$method")
135             or Carp::croak("No such method '$method'");
136              
137             my $our_alarm = (
138             ref($SIG{ALRM}) eq "CODE"
139             and refaddr($SIG{ALRM}) eq refaddr(\&__timed_out)
140             );
141              
142             if (not $our_alarm) {
143             local $SIG{ALRM} = \&__timed_out;
144             alarm $self->request_timeout;
145             my $ret = $self->$SUPER(@_);
146             alarm 0;
147             return $ret;
148             } else {
149             return $self->$SUPER(@_);
150             }
151             };
152              
153             sub request { __with_timeout("request", @_) }
154             sub simple_request { __with_timeout("simple_request", @_) }
155              
156             "The truth is out there.";
157              
158             =head1 CAVEATS
159              
160             The overall request timeout is implemented using SIGALRM. Any C<$SIG{ALRM}>
161             handler from an outer scope is replaced in the scope of
162             L requests.
163              
164             =head1 BUGS
165              
166             All bugs should be reported via
167             L
168             or L.
169              
170             =head1 AUTHOR
171              
172             Thomas Sibley
173              
174             =head1 LICENSE AND COPYRIGHT
175            
176             This software is Copyright (c) 2013 by Best Practical Solutions
177            
178             This is free software, licensed under:
179            
180             The GNU General Public License, Version 2, June 1991
181              
182             =cut