use
vars
qw(@ISA @EXTRA_SOCK_OPTS)
;
@ISA
=
qw(LWP::Protocol)
;
my
$CRLF
=
"\015\012"
;
sub
_new_socket
{
my
(
$self
,
$host
,
$port
,
$timeout
) =
@_
;
local
($^W) = 0;
my
$sock
= IO::Socket::INET->new(
PeerAddr
=>
$host
,
PeerPort
=>
$port
,
Proto
=>
'tcp'
,
Timeout
=>
$timeout
,
$self
->_extra_sock_opts(
$host
,
$port
),
);
unless
(
$sock
) {
$@ =~ s/^.*?: //;
die
"Can't connect to $host:$port ($@)"
;
}
$sock
;
}
sub
_extra_sock_opts
{
return
@EXTRA_SOCK_OPTS
;
}
sub
_check_sock
{
}
sub
_get_sock_info
{
my
(
$self
,
$res
,
$sock
) =
@_
;
if
(
defined
(
my
$peerhost
=
$sock
->peerhost)) {
$res
->header(
"Client-Peer"
=>
"$peerhost:"
.
$sock
->peerport);
}
}
sub
_fixup_header
{
my
(
$self
,
$h
,
$url
,
$proxy
) =
@_
;
$h
->remove_header(
'Connection'
);
my
$hhost
=
$url
->authority;
if
(
$hhost
=~ s/^([^\@]*)\@//) {
if
(
defined
($1) && not
$h
->header(
'Authorization'
)) {
$h
->authorization_basic(
map
URI::Escape::uri_unescape(
$_
),
split
(
":"
, $1, 2));
}
}
$h
->init_header(
'Host'
=>
$hhost
);
if
(
$proxy
) {
my
$p_auth
=
$proxy
->userinfo();
if
(
defined
$p_auth
) {
$h
->proxy_authorization_basic(
map
URI::Escape::uri_unescape(
$_
),
split
(
":"
,
$p_auth
, 2))
}
}
}
sub
request
{
my
(
$self
,
$request
,
$proxy
,
$arg
,
$size
,
$timeout
) =
@_
;
$size
||= 4096;
my
$method
=
$request
->method;
unless
(
$method
=~ /^[A-Za-z0-9_!\
return
new HTTP::Response
&HTTP::Status::RC_BAD_REQUEST
,
'Library does not allow method '
.
"$method for 'http:' URLs"
;
}
my
$url
=
$request
->url;
my
(
$host
,
$port
,
$fullpath
);
if
(
defined
$proxy
) {
$host
=
$proxy
->host;
$port
=
$proxy
->port;
$fullpath
=
$method
eq
"CONNECT"
?
(
$url
->host .
":"
.
$url
->port) :
$url
->as_string;
}
else
{
$host
=
$url
->host;
$port
=
$url
->port;
$fullpath
=
$url
->path_query;
$fullpath
=
"/"
unless
length
$fullpath
;
}
my
$socket
=
$self
->_new_socket(
$host
,
$port
,
$timeout
);
$self
->_check_sock(
$request
,
$socket
);
my
$sel
= IO::Select->new(
$socket
)
if
$timeout
;
my
$request_line
=
"$method $fullpath HTTP/1.0$CRLF"
;
my
$h
=
$request
->headers->clone;
my
$cont_ref
=
$request
->content_ref;
$cont_ref
=
$$cont_ref
if
ref
(
$$cont_ref
);
my
$ctype
=
ref
(
$cont_ref
);
if
(
$ctype
eq
'CODE'
) {
die
'No Content-Length header for request with dynamic content'
unless
defined
(
$h
->header(
'Content-Length'
)) ||
$h
->content_type =~ /^multipart\//;
}
else
{
$h
->header(
'Content-Length'
=>
length
$$cont_ref
)
if
defined
(
$$cont_ref
) &&
length
(
$$cont_ref
);
}
$self
->_fixup_header(
$h
,
$url
,
$proxy
);
my
$buf
=
$request_line
.
$h
->as_string(
$CRLF
) .
$CRLF
;
my
$n
;
my
$length
;
my
$offset
;
$length
=
length
(
$buf
);
$offset
= 0;
while
(
$offset
<
$length
) {
die
"write timeout"
if
$timeout
&& !
$sel
->can_write(
$timeout
);
$n
=
$socket
->
syswrite
(
$buf
,
$length
-
$offset
,
$offset
);
die
$!
unless
defined
(
$n
);
$offset
+=
$n
;
}
if
(
$ctype
eq
'CODE'
) {
while
( (
$buf
=
&$cont_ref
()),
defined
(
$buf
) &&
length
(
$buf
)) {
$length
=
length
(
$buf
);
$offset
= 0;
while
(
$offset
<
$length
) {
die
"write timeout"
if
$timeout
&& !
$sel
->can_write(
$timeout
);
$n
=
$socket
->
syswrite
(
$buf
,
$length
-
$offset
,
$offset
);
die
$!
unless
defined
(
$n
);
$offset
+=
$n
;
}
}
}
elsif
(
defined
(
$$cont_ref
) &&
length
(
$$cont_ref
)) {
$length
=
length
(
$$cont_ref
);
$offset
= 0;
while
(
$offset
<
$length
) {
die
"write timeout"
if
$timeout
&& !
$sel
->can_write(
$timeout
);
$n
=
$socket
->
syswrite
(
$$cont_ref
,
$length
-
$offset
,
$offset
);
die
$!
unless
defined
(
$n
);
$offset
+=
$n
;
}
}
my
$response
;
$buf
=
''
;
while
(1) {
die
"read timeout"
if
$timeout
&& !
$sel
->can_read(
$timeout
);
$n
=
$socket
->
sysread
(
$buf
,
$size
,
length
(
$buf
));
die
$!
unless
defined
(
$n
);
die
"unexpected EOF before status line seen"
unless
$n
;
if
(
$buf
=~ s/^(HTTP\/\d+\.\d+)[ \t]+(\d+)[ \t]*([^\012]*)\012//) {
my
(
$ver
,
$code
,
$msg
) = ($1, $2, $3);
$msg
=~ s/\015$//;
$response
= HTTP::Response->new(
$code
,
$msg
);
$response
->protocol(
$ver
);
until
(
$buf
=~ /^\015?\012/ ||
$buf
=~ /\015?\012\015?\012/) {
die
"read timeout"
if
$timeout
&& !
$sel
->can_read(
$timeout
);
my
$old_len
=
length
(
$buf
);
$n
=
$socket
->
sysread
(
$buf
,
$size
,
$old_len
);
die
$!
unless
defined
(
$n
);
die
"unexpected EOF before all headers seen"
unless
$n
;
}
my
(
$key
,
$val
);
while
(
$buf
=~ s/([^\012]*)\012//) {
my
$line
= $1;
my
$save
=
"$line\012"
;
$line
=~ s/\015$//;
last
unless
length
$line
;
if
(
$line
=~ /^([a-zA-Z0-9_\-.]+)\s*:\s*(.*)/) {
$response
->push_header(
$key
,
$val
)
if
$key
;
(
$key
,
$val
) = ($1, $2);
}
elsif
(
$line
=~ /^\s+(.*)/ &&
$key
) {
$val
.=
" $1"
;
}
else
{
$response
->push_header(
"Client-Bad-Header-Line"
=>
$line
);
}
}
$response
->push_header(
$key
,
$val
)
if
$key
;
last
;
}
elsif
((
length
(
$buf
) >= 5 and
$buf
!~ /^HTTP\//) or
$buf
=~ /\012/ ) {
$response
= HTTP::Response->new(
&HTTP::Status::RC_OK
,
"OK"
);
$response
->protocol(
'HTTP/0.9'
);
last
;
}
else
{
}
};
$response
->request(
$request
);
$self
->_get_sock_info(
$response
,
$socket
);
if
(
$method
eq
"CONNECT"
) {
$response
->{client_socket} =
$socket
;
$response
->content(
$buf
);
return
$response
;
}
my
$usebuf
=
length
(
$buf
) > 0;
$response
=
$self
->collect(
$arg
,
$response
,
sub
{
if
(
$usebuf
) {
$usebuf
= 0;
return
\
$buf
;
}
die
"read timeout"
if
$timeout
&& !
$sel
->can_read(
$timeout
);
my
$n
=
$socket
->
sysread
(
$buf
,
$size
);
die
$!
unless
defined
(
$n
);
return
\
$buf
;
} );
$response
;
}
1;