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 )