File Coverage

blib/lib/Test/LWP/MockSocket/http.pm
Criterion Covered Total %
statement 84 89 94.3
branch 13 16 81.2
condition 1 3 33.3
subroutine 23 25 92.0
pod 0 2 0.0
total 121 135 89.6


line stmt bran cond sub pod time code
1             package Test::LWP::MockSocket::http;
2             #Hack into LWP's socket methods
3 1     1   23980 use strict;
  1         2  
  1         37  
4 1     1   5 use warnings;
  1         2  
  1         31  
5 1     1   5 use base qw(Exporter);
  1         6  
  1         112  
6 1     1   873 use LWP::Protocol::http;
  1         112116  
  1         39  
7 1     1   1013 use HTTP::Request;
  1         1015  
  1         32  
8 1     1   7 no warnings 'redefine';
  1         2  
  1         57  
9              
10             use constant {
11 1         666 HT_MOCKSOCK_PERSIST => 1,
12             HT_MOCKSOCK_QUICKIE => 2
13 1     1   6 };
  1         2  
14              
15             our @EXPORT = qw(
16             $LWP_Response $LWP_SocketArgs
17             mocksock_mode mocksock_response
18             );
19              
20             our $VERSION = 0.05;
21             our ($LWP_Response, $LWP_SocketArgs);
22              
23             my $MODE = HT_MOCKSOCK_PERSIST;
24              
25             *LWP::Protocol::http::socket_class = sub {
26 26     26   81015 '_LWP::FakeSocket';
27             };
28              
29             sub mocksock_mode {
30 0     0 0 0 my $mode = shift;
31 0 0       0 return $MODE unless defined $mode;
32 0         0 $MODE = $MODE;
33             }
34              
35             sub mocksock_response {
36 2     2 0 9288 my $response = shift;
37 2 50       10 return $LWP_Response unless defined $response;
38 2         7 $LWP_Response = $response;
39             }
40              
41              
42             ################################################################################
43             ### Private ###
44             ################################################################################
45             my $RESPONSE_BUF;
46             my $REQDATA; #I don't always use the same conventions for mutables, especially in
47             #such horrible hacks like this
48              
49             my $SEND_REQUEST_DONE = 0;
50             my $RESPBYTES_SENT = 0;
51              
52             sub _add_reqdata {
53 26     26   54 my (undef, $buf) = @_;
54 26         45 $REQDATA .= $buf;
55             }
56              
57             sub _initialize {
58 26     26   44 $REQDATA = "";
59            
60             #The following needs to be true in order for can_read to not fail
61             #before the initial sysread.
62 26         37 $RESPONSE_BUF = "DUMMY";
63            
64 26         34 $SEND_REQUEST_DONE = 0;
65 26         40 $RESPBYTES_SENT = 0;
66             }
67              
68             sub _ensure_response_mode {
69 52 100   52   117 return unless !$SEND_REQUEST_DONE;
70 26         49 my $reftype = ref $LWP_Response;
71 26 100       145 if($reftype eq 'CODE') {
    100          
72 2         15 my $req = HTTP::Request->parse($REQDATA);
73 2         415 $RESPONSE_BUF = $LWP_Response->($REQDATA, $req, $LWP_SocketArgs);
74             } elsif ($reftype eq 'ARRAY') {
75 2         5 $RESPONSE_BUF = shift @{$LWP_Response};
  2         6  
76             } else {
77 22         35 $RESPONSE_BUF = $LWP_Response;
78             }
79 26         225 $SEND_REQUEST_DONE = 1;
80             }
81              
82             sub _get_response_data {
83 52     52   103 my (undef, $buf,$length) = @_;
84 52         97 _ensure_response_mode();
85 52         79 my $remaining_length = length($RESPONSE_BUF);
86 52 100       121 $length = $remaining_length if $length > $remaining_length;
87 52         155 my $blob = substr($RESPONSE_BUF, $RESPBYTES_SENT, $length);
88 52 100       111 if(!$blob) {
89             #No data left. Maybe ConnCache is checking to see if we're still alive.
90             #If we set this to -1, can_read will return false, and it will force the
91             #creation of a new socket.
92 6         11 $RESPBYTES_SENT = -1;
93 6         110 return 0;
94             }
95 46         61 $_[1] = $blob;
96 46         58 $RESPBYTES_SENT += $length;
97 46         140 return length($blob);
98             }
99              
100             package _LWP::FakeSocket;
101 1     1   766 use IO::String;
  1         2987  
  1         74  
102 1     1   9 use base qw(IO::String);
  1         3  
  1         189  
103 1     1   7 use strict;
  1         2  
  1         32  
104 1     1   6 use warnings;
  1         2  
  1         34  
105 1     1   6 no warnings 'redefine';
  1         1  
  1         434  
106             Test::LWP::MockSocket::http->import();
107              
108             my $mock = 'Test::LWP::MockSocket::http';
109              
110             my $n_passed = 0;
111             our $AUTOLOAD;
112             sub AUTOLOAD {
113 306     306   53903 my $self = shift;
114 306         1139 my ($fn_name) = (split(/::/, $AUTOLOAD))[-1];
115 306         2967 my $meth = Net::HTTP::Methods->can($fn_name);
116 306 100       683 if(!$meth) {
117 52         154 return;
118             }
119 254         1749 return $meth->($self, @_);
120             }
121              
122             sub new {
123 26     26   248 $mock->_initialize();
124 26         201 my ($cls,%opts) = @_;
125 26         49 $LWP_SocketArgs = \%opts;
126 26         189 my $self = IO::String->new();
127 26         1302 bless $self, __PACKAGE__;
128 26         76 return $self;
129             }
130              
131             sub can_read {
132 59     59   12015 $RESPONSE_BUF;
133             }
134              
135             sub configure {
136 0     0   0 my $self = $_[0];
137             #log_err("Configure Called!");
138 0         0 return $self;
139             }
140              
141             sub syswrite {
142             #We do some hackery here..
143 26     26   1291 my ($self,$buf,$length) = @_;
144 26   33     69 $length ||= length($buf);
145 26         219 $mock->_add_reqdata($buf);
146 26         94 return $length;
147             }
148              
149             sub sysread {
150 52     52   326 return $mock->_get_response_data($_[1], $_[2]);
151             }
152              
153             0xb00b135;
154              
155             =head1 NAME
156              
157             Test::LWP::MockSocket::http - Inject arbitrary data as socket data for LWP::UserAgent
158              
159             =head1 SYNOPSIS
160              
161             use Test::LWP::MockSocket::http;
162             use LWP::UserAgent;
163             # $LWP_Response is exported by this module
164             $LWP_Response = "HTTP/1.0 200 OK\r\n\r\nSome Response Text";
165             my $ua = LWP::UserAgent->new();
166             $ua->proxy("http", "http://1.2.3.4:56");
167             my $http_response = $ua->get("http://www.foo.com/bar.html");
168            
169             $http_response->code; #200
170             $http_response->content; # "Some response text"
171             $LWP_SocketArgs->{PeerAddr} # '1.2.3.4'
172              
173             =head1 DESCRIPTION
174              
175             This module, when loaded, mangles some functions in L
176             which will emulate a real socket. LWP is used as normally as much as possible.
177              
178             Effort has been made to maintain the exact behavior of L and L.
179              
180             Two variables are exported, C<$LWP_Response> which should contain raw HTTP 'data',
181             and $LWP_SocketArgs which contains a hashref passed to the socket's C constructor.
182             This is helpful for debugging complex LWP::UserAgent subclasses (or wrappers) which
183             modify possible connection settings.
184              
185             =head2 EXPORTED SYMBOLS
186              
187             Following the inspiration of L, two package variables will nicely
188             invade your namespace; they are C<$LWP_Response> which contains a 'response thingy'
189             (see below) and C<$LWP_SocketArgs> which contains a hashref of options that LWP
190             thought it would pass to L or L.
191              
192             In addition, you can use C as an accessor to the C<$LWP_Response>,
193             if you absolutely must.
194              
195             =head2 RESPONSE VARIABLE
196              
197             It was mentioned that C<$LWP_Response> is a 'thingy', and this is because it can
198             be three things:
199              
200             =over
201              
202             =item Scalar
203              
204             This is the simplest way to use this module, and it will simply copy the contents
205             of the scalar verbatim into LWP's read buffers.
206              
207             =item Array Reference
208              
209             This functions like the Scalar model, except that it will cycle through each of the
210             elements in the array for each request, exhausting them - I don't know what happens
211             if you overrun the array - and your test code really shouldn't be doing anything that
212             causes it anyway.
213              
214             =item Code Reference
215              
216             This is the most entertaining of the three. The handler is called with three
217             arguments. The first is the raw request data as received from LWP's serialization
218             methods. The second is an L object which is pretty much just there
219             for your convenience (this is a test module, the more information, the better, and
220             performance is not a big issue), and the last is the socket options found in
221             C<$LWP_SocketArgs>, again, for convenience.
222              
223             =back
224              
225             =head1 CAVEATS/BUGS
226              
227             Probably many. This relies on mainly undocumented behavior and features of LWP
228             and is likely to break. In particular, the module test tries to ensure
229             that the mock socket works together with L.
230              
231             Depending on how LWP handles POST requests and other, perhaps more exotic requests,
232             this module might break. Then again, if you find a need to use this module in the
233             first place, you probably Know What You Are Doing(TM).
234              
235             =head2 RATIONALE
236              
237             I wrote this for testing code which used LWP and its
238             subclasses heavily, but still desired the full functionality of LWP::UserAgent
239             (if you look closely enough, you will see that the same L object which
240             is passed to LWP is not the actual one sent on the wire, and the L
241             object returned by LWP methods is not the same one received on the wire).
242              
243             =head1 ACKNOWLEDGEMENTS
244              
245             Thanks to mst for helping me with the difficult task of selecting the module name
246              
247             =head1 AUTHOR AND COPYRIGHT
248              
249             Copyright 2011 M. Nunberg
250              
251             You may use and distribute this software under the terms of the GNU General Public
252             License Version 2 or higher.