Skip Menu |

This queue is for tickets about the IO-Socket-IP CPAN distribution.

Report information
The Basics
Id: 78136
Status: open
Priority: 0/
Queue: IO-Socket-IP

People
Owner: Nobody in particular
Requestors: massa.hara [...] gmail.com
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: IPv6 fast fallback patch for IO::Socket::IP
I wrote a patch which enables "IPv6 fast fallback" to IO::Socket::IP. I fixed some old code and added documents. All tests pass after applied this patch. % make test PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t t/00use.t ........................... ok t/01local-client-v4.t ............... ok t/02local-server-v4.t ............... ok t/03local-cross-v4.t ................ ok t/04local-client-v6.t ............... ok t/05local-server-v6.t ............... ok t/06local-cross-v6.t ................ ok t/10args.t .......................... ok t/11sockopts.t ...................... ok t/12port-fallback.t ................. ok t/13addrinfo.t ...................... ok t/14fileno.t ........................ ok t/15io-socket.t ..................... ok t/16v6only.t ........................ ok t/17gai-flags.t ..................... ok t/20nonblocking-connect.t ........... ok t/21nonblocking-connect-internet.t .. ok t/22ipv6fastfallback.t .............. 1/? # filter ipv6 packet # restore ipv6 filter t/22ipv6fastfallback.t .............. ok t/99pod.t ........................... ok All tests successful. Files=19, Tests=188, 64 wallclock secs ( 0.06 usr 0.02 sys + 0.69 cusr 0.13 csys = 0.90 CPU) Result: PASS
Subject: 22ipv6fastfallback.t
#!/usr/bin/perl -w use strict; use Test::More; use IO::Socket::INET6; use IO::Socket::IP; SKIP: { my $AF_INET6 = eval { Socket::AF_INET6() } || eval { require Socket6; Socket6::AF_INET6() }; $AF_INET6 or skip "No AF_INET6", 1; my $ipv6server = IO::Socket::INET6->new( Domain => PF_INET6, Listen => 1, LocalHost => "::1", LocalPort => 0, Type => SOCK_STREAM, ) or die "Cannot listen on PF_INET - $@"; my $sock = IO::Socket::IP->new( PeerHost => "::1", PeerService => $ipv6server->sockport, Type => SOCK_STREAM, IPv6FastFallback => 1, ); isa_ok( $sock, "IO::Socket::IP", 'connect ipv6server' ); is $sock->sockdomain, PF_INET6, 'connected via IPv6'; } if ( $ENV{TEST_IPV6HOST} ) { my $sock = IO::Socket::IP->new( PeerHost => $ENV{TEST_IPV6HOST}, PeerPort => 'http', Type => SOCK_STREAM, IPv6FastFallback => 1, ); is $sock->sockdomain, PF_INET6, "connected to $ENV{TEST_IPV6HOST} via IPv6"; # do ipv6 filterling on linux system( qw( sudo ip6tables -P OUTPUT DROP ) ) == 0 or skip( "failed ipv6tables" ); diag "filter ipv6 packet"; eval { my $sock = IO::Socket::IP->new( PeerAddr => $ENV{TEST_IPV6HOST}, Proto => 'tcp', PeerPort => 80, Type => SOCK_STREAM, IPv6FastFallback => 1, ); is $sock->sockdomain, PF_INET, "connected to $ENV{TEST_IPV6HOST} via IPv4"; }; # release ipv6 filterling system( qw( sudo ip6tables -P OUTPUT ACCEPT ) ); diag "restore ipv6 filter"; } done_testing;
Did I forget to attach the patch itself ? I attach again to this reply again.
Subject: IO_Socket_IP.pm.patch
--- /home/haram/perl5/lib/perl5/IO/Socket/IP.pm 2012-07-02 18:59:05.000000000 +0900 +++ IP.pm 2012-07-02 20:09:15.622094510 +0900 @@ -12,6 +12,7 @@ our $VERSION = '0.16'; use Carp; +use IO::Select; use Socket 1.97 qw( getaddrinfo getnameinfo @@ -302,6 +303,12 @@ it will default to blocking mode. See the NON-BLOCKING section below for more detail. +=item IPv6FastFallback => INT or FLOAT + +If defined, ipv6 connect will be expired in 300ms and try next connect. +If "1" is defined, timeout will be 300ms (default). +Other value will be passed directly to IO::Select->can_write(). + =back If neither C<Type> nor C<Proto> hints are provided, a default of @@ -519,7 +526,12 @@ ${*$self}{io_socket_ip_blocking} = $blocking; ${*$self}{io_socket_ip_errors} = [ undef, undef, undef ]; - + if ( $arg->{IPv6FastFallback} ) { + my $timeout = $arg->{IPv6FastFallback}; + $timeout = 1 == $timeout ? 0.03 : $timeout; # default 300ms + ${*$self}{io_socket_ip_ipv6_fast_fallback} = $timeout; + } + # ->setup is allowed to return false in nonblocking mode $self->setup or !$blocking or return undef; @@ -540,6 +552,10 @@ ( ${*$self}{io_socket_ip_errors}[2] = $!, next ); $self->blocking( 0 ) unless ${*$self}{io_socket_ip_blocking}; + if ( $info->{family} == $AF_INET6 and + ${*$self}{io_socket_ip_ipv6_fast_fallback} ) { + $self->blocking( 0 ); + } foreach my $sockopt ( @{ ${*$self}{io_socket_ip_sockopts} } ) { $self->setsockopt( SOL_SOCKET, $sockopt, pack "i", 1 ) or ( $@ = "$!", return undef ); @@ -566,6 +582,18 @@ } if( $! == EINPROGRESS or HAVE_MSWIN32 && $! == Errno::EWOULDBLOCK() ) { + if ( $info->{family} == $AF_INET6 and + ${*$self}{io_socket_ip_ipv6_fast_fallback} ) { + my $timeout = ${*$self}{io_socket_ip_ipv6_fast_fallback}; + my $sel = IO::Select->new($self); + my ($fh) = $sel->can_write($timeout); + if ( $fh and $fh->connected ) { + $self->blocking(1) if ${*$self}{io_socket_ip_blocking}; + return 1; + } + $self->close; + next; + } ${*$self}{io_socket_ip_connect_in_progress} = 1; return 0; }
Some commentary on the code: +=item IPv6FastFallback => INT or FLOAT + +If defined, ipv6 connect will be expired in 300ms and try next connect. IPv6 - capitalise. +If "1" is defined, timeout will be 300ms (default). +Other value will be passed directly to IO::Select->can_write(). + I wouldn't mention IO::Select here, that's an internal detail. Also some wording to the fact that it only happens during blocking setups, not nonblocking. =back If neither C<Type> nor C<Proto> hints are provided, a default of @@ -519,7 +526,12 @@ ${*$self}{io_socket_ip_blocking} = $blocking; ${*$self}{io_socket_ip_errors} = [ undef, undef, undef ]; - + if ( $arg->{IPv6FastFallback} ) { + my $timeout = $arg->{IPv6FastFallback}; + $timeout = 1 == $timeout ? 0.03 : $timeout; # default 300ms + ${*$self}{io_socket_ip_ipv6_fast_fallback} = $timeout; + } + I'm not sure I like the dual boolean-or-timeout nature of this configuration. It means that I can't set a timeout of 1.000sec exactly. Can you split this into two options: IPv6FastFallback => 1, IPv6FastFallbackTimeout => 0.3; (Also, 300msec is 0.3, not 0.03.) + my $sel = IO::Select->new($self); + my ($fh) = $sel->can_write($timeout); + if ( $fh and $fh->connected ) { + $self->blocking(1) if ${*$self}{io_socket_ip_blocking}; + return 1; + } On MSWin32 you also have to select() for exceptional status, because a socket only becomes write-ready if the connect() operation actually succeeds. Errors are only exceptional. At which point it's possibly simpler to use core select() rather than IO::Select. -- Paul Evans
I fixed below as you collected. - split option to IPv6FastFallback, IPv6FastFallbackTimeout - more documentations - default timeout to 0.3 - use CORE::select instead of IO::Select Show quoted text
> On MSWin32 you also have to select() for exceptional status, because a > socket only > becomes write-ready if the connect() operation actually succeeds. > Errors are only > exceptional. > > At which point it's possibly simpler to use core select() rather than > IO::Select.
I don't really understand text above. It's not my English skill though, I'm not sure about MSWin32 platform. I added result parameter check for CORE::select. This may solve the problem you asked.
Subject: IP.pm.select.patch
--- perl5/lib/perl5/IO/Socket/IP.pm 2012-06-27 15:56:13.297077617 +0900 +++ IP.pm 2012-07-05 02:34:37.746765107 +0900 @@ -302,6 +302,17 @@ it will default to blocking mode. See the NON-BLOCKING section below for more detail. +=item IPv6FastFallback => BOOL + +If defined, IPv6 connect will be expired in 300ms and try next connect. +Timeout can be changed by IPv6FastFallbackTimeout parameter. +IPv6 connect will be blocking mode till the timeout even "Blocking => 0" +is defined. + +=item IPv6FastFallbackTimeout => INT or FLOAT + +Timeout length for IPv6FastFallback. 0.3(300ms) by default. + =back If neither C<Type> nor C<Proto> hints are provided, a default of @@ -519,7 +530,11 @@ ${*$self}{io_socket_ip_blocking} = $blocking; ${*$self}{io_socket_ip_errors} = [ undef, undef, undef ]; - + if ( $arg->{IPv6FastFallback} ) { + ${*$self}{io_socket_ip_ipv6_fast_fallback} = + $arg->{IPv6FastFallbackTimeout} || 0.3; # default 300ms + } + # ->setup is allowed to return false in nonblocking mode $self->setup or !$blocking or return undef; @@ -540,6 +555,10 @@ ( ${*$self}{io_socket_ip_errors}[2] = $!, next ); $self->blocking( 0 ) unless ${*$self}{io_socket_ip_blocking}; + if ( $info->{family} == $AF_INET6 and + ${*$self}{io_socket_ip_ipv6_fast_fallback} ) { + $self->blocking( 0 ); + } foreach my $sockopt ( @{ ${*$self}{io_socket_ip_sockopts} } ) { $self->setsockopt( SOL_SOCKET, $sockopt, pack "i", 1 ) or ( $@ = "$!", return undef ); @@ -566,6 +585,20 @@ } if( $! == EINPROGRESS or HAVE_MSWIN32 && $! == Errno::EWOULDBLOCK() ) { + if ( $info->{family} == $AF_INET6 and + ${*$self}{io_socket_ip_ipv6_fast_fallback} ) { + my $timeout = ${*$self}{io_socket_ip_ipv6_fast_fallback}; + my $vec = ''; + vec( $vec, $self->fileno, 1 ) = 1; + my ( $nfound, $timeleft ) = + select undef, $vec, undef, $timeout; + if( $nfound > 0 and $self->connected ) { + $self->blocking(1) if ${*$self}{io_socket_ip_blocking}; + return 1; + } + $self->close; + next; + } ${*$self}{io_socket_ip_connect_in_progress} = 1; return 0; }