sub
get {
my
$self
;
if
(
ref
(
$_
[0]) ) {
$self
=
shift
;
}
my
(
$url
,
$proxy
,
$timeout
,
$auth_user
,
$auth_pass
) =
__PACKAGE__->_rearrange([
qw(URL PROXY TIMEOUT USER PASS)
],
@_
);
my
$dest
=
$proxy
||
$url
;
my
(
$host
,
$port
,
$path
,
$user
,
$pass
)
= _http_parse_url(
$dest
) or __PACKAGE__->throw(
"invalid URL $url"
);
$auth_user
||=
$user
;
$auth_pass
||=
$pass
;
if
(
$self
) {
unless
(
$proxy
) {
$proxy
=
$self
->proxy;
}
unless
(
$auth_user
) {
(
$auth_user
,
$auth_pass
) =
$self
->authentication;
}
}
$path
=
$url
if
$proxy
;
my
$socket
= _http_connect(
$host
,
$port
) or __PACKAGE__->throw(
"can't connect: $@"
);
print
$socket
"GET $path HTTP/1.0$CRLF"
;
print
$socket
"User-Agent: Bioperl fallback fetcher/1.0$CRLF"
;
print
$socket
"HOST: $host$CRLF"
;
if
(
$auth_user
&&
$auth_pass
) {
my
$token
= _encode_base64(
"$auth_user:$auth_pass"
);
print
$socket
"Authorization: Basic $token$CRLF"
;
}
print
$socket
"$CRLF"
;
my
$response
;
{
local
$/ =
"$CRLF$CRLF"
;
$response
= <
$socket
>;
}
my
(
$status_line
,
@other_lines
) =
split
$CRLF
,
$response
;
my
(
$stat_code
,
$stat_msg
) =
$status_line
=~ m!^HTTP/1\.[01] (\d+) (.+)!
or __PACKAGE__->throw(
"invalid response from web server: got $response"
);
my
%headers
=
map
{/^(\S+): (.+)/}
@other_lines
;
if
(
$stat_code
== 302 ||
$stat_code
== 301) {
my
$location
=
$headers
{Location} or
__PACKAGE__->throw(
"invalid redirect: no Location header"
);
return
get(
-url
=>
$location
,
-proxy
=>
$proxy
,
-timeout
=>
$timeout
,
-user
=>
$auth_user
,
-pass
=>
$auth_pass
);
}
elsif
(
$stat_code
== 401) {
my
$auth_required
=
$headers
{
'WWW-Authenticate'
};
$auth_required
=~ /^Basic realm=
"([^\"]+)"
/
or __PACKAGE__->throw(
"server requires unknown type of"
.
" authentication: $auth_required"
);
__PACKAGE__->throw(
"request failed: $status_line, realm = $1"
);
}
elsif
(
$stat_code
!= 200) {
__PACKAGE__->throw(
"request failed: $status_line"
);
}
$response
=
''
;
while
(1) {
my
$bytes
=
read
(
$socket
,
$response
,2048,
length
$response
);
last
unless
$bytes
> 0;
}
$response
;
}
sub
getFH {
my
(
$url
,
$proxy
,
$timeout
,
$auth_user
,
$auth_pass
) =
__PACKAGE__->_rearrange([
qw(URL PROXY TIMEOUT USER PASS)
],
@_
);
my
$dest
=
$proxy
||
$url
;
my
(
$host
,
$port
,
$path
,
$user
,
$pass
)
= _http_parse_url(
$dest
) or __PACKAGE__->throw(
"invalid URL $url"
);
$auth_user
||=
$user
;
$auth_pass
||=
$pass
;
$path
=
$url
if
$proxy
;
my
$socket
= _http_connect(
$host
,
$port
) or __PACKAGE__->throw(
"can't connect: $@"
);
print
$socket
"GET $path HTTP/1.0$CRLF"
;
print
$socket
"User-Agent: Bioperl fallback fetcher/1.0$CRLF"
;
print
$socket
"HOST: $host$CRLF"
;
if
(
$auth_user
&&
$auth_pass
) {
my
$token
= _encode_base64(
"$auth_user:$auth_pass"
);
print
$socket
"Authorization: Basic $token$CRLF"
;
}
print
$socket
"$CRLF"
;
my
$response
;
{
local
$/ =
"$CRLF$CRLF"
;
$response
= <
$socket
>;
}
my
(
$status_line
,
@other_lines
) =
split
$CRLF
,
$response
;
my
(
$stat_code
,
$stat_msg
) =
$status_line
=~ m!^HTTP/1\.[01] (\d+) (.+)!
or __PACKAGE__->throw(
"invalid response from web server: got $response"
);
my
%headers
=
map
{/^(\S+): (.+)/}
@other_lines
;
if
(
$stat_code
== 302 ||
$stat_code
== 301) {
my
$location
=
$headers
{Location} or
__PACKAGE__->throw(
"invalid redirect: no Location header"
);
return
getFH(
-url
=>
$location
,
-proxy
=>
$proxy
,
-timeout
=>
$timeout
,
-user
=>
$auth_user
,
-pass
=>
$auth_pass
);
}
elsif
(
$stat_code
== 401) {
my
$auth_required
=
$headers
{
'WWW-Authenticate'
};
$auth_required
=~ /^Basic realm=
"([^\"]+)"
/
or __PACKAGE__->throw(
"server requires unknown type of "
.
"authentication: $auth_required"
);
__PACKAGE__->throw(
"request failed: $status_line, realm = $1"
);
}
elsif
(
$stat_code
!= 200) {
__PACKAGE__->throw(
"request failed: $status_line"
);
}
$socket
;
}
sub
_http_parse_url {
my
$url
=
shift
;
my
(
$user
,
$pass
,
$hostent
,
$path
) =
$url
=~ m!^http://(?:([^:]+):([^:]+)@)?([^/]+)(/?[^\
$path
||=
'/'
;
my
(
$host
,
$port
) =
split
(
':'
,
$hostent
);
return
(
$host
,
$port
||80,
$path
,
$user
,
$pass
);
}
sub
_http_connect {
my
(
$host
,
$port
,
$timeout
) =
@_
;
my
$sock
= IO::Socket::INET->new(
Proto
=>
'tcp'
,
Type
=> SOCK_STREAM,
PeerHost
=>
$host
,
PeerPort
=>
$port
,
Timeout
=>
$timeout
,
);
$sock
;
}
sub
_encode_base64 {
my
$res
=
""
;
my
$eol
=
$_
[1];
$eol
=
"\n"
unless
defined
$eol
;
pos
(
$_
[0]) = 0;
$res
=
join
''
,
map
(
pack
(
'u'
,
$_
)=~ /^.(\S*)/, (
$_
[0]=~/(.{1,45})/gs));
$res
=~
tr
|` -_|AA-Za-z0-9+/|;
my
$padding
= (3 -
length
(
$_
[0]) % 3) % 3;
$res
=~ s/.{
$padding
}$/
'='
x
$padding
/e
if
$padding
;
if
(
length
$eol
) {
$res
=~ s/(.{1,76})/$1
$eol
/g;
}
return
$res
;
}
sub
proxy {
my
(
$self
,
$protocol
,
$proxy
,
$username
,
$password
) =
@_
;
$protocol
||=
'http'
;
unless
(
$proxy
) {
if
(
defined
$ENV
{http_proxy}) {
$proxy
=
$ENV
{http_proxy};
if
(
$proxy
=~ /\@/) {
(
$username
,
$password
,
$proxy
) =
$proxy
=~ m{http://(\S+):(\S+)\@(\S+)};
}
}
}
return
unless
(
defined
$proxy
);
$self
->authentication(
$username
,
$password
)
if
(
$username
&&
$password
);
return
$self
->{
'_proxy'
}->{
$protocol
} =
$proxy
;
}
sub
authentication{
my
(
$self
,
$u
,
$p
) =
@_
;
if
(
defined
$u
&&
defined
$p
) {
$self
->{
'_authentication'
} = [
$u
,
$p
];
}
return
@{
$self
->{
'_authentication'
} || []};
}
1;