File Coverage

blib/lib/HTTP/Request/StreamingUpload.pm
Criterion Covered Total %
statement 48 48 100.0
branch 21 22 95.4
condition 7 8 87.5
subroutine 8 8 100.0
pod 1 2 50.0
total 85 88 96.5


line stmt bran cond sub pod time code
1             package HTTP::Request::StreamingUpload;
2 3     3   13486 use strict;
  3         5  
  3         158  
3 3     3   17 use warnings;
  3         5  
  3         158  
4             our $VERSION = '0.01';
5              
6 3     3   17 use Carp ();
  3         5  
  3         57  
7 3     3   3279 use HTTP::Request;
  3         101198  
  3         1154  
8              
9             sub new {
10 10     10 1 536 my($class, $method, $uri, %args) = @_;
11              
12 10         26 my $headers = $args{headers};
13 10 100       32 if ($headers) {
14 3 100       12 if (ref $headers eq 'HASH') {
15 1         3 $headers = +[ %{ $headers } ];
  1         5  
16             }
17             }
18              
19 10         69 my $req = HTTP::Request->new($method, $uri, $headers);
20 10         22294 _set_content($req, \%args);
21 10         159 $req;
22             }
23              
24             sub _set_content {
25 10     10   24 my($req, $args) = @_;
26              
27 10 100 66     128 if ($args->{content}) {
    100 100        
    100          
28 1         6 $req->content($args->{content});
29             } elsif ($args->{callback} && ref($args->{callback}) eq 'CODE') {
30 1         6 $req->content($args->{callback});
31             } elsif ($args->{path} || $args->{fh}) {
32 3         7 my $fh;
33 3 100       11 if ($args->{fh}) {
34 2         6 $fh = $args->{fh};
35             } else {
36 1 50       60 open $fh, '<', $args->{path} or Carp::croak "$args->{path}: $!";
37             }
38 3   100     19 my $chunk_size = $args->{chunk_size} || 4096;
39             $req->content(sub {
40 5     5   134 my $len = read($fh, my $buf, $chunk_size);
41 5 100       15 return unless $len;
42 3         12 return $buf;
43 3         32 });
44             }
45             }
46              
47             # some code takes by LWP::Protocol::http->request
48             sub slurp {
49 4     4 0 38 my(undef, $req) = @_;
50 4         14 my $content_ref = $req->content_ref;
51 4 100       45 $content_ref = ${ $content_ref } if ref ${ $content_ref };
  3         8  
  4         16  
52              
53 4         5 my $content;
54 4 100       16 if (ref($content_ref) eq 'CODE') {
55 3         4 while (1) {
56 12         24 my $buf = $content_ref->();
57 12 100       42 last unless defined $buf;
58 9         30 $content .= $buf;
59             }
60             } else {
61 1         3 $content = ${ $content_ref };
  1         3  
62             }
63 4         22 $content;
64             }
65              
66             1;
67             __END__
68              
69             =for stopwords filepath callback chunked HeaderName HeaderValue fh
70              
71             =head1 NAME
72              
73             HTTP::Request::StreamingUpload - streaming upload wrapper for HTTP::Request
74              
75             =head1 SYNOPSIS
76              
77             =head2 upload from filepath
78              
79             my $req = HTTP::Request::StreamingUpload->new(
80             PUT => 'http://example.com/foo.cgi',
81             path => '/your/upload.jpg',
82             headers => HTTP::Headers->new(
83             'Content-Type' => 'image/jpeg',
84             'Content-Length' => -s '/your/upload.jpg',
85             ),
86             );
87             my $res = LWP::UserAgent->new->request($req);
88              
89             =head2 upload from filehandle
90              
91             open my $fh, '<', '/your/upload/requestbody' or die $!;
92             my $req = HTTP::Request::StreamingUpload->new(
93             PUT => 'http://example.com/foo.cgi',
94             fh => $fh,
95             headers => HTTP::Headers->new(
96             'Content-Length' => -s $fh,
97             ),
98             );
99             my $res = LWP::UserAgent->new->request($req);
100              
101             =head2 upload from callback
102              
103             my @chunk = qw( foo bar baz );
104             my $req = HTTP::Request::StreamingUpload->new(
105             PUT => 'http://example.com/foo.cgi',
106             callback => sub { shift @chunk },
107             headers => HTTP::Headers->new(
108             'Content-Type' => 'text/plain',
109             'Content-Length' => 9,
110             ),
111             );
112             my $res = LWP::UserAgent->new->request($req);
113              
114             =head1 DESCRIPTION
115              
116             HTTP::Request::StreamingUpload is streaming upload wrapper for L<HTTP::Request>.
117             It could be alike when $DYNAMIC_FILE_UPLOAD of L<HTTP::Request::Common> was used.
118             However, it is works only for POST method with form-data.
119             HTTP::Request::StreamingUpload works on the all HTTP methods.
120              
121             Of course, you can big file upload using few memory by this wrapper.
122              
123             =head1 HTTP::Request::StreamingUpload->new( $method, $uir, %args );
124              
125             =head2 %args Options
126              
127             =over 4
128              
129             =item headers => [ HeaderName => 'HeaderValue', ... ]
130              
131             =item headers => { HeaderName => 'HeaderValue', ... }
132              
133             =item headers => HTTP::Headers->new( HeaderName => 'HeaderValue', ... )
134              
135             header is passed. HASHREF, ARRAYREF or L<HTTP::Headers> object can be passed.
136              
137             If you are possible, you should set up C<Content-Length> for file upload.
138             However, chunked upload for HTTP 1.1 will be performed by L<LWP::UserAgent> if it does not set up.
139              
140             =item path => '/your/file.txt'
141              
142             set the upload file path.
143              
144             =item fh => $fh
145              
146             set the file-handle of upload file.
147             It can use instead of C<path>.
148              
149             =item chunk_size => 4096
150              
151             set the buffer size when reading the file of C<fh> or C<path>.
152              
153             =item callback => sub { ...; return if $eof; return $buf }
154              
155             Instead of C<path> or C<fh>, upload data is controlled by itself and can be made.
156              
157             # 10 times send epoch time
158             callback => sub {
159             return if $i++ > 10;
160             return time() . "\n";
161             },
162              
163             =back
164              
165             =head1 AUTHOR
166              
167             Kazuhiro Osawa E<lt>yappo <at> shibuya <döt> plE<gt>
168              
169             =head1 SEE ALSO
170              
171             L<HTTP::Request>,
172             L<HTTP::Request::Common>,
173             L<HTTP::Headers>,
174             L<LWP::UserAgent>
175              
176             =head1 LICENSE
177              
178             This library is free software; you can redistribute it and/or modify
179             it under the same terms as Perl itself.
180              
181             =cut