classification
Title: asyncore/Windows: select() doesn't report errors for a non-blocking connect()
Type: behavior Stage:
Components: asyncio, Library (Lib), Windows Versions: Python 2.3
process
Status: closed Resolution: fixed
Dependencies: Superseder: asyncore fixes and improvements
View: 909005
Assigned To: josiahcarlson Nosy List: ajaksu2, garth42, giampaolo.rodola, gvanrossum, intgr, johnjsmith, josiahcarlson, klimkin, vstinner, yselivanov
Priority: normal Keywords:

Created on 2003-07-25 14:43 by garth42, last changed 2014-06-27 22:42 by vstinner. This issue is now closed.

Messages (11)
msg17326 - (view) Author: Garth Bushell (garth42) Date: 2003-07-25 14:43
asyncore.poll is broken on windows. If a connection is
refused happens it will hang for ever and never raise
an exception.

The Select statment never checks the exfds. This is
needed as this is where windows reports failed
connections. The documentation in the microsoft
platform SDK mentions this. 

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/select_2.asp

The suggested fix is shown below althought this is
untested. The correct error number is recived from
getsockopt(SOL_SOCKET,SO_ERROR) 

def poll(timeout=0.0, map=None):
     if map is None:
         map = socket_map
     if map:
         r = []; w = []; e = []
         for fd, obj in map.items():
             if obj.readable():
                 r.append(fd)
             if obj.writable():
                 w.append(fd)

             if sys.platform == 'win32':
                 if not obj.connected:
                     e.append(fd)
         if [] == r == w == e:
             time.sleep(timeout)
         else:
             try:
                 r, w, e = select.select(r, w, e, timeout)
             except select.error, err:
                 if err[0] != EINTR:
                     raise
                 else:
                     return

         if sys.platform == 'win32':
             for fd in e:
                 obj = map.get(fd)
                 if obj is None:
                     continue
                 errno =
fs.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
                 raise
socket.error,(errno,socketerrorTab[error])

         for fd in r:
             obj = map.get(fd)
             if obj is None:
                 continue
             read(obj)

         for fd in w:
             obj = map.get(fd)
             if obj is None:
                 continue
             write(obj)
msg17327 - (view) Author: John J Smith (johnjsmith) Date: 2003-07-29 12:49
Logged In: YES 
user_id=830565

I was bitten by the same problem.  My workaround (in a
Tkinter application) is given below.

Would it make sense to modify poll() to simply add the union
of r and w to e, and call handle_error() for any fd in e?

Workaround:

        try:
            self.connect(send_addr)
        except socket.error:
            self.handle_error()
        if sys.platform == 'win32':
            # Win98 select() doesn't seem to report errors for a
            # non-blocking connect().
            self.__connected = 0
            self.__frame.after(2000, self.__win_connect_poll)

...

    if sys.platform == 'win32':
        def __win_connect_poll (self):
            if self.__connected:
                return
            e = self.socket.getsockopt(socket.SOL_SOCKET,
                                       socket.SO_ERROR)
            if e in (0, errno.EINPROGRESS, 
errno.WSAEINPROGRESS):
                self.__frame.after(2000, self.__win_connect_poll)
            else:
                try:
                    str = socket.errorTab[e]
                except KeyError:
                    str = os.strerror(e)
                try:
                    raise socket.error(e, str)
                except socket.error:
                    self.handle_error()
msg17328 - (view) Author: Alexey Klimkin (klimkin) Date: 2004-03-04 08:23
Logged In: YES 
user_id=410460

Patch #909005 fixes the problem.
msg17329 - (view) Author: Josiah Carlson (josiahcarlson) * (Python triager) Date: 2007-01-07 06:19
I am looking into applying a variant of portions of #909005 to fix this bug.
msg81863 - (view) Author: Daniel Diniz (ajaksu2) (Python triager) Date: 2009-02-13 03:27
Looks like this one was solved in issue 909005, does anyone disagree?
msg81978 - (view) Author: Josiah Carlson (josiahcarlson) * (Python triager) Date: 2009-02-13 23:46
According to Garth, sockets that don't connect on Windows get put into 
the error sockets list.

According to John, you need to poll sockets to determine whether or not 
the attempted connection was refused.

If Garth is right, the problem is fixed, though we aren't quite 
retrieving the correct error code on win32.  If John is right, we need 
to repeatedly check for error conditions on sockets that are trying to 
connect to a remote host, and the problem is not fixed.
msg176687 - (view) Author: Marc Schlaich (schlamar) * Date: 2012-11-30 14:08
This issue is not fixed. Another workaround would be the `win32select` function from twisted: http://twistedmatrix.com/trac/browser/trunk/twisted/internet/selectreactor.py#L23
msg206573 - (view) Author: Mark Lawrence (BreamoreBoy) * Date: 2013-12-19 03:15
Is it worth leaving this open given the arrival of the new asyncio module in Python 3.4?
msg219304 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-05-28 21:44
This issue was opened 11 years ago, when Windows 95, 98, Me, 2000 and XP were used. Python is going to drop support of Windows XP, and most older versions are already no more supported. Does this issue still exist in recent Windows versions? I'm not aware of such bug.

Does asyncio have the bug?
msg221750 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-06-27 22:40
"Workaround: (...)
e = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)"

Oh, it looks like the issue was already fixed 4 years ago:
---
changeset:   63720:ba7353147507
branch:      3.1
parent:      63716:915b028b954d
user:        Giampaolo RodolĂ  <g.rodola@gmail.com>
date:        Wed Aug 04 09:04:53 2010 +0000
files:       Lib/asyncore.py Misc/ACKS Misc/NEWS
description:
Merged revisions 83705 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/py3k

........
  r83705 | giampaolo.rodola | 2010-08-04 11:02:27 +0200 (mer, 04 ago 2010) | 1 line

  fix issue #2944: asyncore doesn't handle connection refused correctly (patch by Alexander Shigin). Merged from 2.7 branch.
........


diff -r 915b028b954d -r ba7353147507 Lib/asyncore.py
--- a/Lib/asyncore.py   Wed Aug 04 04:53:07 2010 +0000
+++ b/Lib/asyncore.py   Wed Aug 04 09:04:53 2010 +0000
@@ -426,8 +426,11 @@ class dispatcher:
             self.handle_read()
 
     def handle_connect_event(self):
+        err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
+        if err != 0:
+            raise socket.error(err, _strerror(err))
+        self.handle_connect()
         self.connected = True
-        self.handle_connect()
 
     def handle_write_event(self):
         if self.accepting:
...
---
msg221751 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-06-27 22:42
Note: asyncio also calls getsockopt(SOL_SOCKET,SO_ERROR) to check if the connect() succeeded or not, and so it doesn't have this bug.
History
Date User Action Args
2014-06-27 22:42:04vstinnersetmessages: + msg221751
2014-06-27 22:40:49vstinnersetstatus: open -> closed
resolution: fixed
messages: + msg221750
2014-06-06 11:52:29vstinnersettitle: asyncore is broken for windows if connection is refused -> asyncore/Windows: select() doesn't report errors for a non-blocking connect()
2014-06-06 11:45:21vstinnersetnosy: + yselivanov, gvanrossum
components: + asyncio
2014-05-28 21:44:33vstinnersetnosy: + vstinner
messages: + msg219304
2014-02-03 15:41:32BreamoreBoysetnosy: - BreamoreBoy
2013-12-19 19:37:41schlamarsetnosy: - schlamar
2013-12-19 03:15:18BreamoreBoysetnosy: + BreamoreBoy
messages: + msg206573
2012-11-30 14:08:28schlamarsetnosy: + schlamar
messages: + msg176687
2009-03-28 12:18:28intgrsetnosy: + intgr
2009-02-13 23:46:55josiahcarlsonsetmessages: + msg81978
2009-02-13 03:27:42ajaksu2settype: behavior
superseder: asyncore fixes and improvements
messages: + msg81863
components: + Windows
nosy: + ajaksu2
2008-03-20 00:55:45giampaolo.rodolasetnosy: + giampaolo.rodola
2003-07-25 14:43:37garth42create