AnyEvent-HTTP-Socks
view release on metacpan or search on metacpan
lib/AnyEvent/HTTP/Socks.pm view on Meta::CPAN
my $socks = delete $opts{socks};
if ($socks) {
my @chain;
while ($socks =~ m!socks(4|4a|5)://(?:([^\s:]+):([^\s@]*)@)?(\[[0-9a-f:.]+\]|[^\s:]+):(\d+)!gi) {
push @chain, {ver => $1, login => $2, pass => $3, host => $4, port => $5};
}
if (@chain) {
$opts{tcp_connect} = sub {
my ($cv, $watcher, $timer, $sock);
my @tmp_chain = @chain; # copy: on redirect @tmp_chain will be already empty
_socks_prepare_connection(\$cv, \$watcher, \$timer, $sock, \@tmp_chain, @_);
};
}
else {
croak 'unsupported socks address specified';
}
}
AnyEvent::HTTP::http_request( $method, $url, %opts, $cb );
}
sub inject {
my ($class, $where) = @_;
$class->export($where, @EXPORT);
}
sub _socks_prepare_connection {
my ($cv, $watcher, $timer, $sock, $chain, $c_host, $c_port, $c_cb, $p_cb) = @_;
unless ($sock) { # first connection in the chain
# XXX: need also support IPv6 when SOCKS host is a domain name, but this is not so easy
socket(
$sock,
$chain->[0]{host} =~ /^\[.+\]$/ ? PF_INET6 : PF_INET,
SOCK_STREAM,
getprotobyname('tcp')
)
or return $c_cb->();
my $timeout = $p_cb->($sock);
$$timer = AnyEvent->timer(
after => $timeout,
cb => sub {
undef $$watcher;
undef $$cv;
$! = Errno::ETIMEDOUT;
$c_cb->();
}
);
$_->{host} =~ s/^\[// and $_->{host} =~ s/\]$// for @$chain;
}
$$cv = AE::cv {
_socks_connect($cv, $watcher, $timer, $sock, $chain, $c_host, $c_port, $c_cb);
};
$$cv->begin;
$$cv->begin;
inet_aton $chain->[0]{host}, sub {
$chain->[0]{host} = format_address shift;
$$cv->end if $$cv;
};
lib/AnyEvent/HTTP/Socks.pm view on Meta::CPAN
$$cv->end if $$cv;
}
}
$$cv->end;
return $sock;
}
sub _socks_connect {
my ($cv, $watcher, $timer, $sock, $chain, $c_host, $c_port, $c_cb) = @_;
my $link = shift @$chain;
my @specopts;
if ($link->{ver} eq '4a') {
$link->{ver} = 4;
push @specopts, SocksResolve => 1;
}
if (defined $link->{login}) {
push @specopts, Username => $link->{login};
lib/AnyEvent/HTTP/Socks.pm view on Meta::CPAN
) or return $c_cb->();
}
my ($poll, $w_type) = $SOCKS_ERROR == SOCKS_WANT_READ ?
('r', READ_WATCHER) :
('w', WRITE_WATCHER);
$$watcher = AnyEvent->io(
fh => $sock,
poll => $poll,
cb => sub { _socks_handshake($cv, $watcher, $w_type, $timer, $sock, $chain, $c_host, $c_port, $c_cb) }
);
}
sub _socks_handshake {
my ($cv, $watcher, $w_type, $timer, $sock, $chain, $c_host, $c_port, $c_cb) = @_;
if ($sock->ready) {
undef $$watcher;
if (@$chain) {
return _socks_prepare_connection($cv, $watcher, $timer, $sock, $chain, $c_host, $c_port, $c_cb);
}
undef $$timer;
return $c_cb->($sock);
}
if ($SOCKS_ERROR == SOCKS_WANT_WRITE) {
if ($w_type != WRITE_WATCHER) {
undef $$watcher;
$$watcher = AnyEvent->io(
fh => $sock,
poll => 'w',
cb => sub { _socks_handshake($cv, $watcher, WRITE_WATCHER, $timer, $sock, $chain, $c_host, $c_port, $c_cb) }
);
}
}
elsif ($SOCKS_ERROR == SOCKS_WANT_READ) {
if ($w_type != READ_WATCHER) {
undef $$watcher;
$$watcher = AnyEvent->io(
fh => $sock,
poll => 'r',
cb => sub { _socks_handshake($cv, $watcher, READ_WATCHER, $timer, $sock, $chain, $c_host, $c_port, $c_cb) }
);
}
}
else {
# unknown error
$@ = "IO::Socket::Socks: $SOCKS_ERROR";
undef $$watcher;
undef $$timer;
$c_cb->();
}
}
1;
__END__
=head1 NAME
AnyEvent::HTTP::Socks - Adds socks support for AnyEvent::HTTP
( run in 1.024 second using v1.01-cache-2.11-cpan-49f99fa48dc )