File Coverage

blib/lib/CPANPLUS/Internals/Source/MetaCPAN/HTTP.pm
Criterion Covered Total %
statement 227 394 57.6
branch 71 178 39.8
condition 18 50 36.0
subroutine 21 36 58.3
pod 20 31 64.5
total 357 689 51.8


line stmt bran cond sub pod time code
1             package CPANPLUS::Internals::Source::MetaCPAN::HTTP;
2             {
3             $CPANPLUS::Internals::Source::MetaCPAN::HTTP::VERSION = '0.08';
4             }
5              
6             #ABSTRACT: Lightweight HTTP implementation
7              
8 1     1   973 use 5.005;
  1         3  
  1         47  
9 1     1   7 use strict;
  1         2  
  1         40  
10 1     1   6 use Socket 1.3;
  1         24  
  1         1266  
11 1     1   8 use Fcntl;
  1         1  
  1         438  
12 1     1   7 use Errno qw(EAGAIN);
  1         9  
  1         5385  
13              
14             my $BLOCKSIZE = 65536;
15             my $CRLF = "\r\n";
16             my $URLENCODE_VALID = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.";
17              
18             # Forward declarations
19             sub prepare_post;
20             sub http_write;
21             sub http_readline;
22             sub http_read;
23             sub http_readbytes;
24              
25             # Prepare the urlencode validchars lookup hash
26             my @urlencode_valid;
27             foreach my $char (split('', $URLENCODE_VALID)) {
28             $urlencode_valid[ord $char]=$char;
29             }
30             for (my $n=0;$n<255;$n++) {
31             if (!defined($urlencode_valid[$n])) {
32             $urlencode_valid[$n]=sprintf("%%%02X", $n);
33             }
34             }
35              
36             sub new
37             {
38 3     3 1 28 my $self = {};
39 3         12 bless $self;
40 3         14 $self->initialize();
41 3         12 return $self;
42             }
43              
44             sub initialize
45             {
46 3     3 0 6 my $self = shift;
47 3         20 $self->reset;
48 3         9 $self->{timeout} = 120;
49 3         11 $self->{HTTP11} = 0;
50 3         11 $self->{DEBUG} = 0;
51 3         5 $self->{header_at_once} = 0;
52 3         7 $self->{holdback} = 0; # needed for http_write
53             }
54              
55             sub header_at_once
56             {
57 0     0 0 0 my $self=shift;
58 0         0 $self->{header_at_once} = 1;
59             }
60              
61             sub local_addr
62             {
63 0     0 1 0 my $self = shift;
64 0         0 my $val = shift;
65 0         0 my $oldval = $self->{'local_addr'};
66 0 0       0 if (defined($val)) {
67 0         0 $self->{'local_addr'} = $val;
68             }
69 0         0 return $oldval;
70             }
71              
72             sub local_port
73             {
74 0     0 1 0 my $self = shift;
75 0         0 my $val = shift;
76 0         0 my $oldval = $self->{'local_port'};
77 0 0       0 if (defined($val)) {
78 0         0 $self->{'local_port'} = $val;
79             }
80 0         0 return $oldval;
81             }
82              
83             sub method
84             {
85 0     0 1 0 my $self = shift;
86 0         0 my $method = shift;
87 0         0 $method = uc($method);
88 0         0 $self->{method} = $method;
89             }
90              
91             sub DEBUG
92             {
93 0     0 0 0 my $self = shift;
94 0 0       0 if ($self->{DEBUG}) {
95 0         0 print STDERR join(" ", @_),"\n";
96             }
97             }
98              
99             sub reset
100             {
101 6     6 1 12 my $self = shift;
102 6         20 foreach my $var ("body", "request", "content", "status", "proxy",
103             "proxyport", "resp-protocol", "error-message",
104             "resp-headers", "CBARGS", "callback_function", "callback_params")
105             {
106 72         171 $self->{$var} = undef;
107             }
108 6         28 $self->{HTTPReadBuffer} = "";
109 6         16 $self->{method} = "GET";
110 6         32 $self->{headers} = { 'user-agent' => "CPANPLUS-Internals-Source-MetaCPAN/$CPANPLUS::Internals::Source::MetaCPAN::HTTP::VERSION" };
111 6         30 $self->{headermap} = { 'user-agent' => 'User-Agent' };
112             }
113              
114              
115             # URL-encode data
116             sub escape {
117 0     0 0 0 my $toencode = shift;
118 0         0 return join('',
119 0         0 map { $urlencode_valid[ord $_] } split('', $toencode));
120             }
121              
122             sub set_callback {
123 0     0 1 0 my ($self, $callback, @callbackparams) = @_;
124 0         0 $self->{'callback_function'} = $callback;
125 0         0 $self->{'callback_params'} = [ @callbackparams ];
126             }
127              
128             sub request
129             {
130 4     4 1 13 my ($self, $url, $data_callback, $cbargs) = @_;
131              
132 4         12 my $method = $self->{method};
133 4 50       18 if (defined($cbargs)) {
134 0         0 $self->{CBARGS} = $cbargs;
135             }
136              
137 4         10 my $callback_func = $self->{'callback_function'};
138 4         8 my $callback_params = $self->{'callback_params'};
139              
140             # Parse URL
141 4         57 my ($protocol,$host,$junk,$port,$object) =
142             $url =~ m{^([^:/]+)://([^/:]*)(:(\d+))?(/.*)$};
143              
144             # Only HTTP is supported here
145 4 50       16 if ($protocol ne "http")
146             {
147 0         0 warn "Only http is supported by HTTP::Lite";
148 0         0 return undef;
149             }
150              
151             # Setup the connection
152 4         1326 my $proto = getprotobyname('tcp');
153 4         23 local *FH;
154 4         167 socket(FH, PF_INET, SOCK_STREAM, $proto);
155 4 50       16 $port = 80 if !$port;
156              
157 4   33     33 my $connecthost = $self->{'proxy'} || $host;
158 4 50       13 $connecthost = $connecthost ? $connecthost : $host;
159 4   33     49 my $connectport = $self->{'proxyport'} || $port;
160 4 50       10 $connectport = $connectport ? $connectport : $port;
161 4         12521 my $addr = inet_aton($connecthost);
162 4 50       23 if (!$addr) {
163 0         0 close(FH);
164 0         0 return undef;
165             }
166 4 50       16 if ($connecthost ne $host)
167             {
168             # if proxy active, use full URL as object to request
169 0         0 $object = "$url";
170             }
171              
172             # choose local port and address
173 4         14 my $local_addr = INADDR_ANY;
174 4         181 my $local_port = "0";
175 4 50       33 if (defined($self->{'local_addr'})) {
176 0         0 $local_addr = $self->{'local_addr'};
177 0 0 0     0 if ($local_addr eq "0.0.0.0" || $local_addr eq "0") {
178 0         0 $local_addr = INADDR_ANY;
179             } else {
180 0         0 $local_addr = inet_aton($local_addr);
181             }
182             }
183 4 50       26 if (defined($self->{'local_port'})) {
184 0         0 $local_port = $self->{'local_port'};
185             }
186 4         31 my $paddr = pack_sockaddr_in($local_port, $local_addr);
187 4 50       61 bind(FH, $paddr) || return undef; # Failing to bind is fatal.
188              
189 4         38 my $sin = sockaddr_in($connectport,$addr);
190 4 50       5931 connect(FH, $sin) || return undef;
191             # Set nonblocking IO on the handle to allow timeouts
192 4 50       31 if ( $^O ne "MSWin32" ) {
193 4         100 fcntl(FH, F_SETFL, O_NONBLOCK);
194             }
195              
196 4 50       16 if (defined($callback_func)) {
197 0         0 &$callback_func($self, "connect", undef, @$callback_params);
198             }
199              
200 4 50       19 if ($self->{header_at_once}) {
201 0         0 $self->{holdback} = 1; # http_write should buffer only, no sending yet
202             }
203              
204             # Start the request (HTTP/1.1 mode)
205 4 50       27 if ($self->{HTTP11}) {
206 0         0 $self->http_write(*FH, "$method $object HTTP/1.1$CRLF");
207             } else {
208 4         49 $self->http_write(*FH, "$method $object HTTP/1.0$CRLF");
209             }
210              
211             # Add some required headers
212             # we only support a single transaction per request in this version.
213 4         25 $self->add_req_header("Connection", "close");
214 4 50       14 if ($port != 80) {
215 0         0 $self->add_req_header("Host", "$host:$port");
216             } else {
217 4         14 $self->add_req_header("Host", $host);
218             }
219 4 50       23 if (!defined($self->get_req_header("Accept"))) {
220 4         10 $self->add_req_header("Accept", "*/*");
221             }
222              
223 4 50       15 if ($method eq 'POST') {
224 0         0 $self->http_write(*FH, "Content-Type: application/x-www-form-urlencoded$CRLF");
225             }
226              
227             # Purge a couple others
228 4         16 $self->delete_req_header("Content-Type");
229 4         10 $self->delete_req_header("Content-Length");
230              
231             # Output headers
232 4         19 foreach my $header ($self->enum_req_headers())
233             {
234 16         36 my $value = $self->get_req_header($header);
235 16         67 $self->http_write(*FH, $self->{headermap}{$header}.": ".$value."$CRLF");
236             }
237              
238 4         12 my $content_length;
239 4 50       22 if (defined($self->{content}))
240             {
241 0         0 $content_length = length($self->{content});
242             }
243 4 50       9 if (defined($callback_func)) {
244 0         0 my $ncontent_length = &$callback_func($self, "content-length", undef, @$callback_params);
245 0 0       0 if (defined($ncontent_length)) {
246 0         0 $content_length = $ncontent_length;
247             }
248             }
249              
250 4 50       11 if ($content_length) {
251 0         0 $self->http_write(*FH, "Content-Length: $content_length$CRLF");
252             }
253              
254 4 50       10 if (defined($callback_func)) {
255 0         0 &$callback_func($self, "done-headers", undef, @$callback_params);
256             }
257             # End of headers
258 4         16 $self->http_write(*FH, "$CRLF");
259              
260 4 50       17 if ($self->{header_at_once}) {
261 0         0 $self->{holdback} = 0;
262 0         0 $self->http_write(*FH, ""); # pseudocall to get http_write going
263             }
264              
265 4         14 my $content_out = 0;
266 4 50       17 if (defined($callback_func)) {
267 0         0 while (my $content = &$callback_func($self, "content", undef, @$callback_params)) {
268 0         0 $self->http_write(*FH, $content);
269 0         0 $content_out++;
270             }
271             }
272              
273             # Output content, if any
274 4 50 33     34 if (!$content_out && defined($self->{content}))
275             {
276 0         0 $self->http_write(*FH, $self->{content});
277             }
278              
279 4 50       11 if (defined($callback_func)) {
280 0         0 &$callback_func($self, "content-done", undef, @$callback_params);
281             }
282              
283              
284             # Read response from server
285 4         8 my $headmode=1;
286 4         6 my $chunkmode=0;
287 4         8 my $chunksize=0;
288 4         6 my $chunklength=0;
289 4         6 my $chunk;
290 4         7 my $line = 0;
291 4         6 my $data;
292 4         22 while ($data = $self->http_read(*FH,$headmode,$chunkmode,$chunksize))
293             {
294 47 50       183 $self->{DEBUG} && $self->DEBUG("reading: $chunkmode, $chunksize, $chunklength, $headmode, ".
295             length($self->{'body'}));
296 47 50       113 if ($self->{DEBUG}) {
297 0         0 foreach my $var ("body", "request", "content", "status", "proxy",
298             "proxyport", "resp-protocol", "error-message",
299             "resp-headers", "CBARGS", "HTTPReadBuffer")
300             {
301 0         0 $self->DEBUG("state $var ".length($self->{$var}));
302             }
303             }
304 47         57 $line++;
305 47 100       107 if ($line == 1)
306             {
307 4         29 my ($proto,$status,$message) = split(' ', $$data, 3);
308 4 50       16 $self->{DEBUG} && $self->DEBUG("header $$data");
309 4         13 $self->{status}=$status;
310 4         11 $self->{'resp-protocol'}=$proto;
311 4         11 $self->{'error-message'}=$message;
312 4         28 next;
313             }
314 43 100 66     411 if (($headmode || $chunkmode eq "entity-header") && $$data =~ /^[\r\n]*$/)
      100        
315             {
316 4 50       11 if ($chunkmode)
317             {
318 0         0 $chunkmode = 0;
319             }
320 4         9 $headmode = 0;
321              
322             # Check for Transfer-Encoding
323 4         20 my $te = $self->get_header("Transfer-Encoding");
324 4 50       14 if (defined($te)) {
325 0         0 my $header = join(' ',@{$te});
  0         0  
326 0 0       0 if ($header =~ /chunked/i)
327             {
328 0         0 $chunkmode = "chunksize";
329             }
330             }
331 4         11 next;
332             }
333 39 100 66     150 if ($headmode || $chunkmode eq "entity-header")
    50          
334             {
335 28         151 my ($var,$datastr) = $$data =~ /^([^:]*):\s*(.*)$/;
336 28 50       83 if (defined($var))
337             {
338 28         100 $datastr =~s/[\r\n]$//g;
339 28         57 $var = lc($var);
340 28         100 $var =~ s/^(.)/&upper($1)/ge;
  28         72  
341 28         117 $var =~ s/(-.)/&upper($1)/ge;
  12         27  
342 28         34 my $hr = ${$self->{'resp-headers'}}{$var};
  28         67  
343 28 50       61 if (!ref($hr))
344             {
345 28         69 $hr = [ $datastr ];
346             }
347             else
348             {
349 0         0 push @{ $hr }, $datastr;
  0         0  
350             }
351 28         38 ${$self->{'resp-headers'}}{$var} = $hr;
  28         419  
352             }
353             } elsif ($chunkmode)
354             {
355 0 0       0 if ($chunkmode eq "chunksize")
    0          
    0          
356             {
357 0         0 $chunksize = $$data;
358 0         0 $chunksize =~ s/^\s*|;.*$//g;
359 0         0 $chunksize =~ s/\s*$//g;
360 0         0 my $cshx = $chunksize;
361 0 0       0 if (length($chunksize) > 0) {
362             # read another line
363 0 0       0 if ($chunksize !~ /^[a-f0-9]+$/i) {
364 0 0       0 $self->{DEBUG} && $self->DEBUG("chunksize not a hex string");
365             }
366 0         0 $chunksize = hex($chunksize);
367 0 0       0 $self->{DEBUG} && $self->DEBUG("chunksize was $chunksize (HEX was $cshx)");
368 0 0       0 if ($chunksize == 0)
369             {
370 0         0 $chunkmode = "entity-header";
371             } else {
372 0         0 $chunkmode = "chunk";
373 0         0 $chunklength = 0;
374             }
375             } else {
376 0 0       0 $self->{DEBUG} && $self->DEBUG("chunksize empty string, checking next line!");
377             }
378             } elsif ($chunkmode eq "chunk")
379             {
380 0         0 $chunk .= $$data;
381 0         0 $chunklength += length($$data);
382 0 0       0 if ($chunklength >= $chunksize)
383             {
384 0         0 $chunkmode = "chunksize";
385 0 0 0     0 if ($chunklength > $chunksize)
    0          
386             {
387 0         0 $chunk = substr($chunk,0,$chunksize);
388             }
389             elsif ($chunklength == $chunksize && $chunk !~ /$CRLF$/)
390             {
391             # chunk data is exactly chunksize -- need CRLF still
392 0         0 $chunkmode = "ignorecrlf";
393             }
394 0         0 $self->add_to_body(\$chunk, $data_callback);
395 0         0 $chunk="";
396 0         0 $chunklength = 0;
397 0         0 $chunksize = "";
398             }
399             } elsif ($chunkmode eq "ignorecrlf")
400             {
401 0         0 $chunkmode = "chunksize";
402             }
403             } else {
404 11         52 $self->add_to_body($data, $data_callback);
405             }
406             }
407 4 50       15 if (defined($callback_func)) {
408 0         0 &$callback_func($self, "done", undef, @$callback_params);
409             }
410 4         586 close(FH);
411 4         156 return $self->{status};
412             }
413              
414             sub add_to_body
415             {
416 11     11 0 17 my $self = shift;
417 11         18 my ($dataref, $data_callback) = @_;
418              
419 11         22 my $callback_func = $self->{'callback_function'};
420 11         21 my $callback_params = $self->{'callback_params'};
421              
422 11 50 33     53 if (!defined($data_callback) && !defined($callback_func)) {
423 11 50       27 $self->{DEBUG} && $self->DEBUG("no callback");
424 11         876 $self->{'body'}.=$$dataref;
425             } else {
426 0         0 my $newdata;
427 0 0       0 if (defined($callback_func)) {
428 0         0 $newdata = &$callback_func($self, "data", $dataref, @$callback_params);
429             } else {
430 0         0 $newdata = &$data_callback($self, $dataref, $self->{CBARGS});
431             }
432 0 0       0 if ($self->{DEBUG}) {
433 0         0 $self->DEBUG("callback got back a ".ref($newdata));
434 0 0       0 if (ref($newdata) eq "SCALAR") {
435 0         0 $self->DEBUG("callback got back ".length($$newdata)." bytes");
436             }
437             }
438 0 0 0     0 if (defined($newdata) && ref($newdata) eq "SCALAR") {
439 0         0 $self->{'body'} .= $$newdata;
440             }
441             }
442             }
443              
444             sub add_req_header
445             {
446 12     12 1 20 my $self = shift;
447 12         25 my ($header, $value) = @_;
448              
449 12         22 my $lcheader = lc($header);
450 12 50       38 $self->{DEBUG} && $self->DEBUG("add_req_header $header $value");
451 12         17 ${$self->{headers}}{$lcheader} = $value;
  12         49  
452 12         24 ${$self->{headermap}}{$lcheader} = $header;
  12         42  
453             }
454              
455             sub get_req_header
456             {
457 20     20 1 28 my $self = shift;
458 20         24 my ($header) = @_;
459              
460 20         63 return $self->{headers}{lc($header)};
461             }
462              
463             sub delete_req_header
464             {
465 8     8 1 13 my $self = shift;
466 8         13 my ($header) = @_;
467              
468 8         10 my $exists;
469 8 50       9 if ($exists=defined(${$self->{headers}}{lc($header)}))
  8         34  
470             {
471 0         0 delete ${$self->{headers}}{lc($header)};
  0         0  
472 0         0 delete ${$self->{headermap}}{lc($header)};
  0         0  
473             }
474 8         17 return $exists;
475             }
476              
477             sub enum_req_headers
478             {
479 4     4 0 9 my $self = shift;
480 4         7 my ($header) = @_;
481              
482 4         8 my $exists;
483 4         7 return keys %{$self->{headermap}};
  4         26  
484             }
485              
486             sub body
487             {
488 3     3 1 125 my $self = shift;
489 3         29 return $self->{'body'};
490             }
491              
492             sub status
493             {
494 0     0 1 0 my $self = shift;
495 0         0 return $self->{status};
496             }
497              
498             sub protocol
499             {
500 0     0 1 0 my $self = shift;
501 0         0 return $self->{'resp-protocol'};
502             }
503              
504             sub status_message
505             {
506 0     0 1 0 my $self = shift;
507 0         0 return $self->{'error-message'};
508             }
509              
510             sub proxy
511             {
512 0     0 1 0 my $self = shift;
513 0         0 my ($value) = @_;
514              
515             # Parse URL
516 0         0 my ($protocol,$host,$junk,$port,$object) =
517             $value =~ m{^(\S+)://([^/:]*)(:(\d+))?(/.*)$};
518 0 0       0 if (!$host)
519             {
520 0         0 ($host,$port) = $value =~ /^([^:]+):(.*)$/;
521             }
522              
523 0   0     0 $self->{'proxy'} = $host || $value;
524 0   0     0 $self->{'proxyport'} = $port || 80;
525             }
526              
527             sub headers_array
528             {
529 0     0 1 0 my $self = shift;
530              
531 0         0 my @array = ();
532              
533 0         0 foreach my $header (keys %{$self->{'resp-headers'}})
  0         0  
534             {
535 0         0 my $aref = ${$self->{'resp-headers'}}{$header};
  0         0  
536 0         0 foreach my $value (@$aref)
537             {
538 0         0 push @array, "$header: $value";
539             }
540             }
541 0         0 return @array;
542             }
543              
544             sub headers_string
545             {
546 0     0 1 0 my $self = shift;
547              
548 0         0 my $string = "";
549              
550 0         0 foreach my $header (keys %{$self->{'resp-headers'}})
  0         0  
551             {
552 0         0 my $aref = ${$self->{'resp-headers'}}{$header};
  0         0  
553 0         0 foreach my $value (@$aref)
554             {
555 0         0 $string .= "$header: $value\n";
556             }
557             }
558 0         0 return $string;
559             }
560              
561             sub get_header
562             {
563 4     4 1 6 my $self = shift;
564 4         9 my $header = shift;
565              
566 4         11 return $self->{'resp-headers'}{$header};
567             }
568              
569             sub http11_mode
570             {
571 0     0 1 0 my $self = shift;
572 0         0 my $mode = shift;
573              
574 0         0 $self->{HTTP11} = $mode;
575             }
576              
577             sub prepare_post
578             {
579 0     0 1 0 my $self = shift;
580 0         0 my $varref = shift;
581              
582 0         0 my $body = "";
583 0         0 while (my ($var,$value) = map { escape($_) } each %$varref)
  0         0  
584             {
585 0 0       0 if ($body)
586             {
587 0         0 $body .= "&$var=$value";
588             } else {
589 0         0 $body = "$var=$value";
590             }
591             }
592 0         0 $self->{content} = $body;
593 0 0 0     0 $self->{headers}{'Content-Type'} = "application/x-www-form-urlencoded"
594             unless defined ($self->{headers}{'Content-Type'}) and
595             $self->{headers}{'Content-Type'};
596 0         0 $self->{method} = "POST";
597             }
598              
599             sub http_write
600             {
601 24     24 0 35 my $self = shift;
602 24         67 my ($fh,$line) = @_;
603              
604 24 50       69 if ($self->{holdback}) {
605 0         0 $self->{HTTPWriteBuffer} .= $line;
606 0         0 return;
607             } else {
608 24 50       64 if (defined $self->{HTTPWriteBuffer}) { # copy previously buffered, if any
609 0         0 $line = $self->{HTTPWriteBuffer} . $line;
610             }
611             }
612              
613 24         35 my $size = length($line);
614 24         476 my $bytes = syswrite($fh, $line, length($line) , 0 ); # please double check new length limit
615             # is this ok?
616 24         113 while ( ($size - $bytes) > 0) {
617 0         0 $bytes += syswrite($fh, $line, length($line)-$bytes, $bytes ); # also here
618             }
619             }
620              
621             sub http_read
622             {
623 51     51 0 85 my $self = shift;
624 51         119 my ($fh,$headmode,$chunkmode,$chunksize) = @_;
625              
626 51 50       274 $self->{DEBUG} && $self->DEBUG("read handle=$fh, headm=$headmode, chunkm=$chunkmode, chunksize=$chunksize");
627              
628 51         57 my $res;
629 51 100 66     297 if (($headmode == 0 && $chunkmode eq "0") || ($chunkmode eq "chunk")) {
      66        
630 15 0       35 my $bytes_to_read = $chunkmode eq "chunk" ?
    50          
631             ($chunksize < $BLOCKSIZE ? $chunksize : $BLOCKSIZE) :
632             $BLOCKSIZE;
633 15         55 $res = $self->http_readbytes($fh,$self->{timeout},$bytes_to_read);
634             } else {
635 36         102 $res = $self->http_readline($fh,$self->{timeout});
636             }
637 51 100       130 if ($res) {
638 47 50       111 if ($self->{DEBUG}) {
639 0         0 $self->DEBUG("read got ".length($$res)." bytes");
640 0         0 my $str = $$res;
641 0         0 $str =~ s{([\x00-\x1F\x7F-\xFF])}{.}g;
642 0         0 $self->DEBUG("read: ".$str);
643             }
644             }
645 51         181 return $res;
646             }
647              
648             sub http_readline
649             {
650 36     36 0 45 my $self = shift;
651 36         67 my ($fh, $timeout) = @_;
652 36         268 my $EOL = "\n";
653              
654 36 50       101 $self->{DEBUG} && $self->DEBUG("readline handle=$fh, timeout=$timeout");
655              
656             # is there a line in the buffer yet?
657 36         164 while ($self->{HTTPReadBuffer} !~ /$EOL/)
658             {
659             # nope -- wait for incoming data
660 4         10 my ($inbuf,$bits,$chars) = ("","",0);
661 4         38 vec($bits,fileno($fh),1)=1;
662 4         110807 my $nfound = select($bits, undef, $bits, $timeout);
663 4 50       40 if ($nfound == 0)
664             {
665             # Timed out
666 0         0 return undef;
667             } else {
668             # Get the data
669 4         121 $chars = sysread($fh, $inbuf, $BLOCKSIZE);
670 4 50       37 $self->{DEBUG} && $self->DEBUG("sysread $chars bytes");
671             }
672             # End of stream?
673 4 50 33     20 if ($chars <= 0 && !$!{EAGAIN})
674             {
675 0         0 last;
676             }
677             # tag data onto end of buffer
678 4         121 $self->{HTTPReadBuffer}.=$inbuf;
679             }
680             # get a single line from the buffer
681 36         70 my $nlat = index($self->{HTTPReadBuffer}, $EOL);
682 36         43 my $newline;
683             my $oldline;
684 36 50       70 if ($nlat > -1)
685             {
686 36         128 $newline = substr($self->{HTTPReadBuffer},0,$nlat+1);
687 36         165 $oldline = substr($self->{HTTPReadBuffer},$nlat+1);
688             } else {
689 0         0 $newline = substr($self->{HTTPReadBuffer},0);
690 0         0 $oldline = "";
691             }
692             # and update the buffer
693 36         66 $self->{HTTPReadBuffer}=$oldline;
694 36 50       170 return length($newline) ? \$newline : 0;
695             }
696              
697             sub http_readbytes
698             {
699 15     15 0 18 my $self = shift;
700 15         32 my ($fh, $timeout, $bytes) = @_;
701 15         24 my $EOL = "\n";
702              
703 15 50       33 $self->{DEBUG} && $self->DEBUG("readbytes handle=$fh, timeout=$timeout, bytes=$bytes");
704              
705             # is there enough data in the buffer yet?
706 15         90 while (length($self->{HTTPReadBuffer}) < $bytes)
707             {
708             # nope -- wait for incoming data
709 30         72 my ($inbuf,$bits,$chars) = ("","",0);
710 30         152 vec($bits,fileno($fh),1)=1;
711 30         12401 my $nfound = select($bits, undef, $bits, $timeout);
712 30 50       80 if ($nfound == 0)
713             {
714             # Timed out
715 0         0 return undef;
716             } else {
717             # Get the data
718 30         4120 $chars = sysread($fh, $inbuf, $BLOCKSIZE);
719 30 50       162 $self->{DEBUG} && $self->DEBUG("sysread $chars bytes");
720             }
721             # End of stream?
722 30 100 66     423 if ($chars <= 0 && !$!{EAGAIN})
723             {
724 8         149 last;
725             }
726             # tag data onto end of buffer
727 22         880 $self->{HTTPReadBuffer}.=$inbuf;
728             }
729 15         34 my $newline;
730             my $buflen;
731 15 100       51 if (($buflen=length($self->{HTTPReadBuffer})) >= $bytes)
732             {
733 7         429 $newline = substr($self->{HTTPReadBuffer},0,$bytes+1);
734 7 50       22 if ($bytes+1 < $buflen) {
735 7         300 $self->{HTTPReadBuffer} = substr($self->{HTTPReadBuffer},$bytes+1);
736             } else {
737 0         0 $self->{HTTPReadBuffer} = "";
738             }
739             } else {
740 8         74 $newline = substr($self->{HTTPReadBuffer},0);
741 8         18 $self->{HTTPReadBuffer} = "";
742             }
743 15 100       69 return length($newline) ? \$newline : 0;
744             }
745              
746             sub upper
747             {
748 40     40 0 84 my ($str) = @_;
749 40 50       78 if (defined($str)) {
750 40         171 return uc($str);
751             } else {
752 0           return undef;
753             }
754             }
755              
756             1;
757              
758             __END__