IO::Socket::Timeout: socket timeout made easy

Without network operations, running a website for booking accommodation online would be nearly impossible. Network operations can be anything from simple actions like talking to a browser with a user at the other end, to providing an API for our affiliates, or even writing the internal services that help maintain our systems.

What is a network socket

# example 1
my $socket = IO::Socket::INET->new('google.com:80');
print {$socket} "GET / \n";
my $html = join '', <$socket>;
# example 2
my $socket = IO::Socket::INET->new(
PeerHost => 'www.booking.com',
PeerPort => 80,
);

$socket->print("GET / \n");
my $html = join '', $socket->getlines();

Why sockets timeouts are important

Connection timeout

my $socket = IO::Socket::INET->new(
PeerHost => '127.0.0.1',
PeerPort => 80,
Timeout => 3,
);

Read/Write timeouts via setsockopt

my $seconds  = int($timeout);
my $useconds = int( 1_000_000 * ( $timeout - $seconds ) );
my $timeout = pack( 'l!l!', $seconds, $useconds );
$socket->setsockopt( SOL_SOCKET, SO_RCVTIMEO, $timeout )
# then use $socket as usual

Read/Write timeouts via select

sub _can_read {
my ( $file_desc, $timeout ) = @_;
vec( my $fdset = '', $file_desc, 1 ) = 1;
my $nfound = select( $fdset, undef, undef, $timeout );
}

Using an external library

my $handle = AnyEvent::Handle->new (
connect => [ $host, $port ],
on_prepare => sub { 0.5 },
# ...
);

$handle->on_timeout( sub { say 'timeout occurred' } );
$handle->timeout(0.01);

Provide a nice API

my $socket = IO::Socket::INET->new( ... );
print {$socket} 'something';
my $socket = IO::Socket::INET->new( ... );

# set timeouts
$socket->read_timeout(0.5);

# use the socket as before
print {$socket} 'something';

# later, get the timeout value
my $timeout = $socket->read_timeout();

when using setsockopt

when using select

PerlIO layers

open my $fh, 'filename';
# for direct binary non-buffered access
binmode $fh, ':raw';
# specify that the file is in utf8, and enforce validation
binmode $fh, ':encoding(UTF-8)';
my $line = <$fh>;
binmode $fh, ':via(MyOwnLayer)';

Implementing a timeout PerlIO layer

package PerlIO::via::Timeout;
sub READ {
my ( $self, $buf, $len, $fh ) = @_;
my $fd = fileno($fh);

# we use the same can_read as previously
can_read( $fd, $timeout )
or return 0;

return sysread( $fh, $buf, $len, 0 );
}
use PerlIO::via::Timeout;
open my $fh, '<:via(Timeout)', 'foo.html';
my $line = <$fh>;
if ( $line == undef && 0+$! == ETIMEDOUT ) {
# timed out reading
...
} else {
# we read one line fast enough, success!
...
}

Add a properties to a Handle using InsideOut OO

my %fd_properties;

sub PUSHED {
my ( $class, $mode, $fh ) = @_;
$fd_properties{ fileno($fh) } = { read_timeout => 0.5 };
# ...
}

Implement the API

wrap it up

# 1. Creates a socket as usual
my $socket = IO::Socket::INET->new( ... );

# 2. Enable read and write timeouts on the socket
IO::Socket::Timeout->enable_timeouts_on($socket);

# 3. Setup the timeouts
$socket->read_timeout(0.5);
$socket->write_timeout(0.5);

# 4. Use the socket as usual
my $data = <$socket>;

# 5. Profit!

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store