Anorov/PySocks

Non-blocking behaviour (timeout = 0) is ignored

Lonami opened this issue · 3 comments

Hello! I'm facing a bit of an issue with PySocks and asyncio together (by using loop.sock_connect). As it turns out, PySocks seems to be ignoring the non-blocking behaviour. This seems to be the cause:

PySocks/socks.py

Lines 94 to 110 in b687a34

def set_self_blocking(function):
@functools.wraps(function)
def wrapper(*args, **kwargs):
self = args[0]
try:
_is_blocking = self.gettimeout()
if _is_blocking == 0:
self.setblocking(True)
return function(*args, **kwargs)
except Exception as e:
raise
finally:
# set orgin blocking
if _is_blocking == 0:
self.setblocking(False)
return wrapper

Now, could I get some explanation on why that is needed? Here is the code to reproduce the issue:

import socks
import socket
import time
import asyncio

loop = asyncio.get_event_loop()

async def main():
    s = socks.socksocket(socket.AF_INET)
    #   ^^^^^ replacing "socks" with "socket" works correctly
    # But using "socks" does not work:

    s.setblocking(False)
    start = time.time()
    try:
        # The IP we try to connect to doesn't really matter
        await asyncio.wait_for(
            loop.sock_connect(s, ('1.1.1.1', 1234)),
            timeout=2,
            loop=loop
        )
    except:
        pass
    finally:
        print('Took', time.time() - start)

loop.run_until_complete(main())

The change that introduced this (a2cab50) does not give much information:

fix a error when the socket connect is not blocking
add a Decorator to checking the socket's blocking

Same problem: eventlet/eventlet#616

@terryzhu suggested this workaround

def setblocking(self, v):
+    if self._timeout == v:
+        return
     if v:
         self.settimeout(None)
     else:
         self.settimeout(0.0)

I think, pysocks should use existing get/settimeout and don't touch blocking behavior at all. Sorry, don't have time for proper solution right now.

qkn commented

any fix for this?

I no longer depend on PySocks, asyncio-native proxy libraries exist. I won't promote it here, but you can find the one I use on the optional requirements for the library behind the first link I posted.