Skip Menu |

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

Report information
The Basics
Id: 92075
Status: resolved
Priority: 0/
Queue: IO-Socket-IP

People
Owner: Nobody in particular
Requestors: dagolden [...] cpan.org
Cc: oleg [...] cpan.org
AdminCc:

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



Subject: Timeout parameter
The INCOMPATIBILITIES section lists the Timeout as an unsupported parameter. Can it be added? If so, that would help the "drop-in" support. If not, could the rationale for why not be added to the documentation for clarity? Thanks, David
On Sun Jan 12 10:40:20 2014, DAGOLDEN wrote: Show quoted text
> The INCOMPATIBILITIES section lists the Timeout as an unsupported > parameter. Can it be added? If so, that would help the "drop-in" > support. If not, could the rationale for why not be added to the > documentation for clarity?
Turns out that all the Timeout functionallity itself is implemented by the base IO::Socket class, and thus actually applies to /any/ IO::Socket subclass. It is however not documented there, and instead mentioned briefly in passing in IO::Socket::INET, which doesn't do anything special to help implement it. There's therefore no need for IO::Socket::IP to document or mention it as it's simply some behaviour inherited from its superclass. I've just removed all mention of it instead. -- Paul Evans
On Thu Jan 16 07:33:02 2014, PEVANS wrote: Show quoted text
> On Sun Jan 12 10:40:20 2014, DAGOLDEN wrote:
> > The INCOMPATIBILITIES section lists the Timeout as an unsupported > > parameter. Can it be added? If so, that would help the "drop-in" > > support. If not, could the rationale for why not be added to the > > documentation for clarity?
> > Turns out that all the Timeout functionallity itself is implemented by > the base IO::Socket class, and thus actually applies to /any/ > IO::Socket subclass. It is however not documented there, and instead > mentioned briefly in passing in IO::Socket::INET, which doesn't do > anything special to help implement it. > > There's therefore no need for IO::Socket::IP to document or mention it > as it's simply some behaviour inherited from its superclass. I've just > removed all mention of it instead.
This is not totally correct. IO::Socket handles timeout inside connect() using IO::Select: https://metacpan.org/source/GBARR/IO-1.25/lib/IO/Socket.pm#L115 But IO::Socket::IP overloads this method. So, there is no timeout handling here Here is a test: use strict; use Test::More; use IO::Socket::INET; use IO::Socket::IP; use Time::HiRes; use constant { SLOW_HOST => '173.194.32.142', # google.com SLOW_PORT => 81 }; $SIG{ALRM} = sub { die "Alarmed\n"; }; for my $impl ('INET', 'IP') { eval { my $start = Time::HiRes::time; alarm(10); my $sock = "IO::Socket::$impl"->new(PeerHost => SLOW_HOST, PeerPort => SLOW_PORT, Timeout => 3); alarm(0); my $end = Time::HiRes::time; my $err = $@; ok(!$sock, "$impl: socket not created"); like($err, qr/timeout/i, "$impl: proper error") or diag $err; my $duration = $end - $start; ok($duration > 2 && $duration < 5, "$impl: timed out"); diag $duration; }; ok(!$@, "$impl: no exceptions") or diag $@; } done_testing(); __END__ which outputs: ok 1 - INET: socket not created ok 2 - INET: proper error ok 3 - INET: timed out ok 4 - INET: no exceptions # 3.00477385520935 not ok 5 - IP: no exceptions 1..5 # Failed test 'IP: no exceptions' # at timeout.pl line 32. # Alarmed # Looks like you failed 1 test of 5. Dubious, test returned 1 (wstat 256, 0x100) Failed 1/5 subtests Test Summary Report ------------------- timeout.pl (Wstat: 256 Tests: 5 Failed: 1) Failed test: 5 Non-zero exit status: 1 Files=1, Tests=5, 13 wallclock secs ( 0.04 usr 0.00 sys + 0.08 cusr 0.02 csys = 0.14 CPU) Result: FAIL
Ping. Paul, what are your thoughts on this? Even un(der)-documented, if we want this to be a drop-in replacement, it needs to match behaviors, and people do rely on timeout (even if it doesn't always do what they expect).
On Tue Sep 02 11:15:06 2014, DAGOLDEN wrote: Show quoted text
> Ping. > > Paul, what are your thoughts on this? Even un(der)-documented, if we > want this to be a drop-in replacement, it needs to match behaviors, > and people do rely on timeout (even if it doesn't always do what they > expect).
I can easily write some code I can guess is probably correct. I can even go as far as to test it on the platforms I have access to (ie. Linux). I won't be able to write a unit test that the smokers will be able to use, because I can't reliably create a socket in such a timeout state. It's not testable. Ergo I don't know how to test it, so I'm not sure how to proceed. -- Paul Evans
On Tue Sep 02 11:34:49 2014, PEVANS wrote: Show quoted text
> It's not testable. > > Ergo I don't know how to test it, so I'm not sure how to proceed.
As much as I love TDD, I think it's sometimes OK to write code we believe is correct, spot check it, and rely on bug reports from real users.
On Tue Sep 02 11:34:49 2014, PEVANS wrote: Show quoted text
> On Tue Sep 02 11:15:06 2014, DAGOLDEN wrote:
> > Ping. > > > > Paul, what are your thoughts on this? Even un(der)-documented, if we > > want this to be a drop-in replacement, it needs to match behaviors, > > and people do rely on timeout (even if it doesn't always do what they > > expect).
> > I can easily write some code I can guess is probably correct. I can > even go as far as to test it on the platforms I have access to (ie. > Linux). > > I won't be able to write a unit test that the smokers will be able to > use, because I can't reliably create a socket in such a timeout state. > It's not testable. > > Ergo I don't know how to test it, so I'm not sure how to proceed.
The variant how to create non-connectable socket: http://stackoverflow.com/a/904609
So... Do we have a properly-clear semantic definition of what "Timeout" should actually do here? I know it's applying to just the connect() phase. But what does it mean in practice? We have /no/ way to apply a timeout to the getaddrinfo() phase, so if the user is trying to connect by hostname and the (e.g.) DNS resolver takes a long time, that won't work. Presuming we manage to resolve a candidate list of addresses to attempt, how should the timeout apply to these? Should it apply separately to each address candidate? Or should it apply just once overall to all of the connect() calls - if the first one fails due to timeout we'll just stop there and never try the others..? If it applies just once overall, surely we should subtract the time that the name resolve operation took from this, so that the timeout effectively applies to the entire resolve+connect operation..? Or what? I don't have a clear idea of what the thing should do. -- Paul Evans
On Fri Sep 05 11:30:41 2014, PEVANS wrote: Show quoted text
> Do we have a properly-clear semantic definition of what "Timeout" > should actually do here?
I would argue "as close to what IO::Socket::INET does as possible". Show quoted text
> We have /no/ way to apply a timeout to the getaddrinfo() phase,
So let's not try to do that. (Though google tells me about getaddrinfo_a in some glibc's... is that an option if available?) Show quoted text
> Presuming we manage to resolve a candidate list of addresses to > attempt, how should the timeout apply to these?
I like the idea of it applying once overall, but not including DNS resolution since that's not under user control. As long as that's documented, I think that's acceptable. If someone needs more control, they can do the resolution themselves and then call connect per address with whatever timeout they desire.
On Fri Sep 05 11:53:13 2014, DAGOLDEN wrote: Show quoted text
> On Fri Sep 05 11:30:41 2014, PEVANS wrote:
> > Do we have a properly-clear semantic definition of what "Timeout" > > should actually do here?
> > I would argue "as close to what IO::Socket::INET does as possible".
From reading the code, it would appear to be implemented by the 'connect' method itself, so it's applying per address. Show quoted text
> > We have /no/ way to apply a timeout to the getaddrinfo() phase,
> > So let's not try to do that. (Though google tells me about > getaddrinfo_a in some glibc's... is that an option if available?)
I'm not sure I want support a non-default option for "some Linux boxes" at the cost of every other OS not supporting it. Show quoted text
> > Presuming we manage to resolve a candidate list of addresses to > > attempt, how should the timeout apply to these?
> > I like the idea of it applying once overall, but not including DNS > resolution since that's not under user control. As long as that's > documented, I think that's acceptable. > > If someone needs more control, they can do the resolution themselves > and then call connect per address with whatever timeout they desire.
It seems this isn't what IO::Socket::INET does then. -- Paul Evans
Subject: Re: [rt.cpan.org #92075] Timeout parameter
Date: Fri, 5 Sep 2014 12:10:32 -0400
To: bug-IO-Socket-IP [...] rt.cpan.org
From: David Golden <dagolden [...] cpan.org>
On Fri, Sep 5, 2014 at 12:01 PM, Paul Evans via RT < bug-IO-Socket-IP@rt.cpan.org> wrote: Show quoted text
> From reading the code, it would appear to be implemented by the 'connect' > method itself, so it's applying per address. > >
Then I suggest doing that. It wasn't clear to me in the code since I didn't see the loop per address. David
On Fri Sep 05 13:44:03 2014, PEVANS wrote: Show quoted text
The behavior when both Blocking => 0 and Timeout specified not compatible with IO::Socket::INET: time perl -Ilib -MIO::Socket::IP -E 'IO::Socket::IP->new(Blocking => 0, PeerAddr => "google.com", PeerPort => 81, Timeout => 5) or die $@' real 0m0.097s user 0m0.076s sys 0m0.012s perl -MIO::Socket::INET -E 'IO::Socket::INET->new(Blocking => 0, PeerAddr => "google.com", PeerPort => 81, Timeout => 5) or die $@' IO::Socket::INET: connect: timeout at -e line 1. real 0m5.095s user 0m0.064s sys 0m0.020s As you can see for this case IO::Socket::INET makes blocking connection with timeout. IO::Socket::IP just returns immediately. Don't know about others but I widely used this behaviour, for example: https://metacpan.org/source/OLEG/Net-Proxy-Type-0.08/lib/Net/Proxy/Type.pm#L575 Looks like typo in the docs: -This behviour is copied directly form `IO::Socket::INET'; for +This behviour is copied directly from `IO::Socket::INET'; for P.S. Hmm, how can I subscribe to this bug, so I can receive notifications about new messages?
On Sun Sep 07 01:22:00 2014, OLEG wrote: Show quoted text
> The behavior when both Blocking => 0 and Timeout specified not > compatible with IO::Socket::INET: > > time perl -Ilib -MIO::Socket::IP -E 'IO::Socket::IP->new(Blocking => > 0, PeerAddr => "google.com", PeerPort => 81, Timeout => 5) or die $@' > > real 0m0.097s > user 0m0.076s > sys 0m0.012s > > perl -MIO::Socket::INET -E 'IO::Socket::INET->new(Blocking => 0, > PeerAddr => "google.com", PeerPort => 81, Timeout => 5) or die $@' > IO::Socket::INET: connect: timeout at -e line 1. > > real 0m5.095s > user 0m0.064s > sys 0m0.020s > > As you can see for this case IO::Socket::INET makes blocking > connection with timeout. IO::Socket::IP just returns immediately. > Don't know about others but I widely used this behaviour, for example: > https://metacpan.org/source/OLEG/Net-Proxy-Type- > 0.08/lib/Net/Proxy/Type.pm#L575
Yes; I see it looks different, but I notice that IO::Socket::INET doesn't specify the interaction between Blocking and Timeout. Having read the implementation it does look as if that's an unintended bug - the implementor of timeouts in sub connect having just forgotten about the nonblocking case. I consider the behaviour in IO::Socket::IP to be more useful - that 'Blocking' determines if connect will block or just return immediately; if Timeout is also specified it determines the maximum time that it will block for, if it choses to block at all. My advice here would be simply not to specify both 'Blocking => 0' and a Timeout - if you want to perform nonblocking IO -after- the connect, you can do that simply by my $sock = $class->new( PeerAddr => ..., Timeout => $TIME ) or die "Cannot connect - $@"; $sock->blocking( 0 ); which will behave the same for IO::Socket::IP or ::INET Show quoted text
> Looks like typo in the docs: > -This behviour is copied directly form `IO::Socket::INET'; for > +This behviour is copied directly from `IO::Socket::INET'; for
Thanks, fixed. Show quoted text
> P.S. > Hmm, how can I subscribe to this bug, so I can receive notifications > about new messages?
Add yourself to the 'cc' list -- Paul Evans
Show quoted text
> My advice here would be simply not to specify both 'Blocking => 0' and > a Timeout - if you want to perform nonblocking IO -after- the connect, > you can do that simply by > > my $sock = $class->new( PeerAddr => ..., Timeout => $TIME ) > or die "Cannot connect - $@"; > $sock->blocking( 0 ); >
For me IO::Socket::INET's behavior is more like a feature and not a bug. My vote would be to implement same behavior.
CC: oleg [...] cpan.org
Subject: Re: [rt.cpan.org #92075] Timeout parameter
Date: Mon, 8 Sep 2014 11:29:43 -0400
To: bug-IO-Socket-IP [...] rt.cpan.org
From: David Golden <dagolden [...] cpan.org>
I'm with Paul on the Blocking/Timeout case. If I say "non-blocking" then I want it actually non-blocking, not "non-blocking, but only after blocking to connect". David
This is now implemented in 0.32, with documentation explaining the slight difference from ::INET with regards to Blocking => 0 with Timeout. -- Paul Evans