File Coverage

blib/lib/HTTP/Lite.pm
Criterion Covered Total %
statement 43 405 10.6
branch 0 182 0.0
condition 0 53 0.0
subroutine 12 39 30.7
pod 20 31 64.5
total 75 710 10.5


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