📄 lite.pm
字号:
## HTTP::Lite.pm## $Id: Lite.pm,v 1.1.1.1 2005/09/03 16:13:00 xiaosuo Exp $#package HTTP::Lite;use vars qw($VERSION);use strict qw(vars);$VERSION = "2.1.6";my $BLOCKSIZE = 65536;my $CRLF = "\r\n";my $URLENCODE_VALID = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.";# Required modules for Network I/Ouse Socket 1.3;use Fcntl;use Errno qw(EAGAIN);# Forward declarationssub prepare_post;sub http_write;sub http_readline;sub http_read;sub http_readbytes;# Prepare the urlencode validchars lookup hashmy @urlencode_valid;foreach my $char (split('', $URLENCODE_VALID)) { $urlencode_valid[ord $char]=$char;}for (my $n=0;$n<255;$n++) { if (!defined($urlencode_valid[$n])) { $urlencode_valid[$n]=sprintf("%%%02X", $n); }}sub new { my $self = {}; bless $self; $self->initialize(); return $self;}sub initialize{ my $self = shift; $self->reset; $self->{timeout} = 120; $self->{HTTP11} = 0; $self->{DEBUG} = 0; $self->{header_at_once} = 0; $self->{holdback} = 0; # needed for http_write }sub header_at_once{ my $self=shift; $self->{header_at_once} = 1;}sub local_addr{ my $self = shift; my $val = shift; my $oldval = $self->{'local_addr'}; if (defined($val)) { $self->{'local_addr'} = $val; } return $oldval;}sub local_port{ my $self = shift; my $val = shift; my $oldval = $self->{'local_port'}; if (defined($val)) { $self->{'local_port'} = $val; } return $oldval;}sub method{ my $self = shift; my $method = shift; $method = uc($method); $self->{method} = $method;}sub DEBUG{ my $self = shift; if ($self->{DEBUG}) { print STDERR join(" ", @_),"\n"; }}sub reset{ my $self = shift; foreach my $var ("body", "request", "content", "status", "proxy", "proxyport", "resp-protocol", "error-message", "resp-headers", "CBARGS", "callback_function", "callback_params") { $self->{$var} = undef; } $self->{HTTPReadBuffer} = ""; $self->{method} = "GET"; $self->{headers} = { 'user-agent' => "HTTP::Lite/$VERSION" }; $self->{headermap} = { 'user-agent' => 'User-Agent' };}# URL-encode datasub escape { my $toencode = shift; return join('', map { $urlencode_valid[ord $_] } split('', $toencode));}sub set_callback { my ($self, $callback, @callbackparams) = @_; $self->{'callback_function'} = $callback; $self->{'callback_params'} = [ @callbackparams ];}sub request{ my ($self, $url, $data_callback, $cbargs) = @_; my $method = $self->{method}; if (defined($cbargs)) { $self->{CBARGS} = $cbargs; } my $callback_func = $self->{'callback_function'}; my $callback_params = $self->{'callback_params'}; # Parse URL my ($protocol,$host,$junk,$port,$object) = $url =~ m{^([^:/]+)://([^/:]*)(:(\d+))?(/.*)$}; # Only HTTP is supported here if ($protocol ne "http") { warn "Only http is supported by HTTP::Lite"; return undef; } # Setup the connection my $proto = getprotobyname('tcp'); local *FH; socket(FH, PF_INET, SOCK_STREAM, $proto); $port = 80 if !$port; my $connecthost = $self->{'proxy'} || $host; $connecthost = $connecthost ? $connecthost : $host; my $connectport = $self->{'proxyport'} || $port; $connectport = $connectport ? $connectport : $port; my $addr = inet_aton($connecthost); if (!$addr) { close(FH); return undef; } if ($connecthost ne $host) { # if proxy active, use full URL as object to request $object = "$url"; } # choose local port and address my $local_addr = INADDR_ANY; my $local_port = "0"; if (defined($self->{'local_addr'})) { $local_addr = $self->{'local_addr'}; if ($local_addr eq "0.0.0.0" || $local_addr eq "0") { $local_addr = INADDR_ANY; } else { $local_addr = inet_aton($local_addr); } } if (defined($self->{'local_port'})) { $local_port = $self->{'local_port'}; } my $paddr = pack_sockaddr_in($local_port, $local_addr); bind(FH, $paddr) || return undef; # Failing to bind is fatal. my $sin = sockaddr_in($connectport,$addr); connect(FH, $sin) || return undef; # Set nonblocking IO on the handle to allow timeouts if ( $^O ne "MSWin32" ) { fcntl(FH, F_SETFL, O_NONBLOCK); } if (defined($callback_func)) { &$callback_func($self, "connect", undef, @$callback_params); } if ($self->{header_at_once}) { $self->{holdback} = 1; # http_write should buffer only, no sending yet } # Start the request (HTTP/1.1 mode) if ($self->{HTTP11}) { $self->http_write(*FH, "$method $object HTTP/1.1$CRLF"); } else { $self->http_write(*FH, "$method $object HTTP/1.0$CRLF"); } # Add some required headers # we only support a single transaction per request in this version. $self->add_req_header("Connection", "close"); if ($port != 80) { $self->add_req_header("Host", "$host:$port"); } else { $self->add_req_header("Host", $host); } if (!defined($self->get_req_header("Accept"))) { $self->add_req_header("Accept", "*/*"); } if ($method eq 'POST') { $self->http_write(*FH, "Content-Type: application/x-www-form-urlencoded$CRLF"); } # Purge a couple others $self->delete_req_header("Content-Type"); $self->delete_req_header("Content-Length"); # Output headers foreach my $header ($self->enum_req_headers()) { my $value = $self->get_req_header($header); $self->http_write(*FH, $self->{headermap}{$header}.": ".$value."$CRLF"); } my $content_length; if (defined($self->{content})) { $content_length = length($self->{content}); } if (defined($callback_func)) { my $ncontent_length = &$callback_func($self, "content-length", undef, @$callback_params); if (defined($ncontent_length)) { $content_length = $ncontent_length; } } if ($content_length) { $self->http_write(*FH, "Content-Length: $content_length$CRLF"); } if (defined($callback_func)) { &$callback_func($self, "done-headers", undef, @$callback_params); } # End of headers $self->http_write(*FH, "$CRLF"); if ($self->{header_at_once}) { $self->{holdback} = 0; $self->http_write(*FH, ""); # pseudocall to get http_write going } my $content_out = 0; if (defined($callback_func)) { while (my $content = &$callback_func($self, "content", undef, @$callback_params)) { $self->http_write(*FH, $content); $content_out++; } } # Output content, if any if (!$content_out && defined($self->{content})) { $self->http_write(*FH, $self->{content}); } if (defined($callback_func)) { &$callback_func($self, "content-done", undef, @$callback_params); } # Read response from server my $headmode=1; my $chunkmode=0; my $chunksize=0; my $chunklength=0; my $chunk; my $line = 0; my $data; while ($data = $self->http_read(*FH,$headmode,$chunkmode,$chunksize)) { $self->{DEBUG} && $self->DEBUG("reading: $chunkmode, $chunksize, $chunklength, $headmode, ". length($self->{'body'})); if ($self->{DEBUG}) { foreach my $var ("body", "request", "content", "status", "proxy", "proxyport", "resp-protocol", "error-message", "resp-headers", "CBARGS", "HTTPReadBuffer") { $self->DEBUG("state $var ".length($self->{$var})); } } $line++; if ($line == 1) { my ($proto,$status,$message) = split(' ', $$data, 3); $self->{DEBUG} && $self->DEBUG("header $$data"); $self->{status}=$status; $self->{'resp-protocol'}=$proto; $self->{'error-message'}=$message; next; } if (($headmode || $chunkmode eq "entity-header") && $$data =~ /^[\r\n]*$/) { if ($chunkmode) { $chunkmode = 0; } $headmode = 0; # Check for Transfer-Encoding my $te = $self->get_header("Transfer-Encoding"); if (defined($te)) { my $header = join(' ',@{$te}); if ($header =~ /chunked/i) { $chunkmode = "chunksize"; } } next; } if ($headmode || $chunkmode eq "entity-header") { my ($var,$datastr) = $$data =~ /^([^:]*):\s*(.*)$/; if (defined($var)) { $datastr =~s/[\r\n]$//g; $var = lc($var); $var =~ s/^(.)/&upper($1)/ge; $var =~ s/(-.)/&upper($1)/ge; my $hr = ${$self->{'resp-headers'}}{$var}; if (!ref($hr)) { $hr = [ $datastr ]; } else { push @{ $hr }, $datastr; } ${$self->{'resp-headers'}}{$var} = $hr; } } elsif ($chunkmode) { if ($chunkmode eq "chunksize") { $chunksize = $$data; $chunksize =~ s/^\s*|;.*$//g; $chunksize =~ s/\s*$//g; my $cshx = $chunksize; if (length($chunksize) > 0) { # read another line if ($chunksize !~ /^[a-f0-9]+$/i) { $self->{DEBUG} && $self->DEBUG("chunksize not a hex string"); } $chunksize = hex($chunksize); $self->{DEBUG} && $self->DEBUG("chunksize was $chunksize (HEX was $cshx)"); if ($chunksize == 0) { $chunkmode = "entity-header"; } else { $chunkmode = "chunk"; $chunklength = 0; } } else { $self->{DEBUG} && $self->DEBUG("chunksize empty string, checking next line!"); } } elsif ($chunkmode eq "chunk") { $chunk .= $$data; $chunklength += length($$data); if ($chunklength >= $chunksize) { $chunkmode = "chunksize"; if ($chunklength > $chunksize) { $chunk = substr($chunk,0,$chunksize); } elsif ($chunklength == $chunksize && $chunk !~ /$CRLF$/) { # chunk data is exactly chunksize -- need CRLF still $chunkmode = "ignorecrlf"; } $self->add_to_body(\$chunk, $data_callback); $chunk=""; $chunklength = 0; $chunksize = ""; } } elsif ($chunkmode eq "ignorecrlf") { $chunkmode = "chunksize"; } } else { $self->add_to_body($data, $data_callback); } } if (defined($callback_func)) { &$callback_func($self, "done", undef, @$callback_params); } close(FH); return $self->{status};}sub add_to_body{ my $self = shift; my ($dataref, $data_callback) = @_; my $callback_func = $self->{'callback_function'}; my $callback_params = $self->{'callback_params'}; if (!defined($data_callback) && !defined($callback_func)) { $self->{DEBUG} && $self->DEBUG("no callback"); $self->{'body'}.=$$dataref; } else { my $newdata; if (defined($callback_func)) { $newdata = &$callback_func($self, "data", $dataref, @$callback_params); } else { $newdata = &$data_callback($self, $dataref, $self->{CBARGS}); } if ($self->{DEBUG}) { $self->DEBUG("callback got back a ".ref($newdata)); if (ref($newdata) eq "SCALAR") { $self->DEBUG("callback got back ".length($$newdata)." bytes"); } } if (defined($newdata) && ref($newdata) eq "SCALAR") { $self->{'body'} .= $$newdata; } }}sub add_req_header{ my $self = shift; my ($header, $value) = @_; my $lcheader = lc($header); $self->{DEBUG} && $self->DEBUG("add_req_header $header $value"); ${$self->{headers}}{$lcheader} = $value; ${$self->{headermap}}{$lcheader} = $header;}sub get_req_header{ my $self = shift; my ($header) = @_; return $self->{headers}{lc($header)};}sub delete_req_header{ my $self = shift; my ($header) = @_; my $exists; if ($exists=defined(${$self->{headers}}{lc($header)})) { delete ${$self->{headers}}{lc($header)}; delete ${$self->{headermap}}{lc($header)}; } return $exists;}sub enum_req_headers{ my $self = shift; my ($header) = @_; my $exists; return keys %{$self->{headermap}};}sub body{ my $self = shift; return $self->{'body'};}sub status{ my $self = shift; return $self->{status};}sub protocol{ my $self = shift; return $self->{'resp-protocol'};}sub status_message{ my $self = shift; return $self->{'error-message'};}sub proxy{ my $self = shift; my ($value) = @_; # Parse URL my ($protocol,$host,$junk,$port,$object) = $value =~ m{^(\S+)://([^/:]*)(:(\d+))?(/.*)$}; if (!$host) { ($host,$port) = $value =~ /^([^:]+):(.*)$/; } $self->{'proxy'} = $host || $value; $self->{'proxyport'} = $port || 80;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -