source: main/trunk/greenstone2/perllib/cpan/HTTP/Request/Common.pm@ 27174

Last change on this file since 27174 was 27174, checked in by davidb, 11 years ago

Perl modules from CPAN that are used in supporting activate.pl, but not part of the Perl core. Only PMs included.

File size: 14.1 KB
Line 
1package HTTP::Request::Common;
2
3use strict;
4use vars qw(@EXPORT @EXPORT_OK $VERSION $DYNAMIC_FILE_UPLOAD);
5
6$DYNAMIC_FILE_UPLOAD ||= 0; # make it defined (don't know why)
7
8require Exporter;
9*import = \&Exporter::import;
10@EXPORT =qw(GET HEAD PUT POST);
11@EXPORT_OK = qw($DYNAMIC_FILE_UPLOAD DELETE);
12
13require HTTP::Request;
14use Carp();
15
16$VERSION = "6.04";
17
18my $CRLF = "\015\012"; # "\r\n" is not portable
19
20sub GET { _simple_req('GET', @_); }
21sub HEAD { _simple_req('HEAD', @_); }
22sub PUT { _simple_req('PUT' , @_); }
23sub DELETE { _simple_req('DELETE', @_); }
24
25sub POST
26{
27 my $url = shift;
28 my $req = HTTP::Request->new(POST => $url);
29 my $content;
30 $content = shift if @_ and ref $_[0];
31 my($k, $v);
32 while (($k,$v) = splice(@_, 0, 2)) {
33 if (lc($k) eq 'content') {
34 $content = $v;
35 }
36 else {
37 $req->push_header($k, $v);
38 }
39 }
40 my $ct = $req->header('Content-Type');
41 unless ($ct) {
42 $ct = 'application/x-www-form-urlencoded';
43 }
44 elsif ($ct eq 'form-data') {
45 $ct = 'multipart/form-data';
46 }
47
48 if (ref $content) {
49 if ($ct =~ m,^multipart/form-data\s*(;|$),i) {
50 require HTTP::Headers::Util;
51 my @v = HTTP::Headers::Util::split_header_words($ct);
52 Carp::carp("Multiple Content-Type headers") if @v > 1;
53 @v = @{$v[0]};
54
55 my $boundary;
56 my $boundary_index;
57 for (my @tmp = @v; @tmp;) {
58 my($k, $v) = splice(@tmp, 0, 2);
59 if ($k eq "boundary") {
60 $boundary = $v;
61 $boundary_index = @v - @tmp - 1;
62 last;
63 }
64 }
65
66 ($content, $boundary) = form_data($content, $boundary, $req);
67
68 if ($boundary_index) {
69 $v[$boundary_index] = $boundary;
70 }
71 else {
72 push(@v, boundary => $boundary);
73 }
74
75 $ct = HTTP::Headers::Util::join_header_words(@v);
76 }
77 else {
78 # We use a temporary URI object to format
79 # the application/x-www-form-urlencoded content.
80 require URI;
81 my $url = URI->new('http:');
82 $url->query_form(ref($content) eq "HASH" ? %$content : @$content);
83 $content = $url->query;
84
85 # HTML/4.01 says that line breaks are represented as "CR LF" pairs (i.e., `%0D%0A')
86 $content =~ s/(?<!%0D)%0A/%0D%0A/g if defined($content);
87 }
88 }
89
90 $req->header('Content-Type' => $ct); # might be redundant
91 if (defined($content)) {
92 $req->header('Content-Length' =>
93 length($content)) unless ref($content);
94 $req->content($content);
95 }
96 else {
97 $req->header('Content-Length' => 0);
98 }
99 $req;
100}
101
102
103sub _simple_req
104{
105 my($method, $url) = splice(@_, 0, 2);
106 my $req = HTTP::Request->new($method => $url);
107 my($k, $v);
108 my $content;
109 while (($k,$v) = splice(@_, 0, 2)) {
110 if (lc($k) eq 'content') {
111 $req->add_content($v);
112 $content++;
113 }
114 else {
115 $req->push_header($k, $v);
116 }
117 }
118 if ($content && !defined($req->header("Content-Length"))) {
119 $req->header("Content-Length", length(${$req->content_ref}));
120 }
121 $req;
122}
123
124
125sub form_data # RFC1867
126{
127 my($data, $boundary, $req) = @_;
128 my @data = ref($data) eq "HASH" ? %$data : @$data; # copy
129 my $fhparts;
130 my @parts;
131 my($k,$v);
132 while (($k,$v) = splice(@data, 0, 2)) {
133 if (!ref($v)) {
134 $k =~ s/([\\\"])/\\$1/g; # escape quotes and backslashes
135 push(@parts,
136 qq(Content-Disposition: form-data; name="$k"$CRLF$CRLF$v));
137 }
138 else {
139 my($file, $usename, @headers) = @$v;
140 unless (defined $usename) {
141 $usename = $file;
142 $usename =~ s,.*/,, if defined($usename);
143 }
144 $k =~ s/([\\\"])/\\$1/g;
145 my $disp = qq(form-data; name="$k");
146 if (defined($usename) and length($usename)) {
147 $usename =~ s/([\\\"])/\\$1/g;
148 $disp .= qq(; filename="$usename");
149 }
150 my $content = "";
151 my $h = HTTP::Headers->new(@headers);
152 if ($file) {
153 open(my $fh, "<", $file) or Carp::croak("Can't open file $file: $!");
154 binmode($fh);
155 if ($DYNAMIC_FILE_UPLOAD) {
156 # will read file later, close it now in order to
157 # not accumulate to many open file handles
158 close($fh);
159 $content = \$file;
160 }
161 else {
162 local($/) = undef; # slurp files
163 $content = <$fh>;
164 close($fh);
165 }
166 unless ($h->header("Content-Type")) {
167 require LWP::MediaTypes;
168 LWP::MediaTypes::guess_media_type($file, $h);
169 }
170 }
171 if ($h->header("Content-Disposition")) {
172 # just to get it sorted first
173 $disp = $h->header("Content-Disposition");
174 $h->remove_header("Content-Disposition");
175 }
176 if ($h->header("Content")) {
177 $content = $h->header("Content");
178 $h->remove_header("Content");
179 }
180 my $head = join($CRLF, "Content-Disposition: $disp",
181 $h->as_string($CRLF),
182 "");
183 if (ref $content) {
184 push(@parts, [$head, $$content]);
185 $fhparts++;
186 }
187 else {
188 push(@parts, $head . $content);
189 }
190 }
191 }
192 return ("", "none") unless @parts;
193
194 my $content;
195 if ($fhparts) {
196 $boundary = boundary(10) # hopefully enough randomness
197 unless $boundary;
198
199 # add the boundaries to the @parts array
200 for (1..@parts-1) {
201 splice(@parts, $_*2-1, 0, "$CRLF--$boundary$CRLF");
202 }
203 unshift(@parts, "--$boundary$CRLF");
204 push(@parts, "$CRLF--$boundary--$CRLF");
205
206 # See if we can generate Content-Length header
207 my $length = 0;
208 for (@parts) {
209 if (ref $_) {
210 my ($head, $f) = @$_;
211 my $file_size;
212 unless ( -f $f && ($file_size = -s _) ) {
213 # The file is either a dynamic file like /dev/audio
214 # or perhaps a file in the /proc file system where
215 # stat may return a 0 size even though reading it
216 # will produce data. So we cannot make
217 # a Content-Length header.
218 undef $length;
219 last;
220 }
221 $length += $file_size + length $head;
222 }
223 else {
224 $length += length;
225 }
226 }
227 $length && $req->header('Content-Length' => $length);
228
229 # set up a closure that will return content piecemeal
230 $content = sub {
231 for (;;) {
232 unless (@parts) {
233 defined $length && $length != 0 &&
234 Carp::croak "length of data sent did not match calculated Content-Length header. Probably because uploaded file changed in size during transfer.";
235 return;
236 }
237 my $p = shift @parts;
238 unless (ref $p) {
239 $p .= shift @parts while @parts && !ref($parts[0]);
240 defined $length && ($length -= length $p);
241 return $p;
242 }
243 my($buf, $fh) = @$p;
244 unless (ref($fh)) {
245 my $file = $fh;
246 undef($fh);
247 open($fh, "<", $file) || Carp::croak("Can't open file $file: $!");
248 binmode($fh);
249 }
250 my $buflength = length $buf;
251 my $n = read($fh, $buf, 2048, $buflength);
252 if ($n) {
253 $buflength += $n;
254 unshift(@parts, ["", $fh]);
255 }
256 else {
257 close($fh);
258 }
259 if ($buflength) {
260 defined $length && ($length -= $buflength);
261 return $buf
262 }
263 }
264 };
265
266 }
267 else {
268 $boundary = boundary() unless $boundary;
269
270 my $bno = 0;
271 CHECK_BOUNDARY:
272 {
273 for (@parts) {
274 if (index($_, $boundary) >= 0) {
275 # must have a better boundary
276 $boundary = boundary(++$bno);
277 redo CHECK_BOUNDARY;
278 }
279 }
280 last;
281 }
282 $content = "--$boundary$CRLF" .
283 join("$CRLF--$boundary$CRLF", @parts) .
284 "$CRLF--$boundary--$CRLF";
285 }
286
287 wantarray ? ($content, $boundary) : $content;
288}
289
290
291sub boundary
292{
293 my $size = shift || return "xYzZY";
294 require MIME::Base64;
295 my $b = MIME::Base64::encode(join("", map chr(rand(256)), 1..$size*3), "");
296 $b =~ s/[\W]/X/g; # ensure alnum only
297 $b;
298}
299
3001;
301
302__END__
303
304=head1 NAME
305
306HTTP::Request::Common - Construct common HTTP::Request objects
307
308=head1 SYNOPSIS
309
310 use HTTP::Request::Common;
311 $ua = LWP::UserAgent->new;
312 $ua->request(GET 'http://www.sn.no/');
313 $ua->request(POST 'http://somewhere/foo', [foo => bar, bar => foo]);
314
315=head1 DESCRIPTION
316
317This module provide functions that return newly created C<HTTP::Request>
318objects. These functions are usually more convenient to use than the
319standard C<HTTP::Request> constructor for the most common requests. The
320following functions are provided:
321
322=over 4
323
324=item GET $url
325
326=item GET $url, Header => Value,...
327
328The GET() function returns an C<HTTP::Request> object initialized with
329the "GET" method and the specified URL. It is roughly equivalent to the
330following call
331
332 HTTP::Request->new(
333 GET => $url,
334 HTTP::Headers->new(Header => Value,...),
335 )
336
337but is less cluttered. What is different is that a header named
338C<Content> will initialize the content part of the request instead of
339setting a header field. Note that GET requests should normally not
340have a content, so this hack makes more sense for the PUT() and POST()
341functions described below.
342
343The get(...) method of C<LWP::UserAgent> exists as a shortcut for
344$ua->request(GET ...).
345
346=item HEAD $url
347
348=item HEAD $url, Header => Value,...
349
350Like GET() but the method in the request is "HEAD".
351
352The head(...) method of "LWP::UserAgent" exists as a shortcut for
353$ua->request(HEAD ...).
354
355=item PUT $url
356
357=item PUT $url, Header => Value,...
358
359=item PUT $url, Header => Value,..., Content => $content
360
361Like GET() but the method in the request is "PUT".
362
363The content of the request can be specified using the "Content"
364pseudo-header. This steals a bit of the header field namespace as
365there is no way to directly specify a header that is actually called
366"Content". If you really need this you must update the request
367returned in a separate statement.
368
369=item DELETE $url
370
371=item DELETE $url, Header => Value,...
372
373Like GET() but the method in the request is "DELETE". This function
374is not exported by default.
375
376=item POST $url
377
378=item POST $url, Header => Value,...
379
380=item POST $url, $form_ref, Header => Value,...
381
382=item POST $url, Header => Value,..., Content => $form_ref
383
384=item POST $url, Header => Value,..., Content => $content
385
386This works mostly like PUT() with "POST" as the method, but this
387function also takes a second optional array or hash reference
388parameter $form_ref. As for PUT() the content can also be specified
389directly using the "Content" pseudo-header, and you may also provide
390the $form_ref this way.
391
392The $form_ref argument can be used to pass key/value pairs for the
393form content. By default we will initialize a request using the
394C<application/x-www-form-urlencoded> content type. This means that
395you can emulate an HTML E<lt>form> POSTing like this:
396
397 POST 'http://www.perl.org/survey.cgi',
398 [ name => 'Gisle Aas',
399 email => '[email protected]',
400 gender => 'M',
401 born => '1964',
402 perc => '3%',
403 ];
404
405This will create an HTTP::Request object that looks like this:
406
407 POST http://www.perl.org/survey.cgi
408 Content-Length: 66
409 Content-Type: application/x-www-form-urlencoded
410
411 name=Gisle%20Aas&email=gisle%40aas.no&gender=M&born=1964&perc=3%25
412
413Multivalued form fields can be specified by either repeating the field
414name or by passing the value as an array reference.
415
416The POST method also supports the C<multipart/form-data> content used
417for I<Form-based File Upload> as specified in RFC 1867. You trigger
418this content format by specifying a content type of C<'form-data'> as
419one of the request headers. If one of the values in the $form_ref is
420an array reference, then it is treated as a file part specification
421with the following interpretation:
422
423 [ $file, $filename, Header => Value... ]
424 [ undef, $filename, Header => Value,..., Content => $content ]
425
426The first value in the array ($file) is the name of a file to open.
427This file will be read and its content placed in the request. The
428routine will croak if the file can't be opened. Use an C<undef> as
429$file value if you want to specify the content directly with a
430C<Content> header. The $filename is the filename to report in the
431request. If this value is undefined, then the basename of the $file
432will be used. You can specify an empty string as $filename if you
433want to suppress sending the filename when you provide a $file value.
434
435If a $file is provided by no C<Content-Type> header, then C<Content-Type>
436and C<Content-Encoding> will be filled in automatically with the values
437returned by LWP::MediaTypes::guess_media_type()
438
439Sending my F<~/.profile> to the survey used as example above can be
440achieved by this:
441
442 POST 'http://www.perl.org/survey.cgi',
443 Content_Type => 'form-data',
444 Content => [ name => 'Gisle Aas',
445 email => '[email protected]',
446 gender => 'M',
447 born => '1964',
448 init => ["$ENV{HOME}/.profile"],
449 ]
450
451This will create an HTTP::Request object that almost looks this (the
452boundary and the content of your F<~/.profile> is likely to be
453different):
454
455 POST http://www.perl.org/survey.cgi
456 Content-Length: 388
457 Content-Type: multipart/form-data; boundary="6G+f"
458
459 --6G+f
460 Content-Disposition: form-data; name="name"
461
462 Gisle Aas
463 --6G+f
464 Content-Disposition: form-data; name="email"
465
466 [email protected]
467 --6G+f
468 Content-Disposition: form-data; name="gender"
469
470 M
471 --6G+f
472 Content-Disposition: form-data; name="born"
473
474 1964
475 --6G+f
476 Content-Disposition: form-data; name="init"; filename=".profile"
477 Content-Type: text/plain
478
479 PATH=/local/perl/bin:$PATH
480 export PATH
481
482 --6G+f--
483
484If you set the $DYNAMIC_FILE_UPLOAD variable (exportable) to some TRUE
485value, then you get back a request object with a subroutine closure as
486the content attribute. This subroutine will read the content of any
487files on demand and return it in suitable chunks. This allow you to
488upload arbitrary big files without using lots of memory. You can even
489upload infinite files like F</dev/audio> if you wish; however, if
490the file is not a plain file, there will be no Content-Length header
491defined for the request. Not all servers (or server
492applications) like this. Also, if the file(s) change in size between
493the time the Content-Length is calculated and the time that the last
494chunk is delivered, the subroutine will C<Croak>.
495
496The post(...) method of "LWP::UserAgent" exists as a shortcut for
497$ua->request(POST ...).
498
499=back
500
501=head1 SEE ALSO
502
503L<HTTP::Request>, L<LWP::UserAgent>
504
505
506=head1 COPYRIGHT
507
508Copyright 1997-2004, Gisle Aas
509
510This library is free software; you can redistribute it and/or
511modify it under the same terms as Perl itself.
512
513=cut
514
Note: See TracBrowser for help on using the repository browser.