~ubuntu-branches/ubuntu/raring/libwww-perl/raring

« back to all changes in this revision

Viewing changes to lib/HTTP/Request/Common.pm

  • Committer: Bazaar Package Importer
  • Author(s): Nicholas Bamber
  • Date: 2011-04-02 15:13:32 UTC
  • mfrom: (1.4.8 upstream)
  • Revision ID: james.westby@ubuntu.com-20110402151332-tpkr18gfx51ccjn2
Tags: 6.01-1
* New upstream release 
  - Modules not in the LWP namespace have been made into separate modules
  - In particular IPv6 issues now handled by libnet-http-perl,
    see #306914, (Closes: #614948) 
  - Packages using HTTP::Daemon should declare the appropriate
    dependency on libhttp-daemon-perl | libwww-perl (<< 6).
  - Packages using HTML::Form should declare the appropriate
    dependency on libhtml-form-perl | libwww-perl (<< 6).
  - Other depending packages can safely continue to depend on libwww-perl,
    but in some cases may be able to tighten up their dependencies.
  - LWP::Protocol::https will be split off in the next release
    so now liblwp-protocol-https-perl is Provided.
* Updated dependencies
* New upstream release
* Removed unnecessary versioned dependency on libio-compress-perl
* Patched LWP::Protocol::https to use ca-certificates rather than 
  Mozilla::CA (Closes: #619059)

Show diffs side-by-side

added added

removed removed

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