;#
;# Copyright (c) 1995-1997
;# Ikuo Nakagawa. All rights reserved.
;#
;# Redistribution and use in source and binary forms, with or without
;# modification, are permitted provided that the following conditions
;# are met:
;#
;# 1. Redistributions of source code must retain the above copyright
;# notice unmodified, this list of conditions, and the following
;# disclaimer.
;# 2. Redistributions in binary form must reproduce the above copyright
;# notice, this list of conditions and the following disclaimer in the
;# documentation and/or other materials provided with the distribution.
;#
;# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
;# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
;# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
;# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
;# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
;# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
;# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
;# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
;# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;#
;# $Id: HTTP.pm,v 1.16 1997/11/06 12:15:16 ikuo Exp $
;#
;# Description:
;# HTTP.pm - HTTP Class definitions
;# - Socket based operations.
;# - Multiple addresses for a single server are supported.
;# - Multiple sessions are supported.
;#
;# Usage:
;#
package Fan::HTTP;
use strict;
use vars qw($VERSION @ISA $LOG $http_port);
use Carp;
use Fan::Cool;
use Fan::TCP;
use AutoLoader 'AUTOLOAD';
@ISA = qw(Fan::TCP);
$VERSION = '0.02';
$LOG = 5 unless defined($LOG);
;# A special marker for AutoSplit.
1;
__END__
;# destroy an object.
sub DESTROY ($) {
my $self = shift;
# log...
carp("HTTP DESTROYING $self") if $LOG >= 6;
# destroy this object in SUPER class.
Fan::TCP::DESTROY($self);
}
;# creat a new object.
sub new ($%) {
my $this = shift;
my $class = ref($this) || $this;
my %param = @_;
# if we required verbose log, set tcp_debug
$param{tcp_debug} = 1 if $LOG > 5;
# bless myself.
my $self = bless Fan::TCP->new(%param), $class;
ref($self) or return undef;
# log...
carp("HTTP CREATING $self") if $LOG >= 6;
# result is myself.
$self;
}
;# connecting the server, and get contents.
sub get ($$;$) {
my $self = shift;
my ($proto, $server, $port, $document) = &parse_url(shift);
my $out = shift;
my $tmp;
my $need_rename = 0;
my $fh;
local *OUTPUT;
if ($document !~ /^\//) {
carp("$self: document must begin with a slash"),
return undef;
}
if ($out eq '') {
$fh = \*STDOUT;
} elsif (ref($out) eq 'GLOB') {
$fh = $out;
} else {
($tmp = $out) =~ s%[^/]+$%.in.$&%;
$need_rename++;
CORE::open(OUTPUT, ">$tmp") or
carp("$self: open($tmp) - $!"), return undef;
$fh = \*OUTPUT;
}
$proto = $self->{http_proto} if $proto eq '';
$proto = 'http' if $proto eq '';
$server = $self->{http_server} if $server eq '';
if ($server eq '') {
carp("$self: host not specified"), return undef;
}
if ($self->{http_proxy} eq '') {
if ($proto ne 'http') {
carp("$self: protocol $proto is not supported");
return undef;
}
$port = 80 if $port eq '';
} else {
if (defined($port)) { # normal port
$document = sprintf("%s://%s:%d%s",
$proto, $server, $port, $document);
} else {
$document = sprintf("%s://%s%s",
$proto, $server, $document);
}
$server = $self->{http_proxy};
$port = $server =~ s/:(\d+)$// ? $1 : 80;
}
# carp("$self: ($proto, $server, $port, $document)") if $LOG > 6;
warn("HTTP: try to connect $server:$port\n") if $LOG > 6;
warn("HTTP: try to get $document\n") if $LOG > 6;
unless ($self->do_client(tcp_host => $server, tcp_port => $port)) {
# error string was stored by do_client.
$self->close;
return undef;
}
$self->putln("GET $document HTTP/1.0") &&
$self->putln("Accept: */*") &&
$self->putln("User-Agent: HTTP.pm/$VERSION") &&
$self->putln("") or
$self->close, return undef;
defined($_ = $self->getln) &&
/^HTTP\/\d+\.\d+ (\d\d\d) (.+)$/ or
$self->error("wrong response"), $self->close, return undef;
my $result = $1;
my $reason = $2;
my @headers = ();
warn("HTTP: result=$result reason=$2\n") if $LOG > 6;
my $null = 0;
while (defined($_ = $self->getln)) {
$null++, last if /^$/;
$_ = pop(@headers).$_ if /^\s/;
push(@headers, $_);
}
unless ($null) {
$self->error("unexpected end of file");
$self->close;
return undef;
}
# parsing headers
while (@headers) {
$_ = shift(@headers);
unless (s/^([a-zA-Z\-]+):\s+//) {
$self->error("wrong header");
$self->clsoe;
return undef;
}
my $tag = "\L$1";
if ($tag eq 'content-type') {
if (/\s*([-\w]+)\/([-\w]+)\s*(;\s*([^\s;]+)\s*)*$/) {
$self->{content_type} = $1;
$self->{content_subtype} = $2;
}
} elsif ($tag eq 'content-length') {
$self->{content_length} = $_;
} elsif ($tag eq 'content-encoding') {
$self->{content_encoding} = $_;
} elsif ($tag eq 'content-transfer-encoding') {
$self->{content_transfer_encoding} = $_;
} elsif ($tag eq 'last-modified') {
$self->{last_modified} = $_;
} else {
# simply ignored
}
}
# get total length of this contents
my $length = defined($self->{content_length}) ?
$self->{content_length} + 0 : undef;
# try to read real data.
my $len = 0;
my $ll = defined($length) && $length < 10240 ? $length : 10240;
while ($ll > 0 && defined(my $data = $self->getdata($ll))) {
unless (print $fh $data) {
$self->error($!.'');
$self->close;
return undef;
}
$len += length($data);
$ll = defined($length) && $length - $len < 10240
? $length - $len : 10240;
}
# o.k., now close this session.
$self->close;
if ($self->error) {
return undef;
}
# checking result...
unless ($result == 200) {
$self->error("\"$reason\"");
return undef;
}
# check length...
if (defined($length) && $length != $len) {
$self->error('length mismatch');
return undef;
}
# rename if we wrote to a plain file
if ($need_rename) {
unless (rename($tmp, $out)) {
my $e = $!."";
$self->error("rename($out): $e");
return undef;
}
}
# success
1;
}
;#
sub parse_url ($) {
local $_ = shift;
my $proto = s|^(\w+)://|| ? $1 : undef;
my $server = s|^([^/]+)|| ? $1 : undef;
my $port = $server =~ s|:(\d+)$|| ? $1 : undef;
my $document = m|^/| ? $_ : '/';
carp("HTTP parse_url: ($proto, $server, $port, $document)") if $LOG > 6;
($proto, $server, $port, $document);
}
;# end of Fan::HTTP module
syntax highlighted by Code2HTML, v. 0.9.1