On Fri, Aug 05, 2016 at 05:01:10PM -0400, sven falempin wrote:
> Base perl got a deprecated HTTP Tiny code (0.29),
> one can use a package but base may enjoy the correction
> around or a better one.

I'm planning to update perl in base after I get mod_perl working under a
version > 5.20, which hopefully won't be too far in the future.  Doing that
will update IO::Socket::IP to 0.37 which includes the Timeout options.


> # Annoyingly IO::Socket's connect() is where the timeout logic is
> 
> Index: IP.pm
> ===================================================================
> RCS file: /cvs/src/gnu/usr.bin/perl/cpan/IO-Socket-IP/lib/IO/Socket/IP.pm,v
> retrieving revision 1.1.1.1
> diff -u -p -r1.1.1.1 IP.pm
> --- IP.pm 17 Nov 2014 20:52:48 -0000 1.1.1.1
> +++ IP.pm 5 Aug 2016 20:53:17 -0000
> @@ -1,13 +1,13 @@
>  #  You may distribute under the terms of either the GNU General Public
> License
>  #  or the Artistic License (the same terms as Perl itself)
>  #
> -#  (C) Paul Evans, 2010-2014 -- leon...@leonerd.org.uk
> +#  (C) Paul Evans, 2010-2015 -- leon...@leonerd.org.uk
> 
>  package IO::Socket::IP;
>  # $VERSION needs to be set before  use base 'IO::Socket'
>  #  - https://rt.cpan.org/Ticket/Display.html?id=92107
>  BEGIN {
> -   $VERSION = '0.29';
> +   $VERSION = '0.38';
>  }
> 
>  use strict;
> @@ -31,7 +31,7 @@ use Socket 1.97 qw(
>  my $AF_INET6 = eval { Socket::AF_INET6() }; # may not be defined
>  my $AI_ADDRCONFIG = eval { Socket::AI_ADDRCONFIG() } || 0;
>  use POSIX qw( dup2 );
> -use Errno qw( EINVAL EINPROGRESS EISCONN );
> +use Errno qw( EINVAL EINPROGRESS EISCONN ENOTCONN ETIMEDOUT EWOULDBLOCK );
> 
>  use constant HAVE_MSWIN32 => ( $^O eq "MSWin32" );
> 
> @@ -265,6 +265,22 @@ If true, set the C<SO_REUSEPORT> sockopt
> 
>  If true, set the C<SO_BROADCAST> sockopt
> 
> +=item Sockopts => ARRAY
> +
> +An optional array of other socket options to apply after the three listed
> +above. The value is an ARRAY containing 2- or 3-element ARRAYrefs. Each
> inner
> +array relates to a single option, giving the level and option name, and an
> +optional value. If the value element is missing, it will be given the
> value of
> +a platform-sized integer 1 constant (i.e. suitable to enable most of the
> +common boolean options).
> +
> +For example, both options given below are equivalent to setting
> C<ReuseAddr>.
> +
> + Sockopts => [
> +    [ SOL_SOCKET, SO_REUSEADDR ],
> +    [ SOL_SOCKET, SO_REUSEADDR, pack( "i", 1 ) ],
> + ]
> +
>  =item V6Only => BOOL
> 
>  If defined, set the C<IPV6_V6ONLY> sockopt when creating C<PF_INET6>
> sockets
> @@ -304,6 +320,22 @@ If defined but false, the socket will be
>  it will default to blocking mode. See the NON-BLOCKING section below for
> more
>  detail.
> 
> +=item Timeout => NUM
> +
> +If defined, gives a maximum time in seconds to block per C<connect()> call
> +when in blocking mode. If missing, no timeout is applied other than that
> +provided by the underlying operating system. When in non-blocking mode this
> +parameter is ignored.
> +
> +Note that if the hostname resolves to multiple address candidates, the same
> +timeout will apply to each connection attempt individually, rather than to
> the
> +operation as a whole. Further note that the timeout does not apply to the
> +initial hostname resolve operation, if connecting by hostname.
> +
> +This behviour is copied inspired by C<IO::Socket::INET>; for more fine
> grained
> +control over connection timeouts, consider performing a nonblocking connect
> +directly.
> +
>  =back
> 
>  If neither C<Type> nor C<Proto> hints are provided, a default of
> @@ -380,6 +412,12 @@ sub _io_socket_ip__configure
>     my @localinfos;
>     my @peerinfos;
> 
> +   my $listenqueue = $arg->{Listen};
> +   if( defined $listenqueue and
> +       ( defined $arg->{PeerHost} || defined $arg->{PeerService} ||
> defined $arg->{PeerAddrInfo} ) ) {
> +      croak "Cannot Listen with a peer address";
> +   }
> +
>     if( defined $arg->{GetAddrInfoFlags} ) {
>        $hints{flags} = $arg->{GetAddrInfoFlags};
>     }
> @@ -425,11 +463,17 @@ sub _io_socket_ip__configure
>        ref $info eq "ARRAY" or croak "Expected 'LocalAddrInfo' to be an
> ARRAY ref";
>        @localinfos = @$info;
>     }
> -   elsif( defined $arg->{LocalHost} or defined $arg->{LocalService} ) {
> +   elsif( defined $arg->{LocalHost} or
> +          defined $arg->{LocalService} or
> +          HAVE_MSWIN32 and $arg->{Listen} ) {
>        # Either may be undef
>        my $host = $arg->{LocalHost};
>        my $service = $arg->{LocalService};
> 
> +      unless ( defined $host or defined $service ) {
> +         $service = 0;
> +      }
> +
>        local $1; # Placate a taint-related bug; [perl #67962]
>        defined $service and $service =~ s/\((\d+)\)$// and
>           my $fallback_port = $1;
> @@ -476,14 +520,27 @@ sub _io_socket_ip__configure
>        }
>     }
> 
> +   my $INT_1 = pack "i", 1;
> +
>     my @sockopts_enabled;
> -   push @sockopts_enabled, SO_REUSEADDR if $arg->{ReuseAddr};
> -   push @sockopts_enabled, SO_REUSEPORT if $arg->{ReusePort};
> -   push @sockopts_enabled, SO_BROADCAST if $arg->{Broadcast};
> +   push @sockopts_enabled, [ SOL_SOCKET, SO_REUSEADDR, $INT_1 ] if
> $arg->{ReuseAddr};
> +   push @sockopts_enabled, [ SOL_SOCKET, SO_REUSEPORT, $INT_1 ] if
> $arg->{ReusePort};
> +   push @sockopts_enabled, [ SOL_SOCKET, SO_BROADCAST, $INT_1 ] if
> $arg->{Broadcast};
> +
> +   if( my $sockopts = $arg->{Sockopts} ) {
> +      ref $sockopts eq "ARRAY" or croak "Expected 'Sockopts' to be an
> ARRAY ref";
> +      foreach ( @$sockopts ) {
> +         ref $_ eq "ARRAY" or croak "Bad Sockopts item - expected
> ARRAYref";
> +         @$_ >= 2 and @$_ <= 3 or
> +            croak "Bad Sockopts item - expected 2 or 3 elements";
> 
> -   my $listenqueue = $arg->{Listen};
> +         my ( $level, $optname, $value ) = @$_;
> +         # TODO: consider more sanity checking on argument values
> 
> -   croak "Cannot Listen with a PeerHost" if defined $listenqueue and
> @peerinfos;
> +         defined $value or $value = $INT_1;
> +         push @sockopts_enabled, [ $level, $optname, $value ];
> +      }
> +   }
> 
>     my $blocking = $arg->{Blocking};
>     defined $blocking or $blocking = 1;
> @@ -583,7 +640,8 @@ sub setup
>        $self->blocking( 0 ) unless ${*$self}{io_socket_ip_blocking};
> 
>        foreach my $sockopt ( @{ ${*$self}{io_socket_ip_sockopts} } ) {
> -         $self->setsockopt( SOL_SOCKET, $sockopt, pack "i", 1 ) or ( $@ =
> "$!", return undef );
> +         my ( $level, $optname, $value ) = @$sockopt;
> +         $self->setsockopt( $level, $optname, $value ) or ( $@ = "$!",
> return undef );
>        }
> 
>        if( defined ${*$self}{io_socket_ip_v6only} and defined $AF_INET6 and
> $info->{family} == $AF_INET6 ) {
> @@ -606,11 +664,18 @@ sub setup
>              return 1;
>           }
> 
> -         if( $! == EINPROGRESS or HAVE_MSWIN32 && $! ==
> Errno::EWOULDBLOCK() ) {
> +         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;
>        }
> @@ -624,17 +689,60 @@ sub setup
>     return undef;
>  }
> 
> -sub connect
> +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::connect()
> +   # useful APIs I'm just going to end-run around it and call core's
> connect()
>     # directly
> 
> -   return CORE::connect( $self, $_[0] ) if @_;
> +   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;
> +      }
> +
> +      # Hoist the error by connect()ing a second time
> +      $err = $self->getsockopt( SOL_SOCKET, SO_ERROR );
> +      $err = 0 if $err == EISCONN; # Some OSes give EISCONN
> +
> +      $self->blocking( $was_blocking );
> +
> +      $! = $err, return undef if $err;
> +      return 1;
> +   }
> 
>     return 1 if !${*$self}{io_socket_ip_connect_in_progress};
> 
> @@ -651,7 +759,7 @@ sub connect
>     # (still in progress). This even works on MSWin32.
>     my $addr =
> ${*$self}{io_socket_ip_infos}[${*$self}{io_socket_ip_idx}]{peeraddr};
> 
> -   if( $self->connect( $addr ) or $! == EISCONN ) {
> +   if( connect( $self, $addr ) or $! == EISCONN ) {
>        delete ${*$self}{io_socket_ip_connect_in_progress};
>        $! = 0;
>        return 1;
> @@ -682,6 +790,9 @@ sub _get_host_service
>     my $self = shift;
>     my ( $addr, $flags, $xflags ) = @_;
> 
> +   defined $addr or
> +      $! = ENOTCONN, return;
> +
>     $flags |= NI_DGRAM if $self->socktype == SOCK_DGRAM;
> 
>     my ( $err, $host, $service ) = getnameinfo( $addr, $flags, $xflags || 0
> );
> @@ -747,11 +858,11 @@ Return the resolved name of the local po
> 
>  =cut
> 
> -sub sockhost { my $self = shift; ( $self->_get_host_service(
> $self->sockname, NI_NUMERICHOST, NIx_NOSERV ) )[0] }
> -sub sockport { my $self = shift; ( $self->_get_host_service(
> $self->sockname, NI_NUMERICSERV, NIx_NOHOST ) )[1] }
> +sub sockhost { my $self = shift; scalar +( $self->_get_host_service(
> $self->sockname, NI_NUMERICHOST, NIx_NOSERV ) )[0] }
> +sub sockport { my $self = shift; scalar +( $self->_get_host_service(
> $self->sockname, NI_NUMERICSERV, NIx_NOHOST ) )[1] }
> 
> -sub sockhostname { my $self = shift; ( $self->_get_host_service(
> $self->sockname, 0, NIx_NOSERV ) )[0] }
> -sub sockservice  { my $self = shift; ( $self->_get_host_service(
> $self->sockname, 0, NIx_NOHOST ) )[1] }
> +sub sockhostname { my $self = shift; scalar +( $self->_get_host_service(
> $self->sockname, 0, NIx_NOSERV ) )[0] }
> +sub sockservice  { my $self = shift; scalar +( $self->_get_host_service(
> $self->sockname, 0, NIx_NOHOST ) )[1] }
> 
>  =head2 $addr = $sock->sockaddr
> 
> @@ -800,11 +911,11 @@ Return the resolved name of the peer por
> 
>  =cut
> 
> -sub peerhost { my $self = shift; ( $self->_get_host_service(
> $self->peername, NI_NUMERICHOST, NIx_NOSERV ) )[0] }
> -sub peerport { my $self = shift; ( $self->_get_host_service(
> $self->peername, NI_NUMERICSERV, NIx_NOHOST ) )[1] }
> +sub peerhost { my $self = shift; scalar +( $self->_get_host_service(
> $self->peername, NI_NUMERICHOST, NIx_NOSERV ) )[0] }
> +sub peerport { my $self = shift; scalar +( $self->_get_host_service(
> $self->peername, NI_NUMERICSERV, NIx_NOHOST ) )[1] }
> 
> -sub peerhostname { my $self = shift; ( $self->_get_host_service(
> $self->peername, 0, NIx_NOSERV ) )[0] }
> -sub peerservice  { my $self = shift; ( $self->_get_host_service(
> $self->peername, 0, NIx_NOHOST ) )[1] }
> +sub peerhostname { my $self = shift; scalar +( $self->_get_host_service(
> $self->peername, 0, NIx_NOSERV ) )[0] }
> +sub peerservice  { my $self = shift; scalar +( $self->_get_host_service(
> $self->peername, 0, NIx_NOHOST ) )[1] }
> 
>  =head2 $addr = $peer->peeraddr
> 
> @@ -830,13 +941,13 @@ sub accept
> 
>  # This second unbelievably dodgy hack guarantees that $self->fileno doesn't
>  # change, which is useful during nonblocking connect
> -sub socket
> +sub socket :method
>  {
>     my $self = shift;
>     return $self->SUPER::socket(@_) if not defined $self->fileno;
> 
>     # I hate core prototypes sometimes...
> -   CORE::socket( my $tmph, $_[0], $_[1], $_[2] ) or return undef;
> +   socket( my $tmph, $_[0], $_[1], $_[2] ) or return undef;
> 
>     dup2( $tmph->fileno, $self->fileno ) or die "Unable to dup2 $tmph onto
> $self - $!";
>  }
> @@ -844,7 +955,7 @@ sub socket
>  # Versions of IO::Socket before 1.35 may leave socktype undef if from,
> say, an
>  #   ->fdopen call. In this case we'll apply a fix
>  BEGIN {
> -   if( $IO::Socket::VERSION < 1.35 ) {
> +   if( eval($IO::Socket::VERSION) < 1.35 ) {
>        *socktype = sub {
>           my $self = shift;
>           my $type = $self->SUPER::socktype;
> @@ -1082,6 +1193,37 @@ useable address from the results of the
>  constructor will ignore the value of this argument, except if it is defined
>  but false. An exception is thrown in this case, because that would request
> it
>  disable the C<getaddrinfo(3)> search behaviour in the first place.
> +
> +=item *
> +
> +C<IO::Socket::IP> implements both the C<Blocking> and C<Timeout>
> parameters,
> +but it implements the interaction of both in a different way.
> +
> +In C<::INET>, supplying a timeout overrides the non-blocking behaviour,
> +meaning that the C<connect()> operation will still block despite that the
> +caller asked for a non-blocking socket. This is not explicitly specified in
> +its documentation, nor does this author believe that is a useful behaviour
> -
> +it appears to come from a quirk of implementation.
> +
> +In C<::IP> therefore, the C<Blocking> parameter takes precedence - if a
> +non-blocking socket is requested, no operation will block. The C<Timeout>
> +parameter here simply defines the maximum time that a blocking C<connect()>
> +call will wait, if it blocks at all.
> +
> +In order to specifically obtain the "blocking connect then non-blocking
> send
> +and receive" behaviour of specifying this combination of options to
> C<::INET>
> +when using C<::IP>, perform first a blocking connect, then afterwards turn
> the
> +socket into nonblocking mode.
> +
> + my $sock = IO::Socket::IP->new(
> +    PeerHost => $peer,
> +    Timeout => 20,
> + ) or die "Cannot connect - $@";
> +
> + $sock->blocking( 0 );
> +
> +This code will behave identically under both C<IO::Socket::INET> and
> +C<IO::Socket::IP>.
> 
>  =back
> 
> 
> 
> 
> -- 
> ---------------------------------------------------------------------------------------------------------------------
> () ascii ribbon campaign - against html e-mail
> /\

-- 
andrew - http://afresh1.com

There are two ways to write error-free programs;
    only the third one works.

Reply via email to