You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
473 lines
16 KiB
473 lines
16 KiB
# Copyright (c) 2009-2014 Denis Bilenko and gevent contributors. See LICENSE for details.
|
|
"""
|
|
Python 2 socket module.
|
|
"""
|
|
from __future__ import absolute_import
|
|
|
|
# Our import magic sadly makes this warning useless
|
|
# pylint: disable=undefined-variable
|
|
|
|
from gevent import _socketcommon
|
|
from gevent._util import copy_globals
|
|
from gevent._compat import PYPY
|
|
from gevent.timeout import Timeout
|
|
|
|
copy_globals(_socketcommon, globals(),
|
|
names_to_ignore=_socketcommon.__py3_imports__ + _socketcommon.__extensions__,
|
|
dunder_names_to_keep=())
|
|
|
|
__socket__ = _socketcommon.__socket__
|
|
__implements__ = _socketcommon._implements
|
|
__extensions__ = _socketcommon.__extensions__
|
|
__imports__ = [i for i in _socketcommon.__imports__ if i not in _socketcommon.__py3_imports__]
|
|
__dns__ = _socketcommon.__dns__
|
|
try:
|
|
_fileobject = __socket__._fileobject
|
|
_socketmethods = __socket__._socketmethods
|
|
except AttributeError:
|
|
# Allow this module to be imported under Python 3
|
|
# for building the docs
|
|
_fileobject = object
|
|
_socketmethods = ('bind', 'connect', 'connect_ex',
|
|
'fileno', 'listen', 'getpeername',
|
|
'getsockname', 'getsockopt',
|
|
'setsockopt', 'sendall',
|
|
'setblocking', 'settimeout',
|
|
'gettimeout', 'shutdown')
|
|
else:
|
|
# Python 2 doesn't natively support with statements on _fileobject;
|
|
# but it eases our test cases if we can do the same with on both Py3
|
|
# and Py2. Implementation copied from Python 3
|
|
assert not hasattr(_fileobject, '__enter__')
|
|
# we could either patch in place:
|
|
#_fileobject.__enter__ = lambda self: self
|
|
#_fileobject.__exit__ = lambda self, *args: self.close() if not self.closed else None
|
|
# or we could subclass. subclassing has the benefit of not
|
|
# changing the behaviour of the stdlib if we're just imported; OTOH,
|
|
# under Python 2.6/2.7, test_urllib2net.py asserts that the class IS
|
|
# socket._fileobject (sigh), so we have to work around that.
|
|
|
|
# We also make it call our custom socket closing method that disposes
|
|
# if IO watchers but not the actual socket itself.
|
|
|
|
# Python 2 relies on reference counting to close sockets, so this is all
|
|
# very ugly and fragile.
|
|
|
|
class _fileobject(_fileobject): # pylint:disable=function-redefined
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *args):
|
|
if not self.closed:
|
|
self.close()
|
|
|
|
def close(self):
|
|
if self._sock is not None:
|
|
self._sock._drop_events()
|
|
super(_fileobject, self).close()
|
|
|
|
|
|
def _get_memory(data):
|
|
try:
|
|
mv = memoryview(data)
|
|
if mv.shape:
|
|
return mv
|
|
# No shape, probably working with a ctypes object,
|
|
# or something else exotic that supports the buffer interface
|
|
return mv.tobytes()
|
|
except TypeError:
|
|
# fixes "python2.7 array.array doesn't support memoryview used in
|
|
# gevent.socket.send" issue
|
|
# (http://code.google.com/p/gevent/issues/detail?id=94)
|
|
return buffer(data)
|
|
|
|
|
|
class _closedsocket(object):
|
|
__slots__ = []
|
|
|
|
def _dummy(*args, **kwargs): # pylint:disable=no-method-argument,unused-argument
|
|
raise error(EBADF, 'Bad file descriptor')
|
|
# All _delegate_methods must also be initialized here.
|
|
send = recv = recv_into = sendto = recvfrom = recvfrom_into = _dummy
|
|
|
|
if PYPY:
|
|
|
|
def _drop(self):
|
|
pass
|
|
|
|
def _reuse(self):
|
|
pass
|
|
|
|
__getattr__ = _dummy
|
|
|
|
|
|
timeout_default = object()
|
|
|
|
from gevent._hub_primitives import wait_on_socket as _wait_on_socket
|
|
|
|
class socket(object):
|
|
"""
|
|
gevent `socket.socket <https://docs.python.org/2/library/socket.html#socket-objects>`_
|
|
for Python 2.
|
|
|
|
This object should have the same API as the standard library socket linked to above. Not all
|
|
methods are specifically documented here; when they are they may point out a difference
|
|
to be aware of or may document a method the standard library does not.
|
|
"""
|
|
|
|
# pylint:disable=too-many-public-methods
|
|
|
|
def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None):
|
|
if _sock is None:
|
|
self._sock = _realsocket(family, type, proto)
|
|
self.timeout = _socket.getdefaulttimeout()
|
|
else:
|
|
if hasattr(_sock, '_sock'):
|
|
# passed a gevent socket
|
|
self._sock = _sock._sock
|
|
self.timeout = getattr(_sock, 'timeout', False)
|
|
if self.timeout is False:
|
|
self.timeout = _socket.getdefaulttimeout()
|
|
else:
|
|
# passed a native socket
|
|
self._sock = _sock
|
|
self.timeout = _socket.getdefaulttimeout()
|
|
if PYPY:
|
|
self._sock._reuse()
|
|
self._sock.setblocking(0)
|
|
fileno = self._sock.fileno()
|
|
self.hub = get_hub()
|
|
io = self.hub.loop.io
|
|
self._read_event = io(fileno, 1)
|
|
self._write_event = io(fileno, 2)
|
|
|
|
def __repr__(self):
|
|
return '<%s at %s %s>' % (type(self).__name__, hex(id(self)), self._formatinfo())
|
|
|
|
def __str__(self):
|
|
return '<%s %s>' % (type(self).__name__, self._formatinfo())
|
|
|
|
def _formatinfo(self):
|
|
# pylint:disable=broad-except
|
|
try:
|
|
fileno = self.fileno()
|
|
except Exception as ex:
|
|
fileno = str(ex)
|
|
try:
|
|
sockname = self.getsockname()
|
|
sockname = '%s:%s' % sockname
|
|
except Exception:
|
|
sockname = None
|
|
try:
|
|
peername = self.getpeername()
|
|
peername = '%s:%s' % peername
|
|
except Exception:
|
|
peername = None
|
|
result = 'fileno=%s' % fileno
|
|
if sockname is not None:
|
|
result += ' sock=' + str(sockname)
|
|
if peername is not None:
|
|
result += ' peer=' + str(peername)
|
|
if getattr(self, 'timeout', None) is not None:
|
|
result += ' timeout=' + str(self.timeout)
|
|
return result
|
|
|
|
def _get_ref(self):
|
|
return self._read_event.ref or self._write_event.ref
|
|
|
|
def _set_ref(self, value):
|
|
self._read_event.ref = value
|
|
self._write_event.ref = value
|
|
|
|
ref = property(_get_ref, _set_ref)
|
|
|
|
_wait = _wait_on_socket
|
|
|
|
def accept(self):
|
|
while 1:
|
|
try:
|
|
client_socket, address = self._sock.accept()
|
|
break
|
|
except error as ex:
|
|
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
|
|
raise
|
|
sys.exc_clear()
|
|
self._wait(self._read_event)
|
|
sockobj = socket(_sock=client_socket)
|
|
if PYPY:
|
|
client_socket._drop()
|
|
return sockobj, address
|
|
|
|
def _drop_events(self, cancel_wait_ex=cancel_wait_ex):
|
|
if self._read_event is not None:
|
|
self.hub.cancel_wait(self._read_event, cancel_wait_ex, True)
|
|
self._read_event = None
|
|
if self._write_event is not None:
|
|
self.hub.cancel_wait(self._write_event, cancel_wait_ex, True)
|
|
self._write_event = None
|
|
|
|
|
|
def close(self, _closedsocket=_closedsocket):
|
|
# This function should not reference any globals. See Python issue #808164.
|
|
|
|
# Also break any reference to the loop.io objects. Our fileno,
|
|
# which they were tied to, is now free to be reused, so these
|
|
# objects are no longer functional.
|
|
self._drop_events()
|
|
s = self._sock
|
|
|
|
# Note that we change self._sock at this point. Methods *must not*
|
|
# cache `self._sock` separately from self._write_event/self._read_event,
|
|
# or they will be out of sync and we may get inappropriate errors.
|
|
# (See test__hub:TestCloseSocketWhilePolling for an example).
|
|
|
|
self._sock = _closedsocket()
|
|
if PYPY:
|
|
s._drop()
|
|
|
|
@property
|
|
def closed(self):
|
|
return isinstance(self._sock, _closedsocket)
|
|
|
|
def connect(self, address):
|
|
if self.timeout == 0.0:
|
|
return self._sock.connect(address)
|
|
|
|
address = _socketcommon._resolve_addr(self._sock, address)
|
|
|
|
timer = Timeout._start_new_or_dummy(self.timeout, timeout('timed out'))
|
|
try:
|
|
while 1:
|
|
err = self._sock.getsockopt(SOL_SOCKET, SO_ERROR)
|
|
if err:
|
|
raise error(err, strerror(err))
|
|
result = self._sock.connect_ex(address)
|
|
if not result or result == EISCONN:
|
|
break
|
|
elif (result in (EWOULDBLOCK, EINPROGRESS, EALREADY)) or (result == EINVAL and is_windows):
|
|
self._wait(self._write_event)
|
|
else:
|
|
raise error(result, strerror(result))
|
|
finally:
|
|
timer.close()
|
|
|
|
def connect_ex(self, address):
|
|
try:
|
|
return self.connect(address) or 0
|
|
except timeout:
|
|
return EAGAIN
|
|
except error as ex:
|
|
if type(ex) is error: # pylint:disable=unidiomatic-typecheck
|
|
return ex.args[0]
|
|
raise # gaierror is not silenced by connect_ex
|
|
|
|
def dup(self):
|
|
"""dup() -> socket object
|
|
|
|
Return a new socket object connected to the same system resource.
|
|
Note, that the new socket does not inherit the timeout."""
|
|
return socket(_sock=self._sock)
|
|
|
|
def makefile(self, mode='r', bufsize=-1):
|
|
# Two things to look out for:
|
|
# 1) Closing the original socket object should not close the
|
|
# fileobject (hence creating a new socket instance);
|
|
# An alternate approach is what _socket3.py does, which is to
|
|
# keep count of the times makefile objects have been opened (Py3's
|
|
# SocketIO helps with that). But the newly created socket, which
|
|
# has its own read/write watchers, does need those to be closed
|
|
# when the fileobject is; our custom subclass does that. Note that
|
|
# we can't pass the 'close=True' argument, as that causes reference counts
|
|
# to get screwed up, and Python2 sockets rely on those.
|
|
# 2) The resulting fileobject must keep the timeout in order
|
|
# to be compatible with the stdlib's socket.makefile.
|
|
# Pass self as _sock to preserve timeout.
|
|
fobj = _fileobject(type(self)(_sock=self), mode, bufsize)
|
|
if PYPY:
|
|
self._sock._drop()
|
|
return fobj
|
|
|
|
def recv(self, *args):
|
|
while 1:
|
|
try:
|
|
return self._sock.recv(*args)
|
|
except error as ex:
|
|
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
|
|
raise
|
|
# QQQ without clearing exc_info test__refcount.test_clean_exit fails
|
|
sys.exc_clear()
|
|
self._wait(self._read_event)
|
|
|
|
def recvfrom(self, *args):
|
|
while 1:
|
|
try:
|
|
return self._sock.recvfrom(*args)
|
|
except error as ex:
|
|
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
|
|
raise
|
|
sys.exc_clear()
|
|
self._wait(self._read_event)
|
|
|
|
def recvfrom_into(self, *args):
|
|
while 1:
|
|
try:
|
|
return self._sock.recvfrom_into(*args)
|
|
except error as ex:
|
|
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
|
|
raise
|
|
sys.exc_clear()
|
|
self._wait(self._read_event)
|
|
|
|
def recv_into(self, *args):
|
|
while 1:
|
|
try:
|
|
return self._sock.recv_into(*args)
|
|
except error as ex:
|
|
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
|
|
raise
|
|
sys.exc_clear()
|
|
self._wait(self._read_event)
|
|
|
|
def send(self, data, flags=0, timeout=timeout_default):
|
|
if timeout is timeout_default:
|
|
timeout = self.timeout
|
|
try:
|
|
return self._sock.send(data, flags)
|
|
except error as ex:
|
|
if ex.args[0] not in _socketcommon.GSENDAGAIN or timeout == 0.0:
|
|
raise
|
|
sys.exc_clear()
|
|
self._wait(self._write_event)
|
|
try:
|
|
return self._sock.send(data, flags)
|
|
except error as ex2:
|
|
if ex2.args[0] == EWOULDBLOCK:
|
|
return 0
|
|
raise
|
|
|
|
def sendall(self, data, flags=0):
|
|
if isinstance(data, unicode):
|
|
data = data.encode()
|
|
# this sendall is also reused by gevent.ssl.SSLSocket subclass,
|
|
# so it should not call self._sock methods directly
|
|
data_memory = _get_memory(data)
|
|
return _socketcommon._sendall(self, data_memory, flags)
|
|
|
|
def sendto(self, *args):
|
|
try:
|
|
return self._sock.sendto(*args)
|
|
except error as ex:
|
|
if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0:
|
|
raise
|
|
sys.exc_clear()
|
|
self._wait(self._write_event)
|
|
try:
|
|
return self._sock.sendto(*args)
|
|
except error as ex2:
|
|
if ex2.args[0] == EWOULDBLOCK:
|
|
return 0
|
|
raise
|
|
|
|
def setblocking(self, flag):
|
|
if flag:
|
|
self.timeout = None
|
|
else:
|
|
self.timeout = 0.0
|
|
|
|
def settimeout(self, howlong):
|
|
if howlong is not None:
|
|
try:
|
|
f = howlong.__float__
|
|
except AttributeError:
|
|
raise TypeError('a float is required')
|
|
howlong = f()
|
|
if howlong < 0.0:
|
|
raise ValueError('Timeout value out of range')
|
|
self.__dict__['timeout'] = howlong # avoid recursion with any property on self.timeout
|
|
|
|
def gettimeout(self):
|
|
return self.__dict__['timeout'] # avoid recursion with any property on self.timeout
|
|
|
|
def shutdown(self, how):
|
|
if how == 0: # SHUT_RD
|
|
self.hub.cancel_wait(self._read_event, cancel_wait_ex)
|
|
elif how == 1: # SHUT_WR
|
|
self.hub.cancel_wait(self._write_event, cancel_wait_ex)
|
|
else:
|
|
self.hub.cancel_wait(self._read_event, cancel_wait_ex)
|
|
self.hub.cancel_wait(self._write_event, cancel_wait_ex)
|
|
self._sock.shutdown(how)
|
|
|
|
family = property(lambda self: self._sock.family)
|
|
type = property(lambda self: self._sock.type)
|
|
proto = property(lambda self: self._sock.proto)
|
|
|
|
def fileno(self):
|
|
return self._sock.fileno()
|
|
|
|
def getsockname(self):
|
|
return self._sock.getsockname()
|
|
|
|
def getpeername(self):
|
|
return self._sock.getpeername()
|
|
|
|
# delegate the functions that we haven't implemented to the real socket object
|
|
|
|
_s = "def %s(self, *args): return self._sock.%s(*args)\n\n"
|
|
_m = None
|
|
for _m in set(_socketmethods) - set(locals()):
|
|
exec(_s % (_m, _m,))
|
|
del _m, _s
|
|
|
|
if PYPY:
|
|
|
|
def _reuse(self):
|
|
self._sock._reuse()
|
|
|
|
def _drop(self):
|
|
self._sock._drop()
|
|
|
|
|
|
SocketType = socket
|
|
|
|
if hasattr(_socket, 'socketpair'):
|
|
|
|
def socketpair(family=getattr(_socket, 'AF_UNIX', _socket.AF_INET),
|
|
type=_socket.SOCK_STREAM, proto=0):
|
|
one, two = _socket.socketpair(family, type, proto)
|
|
result = socket(_sock=one), socket(_sock=two)
|
|
if PYPY:
|
|
one._drop()
|
|
two._drop()
|
|
return result
|
|
elif 'socketpair' in __implements__:
|
|
__implements__.remove('socketpair')
|
|
|
|
if hasattr(_socket, 'fromfd'):
|
|
|
|
def fromfd(fd, family, type, proto=0):
|
|
s = _socket.fromfd(fd, family, type, proto)
|
|
result = socket(_sock=s)
|
|
if PYPY:
|
|
s._drop()
|
|
return result
|
|
|
|
elif 'fromfd' in __implements__:
|
|
__implements__.remove('fromfd')
|
|
|
|
if hasattr(__socket__, 'ssl'):
|
|
|
|
def ssl(sock, keyfile=None, certfile=None):
|
|
# deprecated in 2.7.9 but still present;
|
|
# sometimes backported by distros. See ssl.py
|
|
# Note that we import gevent.ssl, not _ssl2, to get the correct
|
|
# version.
|
|
from gevent import ssl as _sslmod
|
|
# wrap_socket is 2.7.9/backport, sslwrap_simple is older. They take
|
|
# the same arguments.
|
|
wrap = getattr(_sslmod, 'wrap_socket', None) or getattr(_sslmod, 'sslwrap_simple')
|
|
return wrap(sock, keyfile, certfile)
|
|
__implements__.append('ssl')
|
|
|
|
__all__ = __implements__ + __extensions__ + __imports__
|
|
|