File Coverage

inc/HTTP/Request/Common.pm
Criterion Covered Total %
statement 15 166 9.0
branch 0 86 0.0
condition 0 17 0.0
subroutine 5 12 41.6
pod 4 6 66.6
total 24 287 8.3


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