File Coverage

blib/lib/HTTP/Lite.pm
Criterion Covered Total %
statement 39 401 9.7
branch 0 182 0.0
condition 0 53 0.0
subroutine 10 37 27.0
pod 20 31 64.5
total 69 704 9.8


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