File Coverage

inc/HTTP/Request/Common.pm
Criterion Covered Total %
statement 33 161 20.5
branch 11 82 13.4
condition 1 17 5.8
subroutine 4 11 36.3
pod 4 6 66.6
total 53 277 19.1


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