Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast detection or simulation of WSAECONNREFUSED

Windows sockets have some strange behavior when it comes to WSAECONNREFUSED (which means backlog full or port not available, see https://stackoverflow.com/a/10308338/851737). If Windows detects one of these conditions, it retries (up to) two times with an interval of 0.5s. This means it takes at least 1 second to detect WSAECONNREFUSED on a socket connection attempt (http://support.microsoft.com/kb/175523/en-us).

Is there any way to speed up this detection without messing with the registry values? I need to simulate refusing socket connections in unittests. A workaround like simulating a refused connection with raw sockets would be acceptable, too.

Here is a simple Python script demonstrating the issue:

import errno
import socket
import time

PORT = 50123


def main():
    s = socket.socket()
    s.bind(('127.0.0.1', PORT))
    s.listen(0)
    client = socket.socket()
    client.connect(('127.0.0.1', PORT))

    client2 = socket.socket()
    start = time.time()

    try:
        client2.connect(('127.0.0.1', PORT))
    except socket.error as e:
        assert e.errno == errno.WSAECONNREFUSED
        print 'connection attempt took', time.time() - start
    finally:
        client2.close()
        client.close()
        s.close()


if __name__ == '__main__':
    main()
like image 271
schlamar Avatar asked Jan 16 '14 16:01

schlamar


2 Answers

It's not exactly what you asked about. But if you need this in the unittests only, mock library would be useful.

import errno
import socket
import time
import mock

PORT = 50123


def connect_mock(*agrs):
    raise socket.error(errno.WSAECONNREFUSED, "Testing")


def main():
    s = socket.socket()
    s.bind(('127.0.0.1', PORT))
    s.listen(0)
    client = socket.socket()
    client.connect(('127.0.0.1', PORT))

    client2 = socket.socket()
    start = time.time()

    with mock.patch('socket.socket.connect', connect_mock):
        try:
            client2.connect(('127.0.0.1', PORT))
            print "done"
        except socket.error as e:
            assert e.errno == errno.WSAECONNREFUSED
            print 'connection attempt took', time.time() - start
        finally:
            client2.close()
            client.close()
            s.close()


if __name__ == '__main__':
    main()
like image 127
Dmitry Vakhrushev Avatar answered Sep 22 '22 03:09

Dmitry Vakhrushev


Here is my solution based on dmitry-vakhrushev's answer which is patching the connect method more intelligent:

if sys.platform == 'win32':
    n_calls = [0]
    org_connect = socket.socket.connect

    def refusing_connect(*args):
        if n_calls[0] < 2:
            n_calls[0] += 1
            raise socket.error(errno.WSAECONNREFUSED, "Testing")
        return org_connect(*args)

    # patch socket.connect to speed up WSAECONNREFUSED detection
    patcher = mock.patch('socket.socket.connect', refusing_connect)
    patcher.start()
    self.addCleanup(patcher.stop)
like image 38
schlamar Avatar answered Sep 21 '22 03:09

schlamar