IO-Socket-IP
view release on metacpan or search on metacpan
lib/IO/Socket/IP.pm view on Meta::CPAN
${*$self}{io_socket_ip_blocking} = $blocking;
${*$self}{io_socket_ip_errors} = [ undef, undef, undef ];
# ->setup is allowed to return false in nonblocking mode
$self->setup or !$blocking or return undef;
return $self;
}
sub setup
{
my $self = shift;
while(1) {
${*$self}{io_socket_ip_idx}++;
last if ${*$self}{io_socket_ip_idx} >= @{ ${*$self}{io_socket_ip_infos} };
my $info = ${*$self}{io_socket_ip_infos}->[${*$self}{io_socket_ip_idx}];
$self->socket( @{$info}{qw( family socktype protocol )} ) or
( ${*$self}{io_socket_ip_errors}[2] = $!, next );
$self->blocking( 0 ) unless ${*$self}{io_socket_ip_blocking};
foreach my $sockopt ( @{ ${*$self}{io_socket_ip_sockopts} } ) {
my ( $level, $optname, $value ) = @$sockopt;
$self->setsockopt( $level, $optname, $value ) or
( $IO::Socket::errstr = $@ = "$!", return undef );
}
if( defined ${*$self}{io_socket_ip_v6only} and defined $AF_INET6 and $info->{family} == $AF_INET6 ) {
my $v6only = ${*$self}{io_socket_ip_v6only};
$self->setsockopt( IPPROTO_IPV6, IPV6_V6ONLY, pack "i", $v6only ) or
( $IO::Socket::errstr = $@ = "$!", return undef );
}
if( defined( my $addr = $info->{localaddr} ) ) {
$self->bind( $addr ) or
( ${*$self}{io_socket_ip_errors}[1] = $!, next );
}
if( defined( my $listenqueue = ${*$self}{io_socket_ip_listenqueue} ) ) {
$self->listen( $listenqueue ) or
( $IO::Socket::errstr = $@ = "$!", return undef );
}
if( defined( my $addr = $info->{peeraddr} ) ) {
if( $self->connect( $addr ) ) {
$! = 0;
return 1;
}
if( $! == EINPROGRESS or $! == EWOULDBLOCK ) {
${*$self}{io_socket_ip_connect_in_progress} = 1;
return 0;
}
# If connect failed but we have no system error there must be an error
# at the application layer, like a bad certificate with
# IO::Socket::SSL.
# In this case don't continue IP based multi-homing because the problem
# cannot be solved at the IP layer.
return 0 if ! $!;
${*$self}{io_socket_ip_errors}[0] = $!;
next;
}
return 1;
}
# Pick the most appropriate error, stringified
$! = ( grep defined, @{ ${*$self}{io_socket_ip_errors}} )[0];
$IO::Socket::errstr = $@ = "$!";
return undef;
}
sub connect :method
{
my $self = shift;
# It seems that IO::Socket hides EINPROGRESS errors, making them look like
# a success. This is annoying here.
# Instead of putting up with its frankly-irritating intentional breakage of
# useful APIs I'm just going to end-run around it and call core's connect()
# directly
if( @_ ) {
my ( $addr ) = @_;
# Annoyingly IO::Socket's connect() is where the timeout logic is
# implemented, so we'll have to reinvent it here
my $timeout = ${*$self}{'io_socket_timeout'};
return connect( $self, $addr ) unless defined $timeout;
my $was_blocking = $self->blocking( 0 );
my $err = defined connect( $self, $addr ) ? 0 : $!+0;
if( !$err ) {
# All happy
$self->blocking( $was_blocking );
return 1;
}
elsif( not( $err == EINPROGRESS or $err == EWOULDBLOCK ) ) {
# Failed for some other reason
$self->blocking( $was_blocking );
return undef;
}
elsif( !$was_blocking ) {
# We shouldn't block anyway
return undef;
}
my $vec = ''; vec( $vec, $self->fileno, 1 ) = 1;
if( !select( undef, $vec, $vec, $timeout ) ) {
$self->blocking( $was_blocking );
$! = ETIMEDOUT;
return undef;
( run in 1.927 second using v1.01-cache-2.11-cpan-39bf76dae61 )