File Coverage

inc/HTTP/Request/Common.pm
Criterion Covered Total %
statement 9 170 5.2
branch 0 90 0.0
condition 0 20 0.0
subroutine 3 12 25.0
pod 4 6 66.6
total 16 298 5.3


line stmt bran cond sub pod time code
1             #line 1
2             package HTTP::Request::Common;
3 4     4   1648  
  4         6  
  4         142  
4 4     4   13 use strict;
  4         4  
  4         383  
5             use vars qw(@EXPORT @EXPORT_OK $VERSION $DYNAMIC_FILE_UPLOAD);
6              
7             $DYNAMIC_FILE_UPLOAD ||= 0; # make it defined (don't know why)
8              
9             require Exporter;
10             *import = \&Exporter::import;
11             @EXPORT =qw(GET HEAD PUT POST);
12             @EXPORT_OK = qw($DYNAMIC_FILE_UPLOAD DELETE);
13              
14 4     4   15 require HTTP::Request;
  4         5  
  4         6143  
15             use Carp();
16              
17             $VERSION = "6.04";
18              
19             my $CRLF = "\015\012"; # "\r\n" is not portable
20 0     0 1    
21 0     0 1   sub GET { _simple_req('GET', @_); }
22 0     0 1   sub HEAD { _simple_req('HEAD', @_); }
23 0     0     sub PUT { _simple_req('PUT' , @_); }
24             sub DELETE { _simple_req('DELETE', @_); }
25              
26             sub POST
27 0     0 1   {
28 0           my $url = shift;
29 0           my $req = HTTP::Request->new(POST => $url);
30 0 0 0       my $content;
31 0           $content = shift if @_ and ref $_[0];
32 0           my($k, $v);
33 0 0         while (($k,$v) = splice(@_, 0, 2)) {
34 0           if (lc($k) eq 'content') {
35             $content = $v;
36             }
37 0           else {
38             $req->push_header($k, $v);
39             }
40 0           }
41 0 0         my $ct = $req->header('Content-Type');
    0          
42 0           unless ($ct) {
43             $ct = 'application/x-www-form-urlencoded';
44             }
45 0           elsif ($ct eq 'form-data') {
46             $ct = 'multipart/form-data';
47             }
48 0 0          
49 0 0         if (ref $content) {
50 0           if ($ct =~ m,^multipart/form-data\s*(;|$),i) {
51 0           require HTTP::Headers::Util;
52 0 0         my @v = HTTP::Headers::Util::split_header_words($ct);
53 0           Carp::carp("Multiple Content-Type headers") if @v > 1;
  0            
54             @v = @{$v[0]};
55 0            
56             my $boundary;
57 0           my $boundary_index;
58 0           for (my @tmp = @v; @tmp;) {
59 0 0         my($k, $v) = splice(@tmp, 0, 2);
60 0           if ($k eq "boundary") {
61 0           $boundary = $v;
62 0           $boundary_index = @v - @tmp - 1;
63             last;
64             }
65             }
66 0            
67             ($content, $boundary) = form_data($content, $boundary, $req);
68 0 0          
69 0           if ($boundary_index) {
70             $v[$boundary_index] = $boundary;
71             }
72 0           else {
73             push(@v, boundary => $boundary);
74             }
75 0            
76             $ct = HTTP::Headers::Util::join_header_words(@v);
77             }
78             else {
79             # We use a temporary URI object to format
80 0           # the application/x-www-form-urlencoded content.
81 0           require URI;
82 0 0         my $url = URI->new('http:');
83 0           $url->query_form(ref($content) eq "HASH" ? %$content : @$content);
84             $content = $url->query;
85              
86 0 0         # HTML/4.01 says that line breaks are represented as "CR LF" pairs (i.e., `%0D%0A')
87             $content =~ s/(?
88             }
89             }
90 0            
91 0 0         $req->header('Content-Type' => $ct); # might be redundant
92 0 0         if (defined($content)) {
93             $req->header('Content-Length' =>
94 0           length($content)) unless ref($content);
95             $req->content($content);
96             }
97 0           else {
98             $req->header('Content-Length' => 0);
99 0           }
100             $req;
101             }
102              
103              
104             sub _simple_req
105 0     0     {
106 0           my($method, $url) = splice(@_, 0, 2);
107 0           my $req = HTTP::Request->new($method => $url);
108 0           my($k, $v);
109 0           my $content;
110 0 0         while (($k,$v) = splice(@_, 0, 2)) {
111 0           if (lc($k) eq 'content') {
112 0           $req->add_content($v);
113             $content++;
114             }
115 0           else {
116             $req->push_header($k, $v);
117             }
118 0 0 0       }
119 0           if ($content && !defined($req->header("Content-Length"))) {
  0            
120             $req->header("Content-Length", length(${$req->content_ref}));
121 0           }
122             $req;
123             }
124              
125              
126             sub form_data # RFC1867
127 0     0 0   {
128 0 0         my($data, $boundary, $req) = @_;
129 0           my @data = ref($data) eq "HASH" ? %$data : @$data; # copy
130             my $fhparts;
131 0           my @parts;
132 0           my($k,$v);
133 0 0         while (($k,$v) = splice(@data, 0, 2)) {
134 0           if (!ref($v)) {
135 0           $k =~ s/([\\\"])/\\$1/g; # escape quotes and backslashes
136             push(@parts,
137             qq(Content-Disposition: form-data; name="$k"$CRLF$CRLF$v));
138             }
139 0           else {
140 0 0         my($file, $usename, @headers) = @$v;
141 0           unless (defined $usename) {
142 0 0         $usename = $file;
143             $usename =~ s,.*/,, if defined($usename);
144 0           }
145 0           $k =~ s/([\\\"])/\\$1/g;
146 0 0 0       my $disp = qq(form-data; name="$k");
147 0           if (defined($usename) and length($usename)) {
148 0           $usename =~ s/([\\\"])/\\$1/g;
149             $disp .= qq(; filename="$usename");
150 0           }
151 0           my $content = "";
152 0 0         my $h = HTTP::Headers->new(@headers);
153 0 0         if ($file) {
154 0           open(my $fh, "<", $file) or Carp::croak("Can't open file $file: $!");
155 0 0         binmode($fh);
156             if ($DYNAMIC_FILE_UPLOAD) {
157             # will read file later, close it now in order to
158 0           # not accumulate to many open file handles
159 0           close($fh);
160             $content = \$file;
161             }
162 0           else {
163 0           local($/) = undef; # slurp files
164 0           $content = <$fh>;
165             close($fh);
166 0 0         }
167 0           unless ($h->header("Content-Type")) {
168 0           require LWP::MediaTypes;
169             LWP::MediaTypes::guess_media_type($file, $h);
170             }
171 0 0         }
172             if ($h->header("Content-Disposition")) {
173 0           # just to get it sorted first
174 0           $disp = $h->header("Content-Disposition");
175             $h->remove_header("Content-Disposition");
176 0 0         }
177 0           if ($h->header("Content")) {
178 0           $content = $h->header("Content");
179             $h->remove_header("Content");
180 0           }
181             my $head = join($CRLF, "Content-Disposition: $disp",
182             $h->as_string($CRLF),
183 0 0         "");
184 0           if (ref $content) {
185 0           push(@parts, [$head, $$content]);
186             $fhparts++;
187             }
188 0           else {
189             push(@parts, $head . $content);
190             }
191             }
192 0 0         }
193             return ("", "none") unless @parts;
194 0            
195 0 0         my $content;
196 0 0         if ($fhparts) {
197             $boundary = boundary(10) # hopefully enough randomness
198             unless $boundary;
199              
200 0           # add the boundaries to the @parts array
201 0           for (1..@parts-1) {
202             splice(@parts, $_*2-1, 0, "$CRLF--$boundary$CRLF");
203 0           }
204 0           unshift(@parts, "--$boundary$CRLF");
205             push(@parts, "$CRLF--$boundary--$CRLF");
206              
207 0           # See if we can generate Content-Length header
208 0           my $length = 0;
209 0 0         for (@parts) {
210 0           if (ref $_) {
211 0           my ($head, $f) = @$_;
212 0 0 0       my $file_size;
213             unless ( -f $f && ($file_size = -s _) ) {
214             # The file is either a dynamic file like /dev/audio
215             # or perhaps a file in the /proc file system where
216             # stat may return a 0 size even though reading it
217             # will produce data. So we cannot make
218 0           # a Content-Length header.
219 0           undef $length;
220             last;
221 0           }
222             $length += $file_size + length $head;
223             }
224 0           else {
225             $length += length;
226             }
227 0 0         }
228             $length && $req->header('Content-Length' => $length);
229              
230             # set up a closure that will return content piecemeal
231 0     0     $content = sub {
232 0 0         for (;;) {
233 0 0 0       unless (@parts) {
234             defined $length && $length != 0 &&
235 0           Carp::croak "length of data sent did not match calculated Content-Length header. Probably because uploaded file changed in size during transfer.";
236             return;
237 0           }
238 0 0         my $p = shift @parts;
239 0   0       unless (ref $p) {
240 0 0         $p .= shift @parts while @parts && !ref($parts[0]);
241 0           defined $length && ($length -= length $p);
242             return $p;
243 0           }
244 0 0         my($buf, $fh) = @$p;
245 0           unless (ref($fh)) {
246 0           my $file = $fh;
247 0 0         undef($fh);
248 0           open($fh, "<", $file) || Carp::croak("Can't open file $file: $!");
249             binmode($fh);
250 0           }
251 0           my $buflength = length $buf;
252 0 0         my $n = read($fh, $buf, 2048, $buflength);
253 0           if ($n) {
254 0           $buflength += $n;
255             unshift(@parts, ["", $fh]);
256             }
257 0           else {
258             close($fh);
259 0 0         }
260 0 0         if ($buflength) {
261 0           defined $length && ($length -= $buflength);
262             return $buf
263             }
264 0           }
265             };
266              
267             }
268 0 0         else {
269             $boundary = boundary() unless $boundary;
270 0            
271             my $bno = 0;
272             CHECK_BOUNDARY:
273 0           {
  0            
274 0 0         for (@parts) {
275             if (index($_, $boundary) >= 0) {
276 0           # must have a better boundary
277 0           $boundary = boundary(++$bno);
278             redo CHECK_BOUNDARY;
279             }
280 0           }
281             last;
282 0           }
283             $content = "--$boundary$CRLF" .
284             join("$CRLF--$boundary$CRLF", @parts) .
285             "$CRLF--$boundary--$CRLF";
286             }
287 0 0          
288             wantarray ? ($content, $boundary) : $content;
289             }
290              
291              
292             sub boundary
293 0   0 0 0   {
294 0           my $size = shift || return "xYzZY";
295 0           require MIME::Base64;
296 0           my $b = MIME::Base64::encode(join("", map chr(rand(256)), 1..$size*3), "");
297 0           $b =~ s/[\W]/X/g; # ensure alnum only
298             $b;
299             }
300              
301             1;
302              
303             __END__