File Coverage

blib/lib/MogileFS/ClientHTTPFile.pm
Criterion Covered Total %
statement 18 158 11.3
branch 0 66 0.0
condition 0 16 0.0
subroutine 6 20 30.0
pod 0 1 0.0
total 24 261 9.2


line stmt bran cond sub pod time code
1             package MogileFS::ClientHTTPFile;
2              
3 4     4   23 use strict;
  4         7  
  4         156  
4              
5 4     4   21 use LWP::UserAgent;
  4         7  
  4         80  
6 4     4   20 use HTTP::Request;
  4         6  
  4         115  
7 4     4   22 use HTTP::Status;
  4         6  
  4         1717  
8 4     4   24 use Errno qw(EIO EINVAL EPERM);
  4         8  
  4         309  
9              
10 4         33 use fields ('mg',
11             'fid',
12             'devid',
13             'class',
14             'key',
15             'path',
16             'length',
17             'pos',
18             'ua',
19             'eof',
20             'readonly',
21             'readLineChunkSize',
22 4     4   21 );
  4         7  
23              
24              
25             sub TIEHANDLE {
26 0     0     my MogileFS::ClientHTTPFile $self = shift;
27              
28 0 0         $self = fields::new($self) unless ref $self;
29              
30 0           my %args = @_;
31              
32 0           $self->{devid} = $args{devid};
33 0           $self->{path} = $args{path};
34 0   0       $self->{readLineChunkSize} = $args{readLineChunkSize} || 4096;
35              
36 0   0       $args{backup_dests} ||= [];
37              
38 0           my $ua = LWP::UserAgent->new( keep_alive => 60, timeout => 5 );
39              
40 0           while ($self->{path}) {
41 0           my $req;
42             # overwrite needs changing to create if not exists?
43 0 0         if ($args{overwrite}) {
44 0           $req = HTTP::Request->new( PUT => $self->{path} ); # Ensure file overwritten/created, even if they don't print anything
45             } else {
46 0           $req = HTTP::Request->new( HEAD => $self->{path} );
47             }
48              
49 0           my $res = $ua->request( $req );
50              
51 0 0         if ($res->is_success) {
52 0 0         if ($args{overwrite}) {
53 0           $self->{length} = 0;
54             } else {
55 0   0       $self->{length} = $res->header( 'Content-Length' ) || 0;
56             }
57              
58 0           last;
59             } else {
60 0           my $dest = shift @{$args{backup_dests}};
  0            
61              
62 0 0         if ($dest) {
63 0           $self->{devid} = $dest->[0];
64 0           $self->{path} = $dest->[1];
65             } else {
66 0           $self->{devid} = undef;
67 0           $self->{path} = undef;
68             }
69             }
70             }
71              
72 0 0         return unless $self->{path};
73              
74 0           $self->{pos} = 0;
75 0           $self->{ua} = $ua;
76 0           $self->{eof} = 0;
77              
78 0           $self->{mg} = $args{mg};
79 0           $self->{fid} = $args{fid};
80 0           $self->{key} = $args{key};
81 0   0       $self->{readonly} = $args{readonly} || 0;
82              
83 0           return $self;
84             }
85             *new = *TIEHANDLE;
86              
87             sub READ {
88 0     0     my MogileFS::ClientHTTPFile $self = shift;
89 0           my $buf = \$_[0]; shift;
  0            
90 0           my ($len, $offset) = @_;
91              
92 0 0         defined( $$buf ) or $$buf = '';
93 0 0         defined( $offset ) or $offset = 0;
94              
95 0 0         if ($len == 0) {
96 0           $$buf = '';
97 0           return 0;
98             }
99              
100 0 0         die "Negative len [$len] passed" if $len < 0;
101              
102 0 0         die "Negative offset [$offset] not supported" if $offset < 0;
103              
104 0 0         return 0 if ($self->EOF);
105              
106 0           my $start = $self->{pos};
107 0           my $end = $self->{pos} + $len - 1;
108              
109 0           my $req = HTTP::Request->new(GET => $self->{path}, [
110             Range => "bytes=$start-$end",
111             ], );
112              
113 0           my $res = $self->{ua}->request( $req );
114              
115 0 0         if ($res->is_error) {
116 0 0         if ($res->code eq RC_REQUEST_RANGE_NOT_SATISFIABLE) {
117 0           $self->{eof} = 1;
118 0           return 0;
119             }
120            
121 0           $! = EIO;
122 0           return;
123             }
124              
125 0           my $length = length( $res->content );
126              
127 0           $self->{pos} += $length;
128              
129             # Behaviour is not correct with offsets < length of existing buffer
130 0 0         if ($offset) {
131 0           $$buf = substr($$buf, 0, $offset) . $res->content;
132             } else {
133 0           $$buf = $res->content;
134             }
135              
136 0           return $length;
137             }
138             *read = *READ;
139              
140             sub WRITE {
141 0     0     my MogileFS::ClientHTTPFile $self = shift;
142              
143 0           my ($buf, $len, $offset) = @_;
144              
145 0 0         if ($self->{readonly}) {
146 0           $! = EPERM;
147 0           return;
148             }
149              
150 0 0 0       if (defined $len || defined $offset) {
151 0 0         $offset = 0 if ! defined $offset;
152              
153 0           $buf = substr($buf, $offset, $len);
154             }
155              
156 0           $len = length($buf);
157              
158 0           my $start = $self->{pos};
159 0           my $end = $self->{pos} + $len - 1;
160              
161 0           my $req = HTTP::Request->new(PUT => $self->{path}, [
162             'Content-Range' => "bytes $start-$end/*",
163             ], );
164              
165 0           $req->add_content($buf);
166              
167 0           my $res = $self->{ua}->request( $req );
168              
169 0 0         if ($res->is_error) {
170 0           $! = EIO;
171 0           return;
172             }
173              
174 0 0         if ($self->{pos} + $len > $self->{length}) {
175 0           $self->{length} = $self->{pos} + $len;
176             }
177              
178 0           $self->{pos} += $len;
179              
180 0 0         $self->{eof} = ($self->{pos} == $self->{length} ? 1 :0);
181              
182 0           return $len;
183             }
184             *write = *WRITE;
185              
186             sub EOF {
187 0     0     my MogileFS::ClientHTTPFile $self = shift;
188              
189 0 0         return 1 if $self->{eof};
190              
191 0 0         return unless $self->{length};
192              
193 0           return $self->{pos} >= $self->{length};
194             }
195             *eof = *EOF;
196              
197             sub TELL {
198 0     0     my MogileFS::ClientHTTPFile $self = shift;
199              
200 0           return $self->{pos};
201             }
202             *tell = *TELL;
203              
204             sub SEEK {
205 0     0     my MogileFS::ClientHTTPFile $self = shift;
206              
207 0           my ($offset, $whence) = @_;
208              
209 0 0         if ($whence == 1) {
    0          
210 0           $offset += $self->{pos};
211             } elsif ($whence == 2) {
212 0           $offset += $self->{length};
213             }
214              
215 0 0         if ($offset > $self->{length}) {
216 0           $! = EINVAL;
217 0           return 0;
218             }
219              
220 0           $self->{pos} = $offset;
221 0 0         $self->{eof} = ($self->{pos} == $self->{length} ? 1 :0);
222              
223 0           return 1;
224             }
225             *seek = *SEEK;
226              
227             sub GETC {
228 0     0     my MogileFS::ClientHTTPFile $self = shift;
229              
230 0           $self->READ( my $buf, 1 );
231            
232 0           return $buf;
233             }
234             *getc = *GETC;
235              
236             sub PRINT {
237 0     0     my MogileFS::ClientHTTPFile $self = shift;
238              
239 0 0         my $buf = join(defined $, ? $, : "", @_);
240              
241 0 0         $buf .= $\ if defined $\;
242              
243 0           $self->WRITE($buf, length($buf), 0);
244             }
245             *print = *PRINT;
246              
247             sub PRINTF {
248 0     0     my MogileFS::ClientHTTPFile $self = shift;
249            
250 0           my $buf = sprintf(shift,@_);
251              
252 0           $self->WRITE($buf,length($buf),0);
253             }
254             *printf = *PRINTF;
255              
256             sub CLOSE {
257 0     0     my MogileFS::ClientHTTPFile $self = shift;
258              
259 0 0         if ($self->{devid}) {
260 0           my $mg = $self->{mg};
261              
262 0           my $rv = $mg->{backend}->do_request
263             ("create_close", {
264             fid => $self->{fid},
265             devid => $self->{devid},
266             domain => $mg->{domain},
267             size => $self->{length},
268             key => $self->{key},
269             path => $self->{path},
270             });
271            
272 0 0         unless ($rv) {
273 0           $@ = "$mg->{backend}->{lasterr}: $mg->{backend}->{lasterrstr}";
274 0           return undef;
275             }
276             }
277              
278 0           return 1;
279             }
280             *close = *CLOSE;
281              
282             sub BINMODE {
283 0     0     return 1;
284             }
285             *binmode = *BINMODE;
286              
287             sub FILENO {
288             # Wanted by perl debugger
289 0     0     return -1;
290             }
291             *fileno = *FILENO;
292              
293             # Must return undef (not just '') on EOF
294             sub READLINE {
295 0     0     my MogileFS::ClientHTTPFile $self = shift;
296              
297 0           my $retBuff;
298 0           my $startPos = $self->{pos};
299 0           my $foundEol;
300             READ:
301 0           while (!$self->EOF) {
302 0           my $readBuff;
303 0           my $rc = $self->read($readBuff, $self->{readLineChunkSize});
304             # Undef $/ => we will only exit on EOF (which should be right)
305 0 0         $foundEol = index($readBuff, $/) if defined $/;
306 0 0 0       if (defined($foundEol) && $foundEol >= 0) {
307 0           $foundEol += length($/);
308 0   0       $retBuff ||= '';
309 0           $retBuff .= substr($readBuff, 0, $foundEol);
310             # We have over-read, so go back
311 0           $self->seek($startPos + length($retBuff) , 0);
312 0           last READ;
313             }
314             else {
315             # Go round again
316 0           $retBuff .= $readBuff;
317             }
318             }
319 0           return $retBuff;
320             }
321             *readline = *READLINE;
322              
323             sub path {
324 0     0 0   my MogileFS::ClientHTTPFile $self = shift;
325              
326 0           return $self->{path};
327             }
328              
329             1;