Kaydet (Commit) 67f7a388 authored tarafından Guido van Rossum's avatar Guido van Rossum

SF patch 555085 (timeout socket implementation) by Michael Gilfix.

I've made considerable changes to Michael's code, specifically to use
the select() system call directly and to store the timeout as a C
double instead of a Python object; internally, -1.0 (or anything
negative) represents the None from the API.

I'm not 100% sure that all corner cases are covered correctly, so
please keep an eye on this.  Next I'm going to try it Windows before
Tim complains.

No way is this a bugfix candidate. :-)
üst c9a55776
......@@ -134,7 +134,8 @@ def getfqdn(name=''):
_socketmethods = (
'bind', 'connect', 'connect_ex', 'fileno', 'listen',
'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
'recv', 'recvfrom', 'send', 'sendall', 'sendto', 'setblocking', 'shutdown')
'recv', 'recvfrom', 'send', 'sendall', 'sendto', 'setblocking',
'settimeout', 'gettimeout', 'shutdown')
class _socketobject:
......@@ -168,94 +169,108 @@ class _socketobject:
class _fileobject:
"""Implements a file object on top of a regular socket object."""
def __init__(self, sock, mode, bufsize):
def __init__(self, sock, mode='rb', bufsize=8192):
self._sock = sock
self._mode = mode
if bufsize < 0:
if bufsize <= 0:
bufsize = 512
self._rbufsize = max(1, bufsize)
self._rbufsize = bufsize
self._wbufsize = bufsize
self._wbuf = self._rbuf = ""
self._rbuf = [ ]
self._wbuf = [ ]
def close(self):
try:
if self._sock:
self.flush()
finally:
self._sock = 0
self._sock = None
def __del__(self):
self.close()
def flush(self):
if self._wbuf:
self._sock.sendall(self._wbuf)
self._wbuf = ""
buffer = ''.join(self._wbuf)
self._sock.sendall(buffer)
self._wbuf = [ ]
def fileno(self):
def fileno (self):
return self._sock.fileno()
def write(self, data):
self._wbuf = self._wbuf + data
self._wbuf.append (data)
# A _wbufsize of 1 means we're doing unbuffered IO.
# Flush accordingly.
if self._wbufsize == 1:
if '\n' in data:
self.flush()
else:
if len(self._wbuf) >= self._wbufsize:
self.flush()
self.flush ()
elif self.__get_wbuf_len() >= self._wbufsize:
self.flush()
def writelines(self, list):
filter(self._sock.sendall, list)
self.flush()
def read(self, n=-1):
if n >= 0:
k = len(self._rbuf)
if n <= k:
data = self._rbuf[:n]
self._rbuf = self._rbuf[n:]
return data
n = n - k
L = [self._rbuf]
self._rbuf = ""
while n > 0:
new = self._sock.recv(max(n, self._rbufsize))
if not new: break
k = len(new)
if k > n:
L.append(new[:n])
self._rbuf = new[n:]
break
L.append(new)
n = n - k
return "".join(L)
k = max(512, self._rbufsize)
L = [self._rbuf]
self._rbuf = ""
while 1:
new = self._sock.recv(k)
if not new: break
L.append(new)
k = min(k*2, 1024**2)
return "".join(L)
def readline(self, limit=-1):
data = ""
i = self._rbuf.find('\n')
while i < 0 and not (0 < limit <= len(self._rbuf)):
new = self._sock.recv(self._rbufsize)
if not new: break
i = new.find('\n')
if i >= 0: i = i + len(self._rbuf)
self._rbuf = self._rbuf + new
if i < 0: i = len(self._rbuf)
else: i = i+1
if 0 <= limit < len(self._rbuf): i = limit
data, self._rbuf = self._rbuf[:i], self._rbuf[i:]
def __get_wbuf_len (self):
buf_len = 0
for i in [len(x) for x in self._wbuf]:
buf_len += i
return buf_len
def __get_rbuf_len(self):
buf_len = 0
for i in [len(x) for x in self._rbuf]:
buf_len += i
return buf_len
def read(self, size=-1):
buf_len = self.__get_rbuf_len()
while size < 0 or buf_len < size:
recv_size = max(self._rbufsize, size - buf_len)
data = self._sock.recv(recv_size)
if not data:
break
buf_len += len(data)
self._rbuf.append(data)
data = ''.join(self._rbuf)
# Clear the rbuf at the end so we're not affected by
# an exception during a recv
self._rbuf = [ ]
if buf_len > size and size >= 0:
self._rbuf.append(data[size:])
data = data[:size]
return data
def readline(self, size=-1):
index = -1
buf_len = self.__get_rbuf_len()
if len (self._rbuf):
index = min([x.find('\n') for x in self._rbuf])
while index < 0 and (size < 0 or buf_len < size):
recv_size = max(self._rbufsize, size - buf_len)
data = self._sock.recv(recv_size)
if not data:
break
buf_len += len(data)
self._rbuf.append(data)
index = data.find('\n')
data = ''.join(self._rbuf)
self._rbuf = [ ]
index = data.find('\n')
if index >= 0:
index += 1
elif buf_len > size:
index = size
else:
index = buf_len
self._rbuf.append(data[index:])
data = data[:index]
return data
def readlines(self, sizehint = 0):
def readlines(self, sizehint=0):
total = 0
list = []
while 1:
......
......@@ -109,6 +109,7 @@ except socket.error:
canfork = hasattr(os, 'fork')
try:
PORT = 50007
msg = 'socket test\n'
if not canfork or os.fork():
# parent is server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
......@@ -133,13 +134,52 @@ try:
f = conn.makefile()
if verbose:
print 'file obj:', f
data = conn.recv(1024)
if verbose:
print 'received:', data
conn.sendall(data)
# Perform a few tests on the windows file object
if verbose:
print "Staring _fileobject tests..."
f = socket._fileobject (conn, 'rb', 8192)
first_seg = f.read(7)
second_seg = f.read(5)
if not first_seg == 'socket ' or not second_seg == 'test\n':
print "Error performing read with the python _fileobject class"
os._exit (1)
elif verbose:
print "_fileobject buffered read works"
f.write (data)
f.flush ()
buf = ''
while 1:
data = conn.recv(1024)
if not data:
char = f.read(1)
if not char:
print "Error performing unbuffered read with the python ", \
"_fileobject class"
os._exit (1)
buf += char
if buf == msg:
if verbose:
print "__fileobject unbuffered read works"
break
if verbose:
print 'received:', data
conn.sendall(data)
if verbose:
# If we got this far, write() must work as well
print "__fileobject write works"
f.write(buf)
f.flush()
line = f.readline()
if not line == msg:
print "Error perferming readline with the python _fileobject class"
os._exit (1)
f.write(line)
f.flush()
if verbose:
print "__fileobject readline works"
conn.close()
else:
try:
......@@ -149,11 +189,18 @@ try:
if verbose:
print 'child connecting'
s.connect(("127.0.0.1", PORT))
msg = 'socket test'
s.send(msg)
data = s.recv(1024)
if msg != data:
print 'parent/client mismatch'
iteration = 0
while 1:
s.send(msg)
data = s.recv(12)
if not data:
break
if msg != data:
print "parent/client mismatch. Failed in %s iteration. Received: [%s]" \
%(iteration, data)
time.sleep (1)
iteration += 1
s.close()
finally:
os._exit(1)
......
#!/home/bernie/src/python23/dist/src/python
import unittest
import time
import socket
class creationTestCase(unittest.TestCase):
"""Test Case for socket.gettimeout() and socket.settimeout()"""
def setUp(self):
self.__s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def tearDown(self):
self.__s.close()
def testObjectCreation(self):
"Test Socket creation"
self.assertEqual(self.__s.gettimeout(), None,
"Timeout socket not default to disable (None)")
def testFloatReturnValue(self):
"Test return value of getter/setter"
self.__s.settimeout(7.345)
self.assertEqual(self.__s.gettimeout(), 7.345,
"settimeout() and gettimeout() return different result")
self.__s.settimeout(3)
self.assertEqual(self.__s.gettimeout(), 3,
"settimeout() and gettimeout() return different result")
def testReturnType(self):
"Test return type of getter/setter"
self.__s.settimeout(1)
self.assertEqual(type(self.__s.gettimeout()), type(1.0),
"return type of gettimeout() is not FloatType")
self.__s.settimeout(3.9)
self.assertEqual(type(self.__s.gettimeout()), type(1.0),
"return type of gettimeout() is not FloatType")
class timeoutTestCase(unittest.TestCase):
"""Test Case for socket.socket() timeout functions"""
def setUp(self):
self.__s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.__addr_remote = ('www.google.com', 80)
self.__addr_local = ('127.0.0.1', 25339)
def tearDown(self):
self.__s.close()
def testConnectTimeout(self):
"Test connect() timeout"
_timeout = 0.02
self.__s.settimeout(_timeout)
_t1 = time.time()
self.failUnlessRaises(socket.error, self.__s.connect,
self.__addr_remote)
_t2 = time.time()
_delta = abs(_t1 - _t2)
self.assert_(_delta < _timeout + 0.5,
"timeout (%f) is 0.5 seconds more than required (%f)"
%(_delta, _timeout))
def testRecvTimeout(self):
"Test recv() timeout"
_timeout = 0.02
self.__s.connect(self.__addr_remote)
self.__s.settimeout(_timeout)
_t1 = time.time()
self.failUnlessRaises(socket.error, self.__s.recv, 1024)
_t2 = time.time()
_delta = abs(_t1 - _t2)
self.assert_(_delta < _timeout + 0.5,
"timeout (%f) is 0.5 seconds more than required (%f)"
%(_delta, _timeout))
def testAcceptTimeout(self):
"Test accept() timeout()"
_timeout = 2
self.__s.settimeout(_timeout)
self.__s.bind(self.__addr_local)
self.__s.listen(5)
_t1 = time.time()
self.failUnlessRaises(socket.error, self.__s.accept)
_t2 = time.time()
_delta = abs(_t1 - _t2)
self.assert_(_delta < _timeout + 0.5,
"timeout (%f) is 0.5 seconds more than required (%f)"
%(_delta, _timeout))
def testRecvfromTimeout(self):
"Test recvfrom() timeout()"
_timeout = 2
self.__s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.__s.settimeout(_timeout)
self.__s.bind(self.__addr_local)
_t1 = time.time()
self.failUnlessRaises(socket.error, self.__s.recvfrom, 8192)
_t2 = time.time()
_delta = abs(_t1 - _t2)
self.assert_(_delta < _timeout + 0.5,
"timeout (%f) is 0.5 seconds more than required (%f)"
%(_delta, _timeout))
def testSend(self):
"Test send() timeout"
# couldn't figure out how to test it
pass
def testSendto(self):
"Test sendto() timeout"
# couldn't figure out how to test it
pass
def testSendall(self):
"Test sendall() timeout"
# couldn't figure out how to test it
pass
def suite():
suite = unittest.TestSuite()
return suite
if __name__ == "__main__":
unittest.main()
......@@ -66,6 +66,8 @@ Socket methods:
- s.sendall(string [,flags]) # tries to send everything in a loop
- s.sendto(string, [flags,] sockaddr) --> nbytes
- s.setblocking(0 | 1) --> None
- s.settimeout(None | float) -> None # Argument in seconds
- s.gettimeout() -> None or float seconds
- s.setsockopt(level, optname, value) --> None
- s.shutdown(how) --> None
- repr(s) --> "<socket object, fd=%d, family=%d, type=%d, protocol=%d>"
......@@ -421,6 +423,107 @@ PyGAI_Err(int error)
return NULL;
}
/* For timeout errors */
static PyObject *
timeout_err(void)
{
PyObject *v;
#ifdef MS_WINDOWS
v = Py_BuildValue("(is)", WSAETIMEDOUT, "Socket operation timed out");
#else
v = Py_BuildValue("(is)", ETIMEDOUT, "Socket operation timed out");
#endif
if (v != NULL) {
PyErr_SetObject(PySocket_Error, v);
Py_DECREF(v);
}
return NULL;
}
/* Function to perfrom the setting of socket blocking mode
* internally. block = (1 | 0).
*/
static int
internal_setblocking(PySocketSockObject *s, int block)
{
#ifndef RISCOS
#ifndef MS_WINDOWS
int delay_flag;
#endif
#endif
Py_BEGIN_ALLOW_THREADS
#ifdef __BEOS__
block = !block;
setsockopt( s->sock_fd, SOL_SOCKET, SO_NONBLOCK,
(void *)(&block), sizeof(int));
#else
#ifndef RISCOS
#ifndef MS_WINDOWS
#if defined(PYOS_OS2) && !defined(PYCC_GCC)
block = !block;
ioctl(s->sock_fd, FIONBIO, (caddr_t)&block, sizeof(block));
#else /* !PYOS_OS2 */
delay_flag = fcntl(s->sock_fd, F_GETFL, 0);
if (block)
delay_flag &= (~O_NDELAY);
else
delay_flag |= O_NDELAY;
fcntl(s->sock_fd, F_SETFL, delay_flag);
#endif /* !PYOS_OS2 */
#else /* MS_WINDOWS */
block = !block;
ioctlsocket(s->sock_fd, FIONBIO, (u_long*)&block);
#endif /* MS_WINDOWS */
#endif /* __BEOS__ */
#endif /* RISCOS */
Py_END_ALLOW_THREADS
/* Since these don't return anything */
return 1;
}
/* For access to the select module to poll the socket for timeout
* functionality. If reading is: 1 poll as read, 0, poll as write.
* Return value: -1 if error, 0 if not ready, >= 1 if ready.
*/
static int
internal_select(PySocketSockObject *s, int reading)
{
fd_set fds;
struct timeval tv;
int count;
/* Construct the arguments to select */
tv.tv_sec = (int)s->sock_timeout;
tv.tv_usec = (int)(s->sock_timeout/1e6);
FD_ZERO(&fds);
FD_SET(s->sock_fd, &fds);
/* See if the socket is ready */
if (reading)
count = select(s->sock_fd, &fds, NULL, NULL, &tv);
else
count = select(s->sock_fd, NULL, &fds, NULL, &tv);
/* Check for errors */
if (count < 0) {
s->errorhandler();
return -1;
}
/* Set the error if the timeout has elapsed, i.e, we were not
* polled.
*/
if (count == 0)
timeout_err();
return count;
}
/* Initialize a new socket object. */
static void
......@@ -434,6 +537,9 @@ init_sockobject(PySocketSockObject *s,
s->sock_family = family;
s->sock_type = type;
s->sock_proto = proto;
s->sock_blocking = 1; /* Start in blocking mode */
s->sock_timeout = -1.0; /* Start without timeout */
s->errorhandler = &PySocket_Err;
#ifdef RISCOS
if(taskwindow) {
......@@ -691,7 +797,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
struct sockaddr_un* addr;
char *path;
int len;
addr = (struct sockaddr_un* )&(s->sock_addr).un;
addr = (struct sockaddr_un*)&(s->sock_addr).un;
if (!PyArg_Parse(args, "t#", &path, &len))
return 0;
if (len > sizeof addr->sun_path) {
......@@ -861,9 +967,47 @@ PySocketSock_accept(PySocketSockObject *s)
if (!getsockaddrlen(s, &addrlen))
return NULL;
memset(addrbuf, 0, addrlen);
errno = 0; /* Reset indicator for use with timeout behavior */
Py_BEGIN_ALLOW_THREADS
newfd = accept(s->sock_fd, (struct sockaddr *) addrbuf, &addrlen);
Py_END_ALLOW_THREADS
if (s->sock_timeout >= 0.0) {
#ifdef MS_WINDOWS
if (newfd == INVALID_SOCKET)
if (!s->sock_blocking)
return s->errorhandler();
/* Check if we have a true failure
for a blocking socket */
if (errno != WSAEWOULDBLOCK)
return s->errorhandler();
#else
if (newfd < 0) {
if (!s->sock_blocking)
return s->errorhandler();
/* Check if we have a true failure
for a blocking socket */
if (errno != EAGAIN && errno != EWOULDBLOCK)
return s->errorhandler();
}
#endif
/* try waiting the timeout period */
if (internal_select(s, 0) <= 0)
return NULL;
Py_BEGIN_ALLOW_THREADS
newfd = accept(s->sock_fd,
(struct sockaddr *)addrbuf,
&addrlen);
Py_END_ALLOW_THREADS
}
/* At this point, we really have an error, whether using timeout
* behavior or regular socket behavior
*/
#ifdef MS_WINDOWS
if (newfd == INVALID_SOCKET)
#else
......@@ -877,18 +1021,19 @@ PySocketSock_accept(PySocketSockObject *s)
s->sock_family,
s->sock_type,
s->sock_proto);
if (sock == NULL) {
SOCKETCLOSE(newfd);
goto finally;
}
addr = makesockaddr(s->sock_fd, (struct sockaddr *)addrbuf,
addrlen);
addrlen);
if (addr == NULL)
goto finally;
res = Py_BuildValue("OO", sock, addr);
finally:
finally:
Py_XDECREF(sock);
Py_XDECREF(addr);
return res;
......@@ -901,47 +1046,24 @@ Wait for an incoming connection. Return a new socket representing the\n\
connection, and the address of the client. For IP sockets, the address\n\
info is a pair (hostaddr, port).";
/* s.setblocking(1 | 0) method */
static PyObject *
PySocketSock_setblocking(PySocketSockObject *s, PyObject *arg)
{
int block;
#ifndef RISCOS
#ifndef MS_WINDOWS
int delay_flag;
#endif
#endif
block = PyInt_AsLong(arg);
if (block == -1 && PyErr_Occurred())
return NULL;
Py_BEGIN_ALLOW_THREADS
#ifdef __BEOS__
block = !block;
setsockopt( s->sock_fd, SOL_SOCKET, SO_NONBLOCK,
(void *)(&block), sizeof( int ) );
#else
#ifndef RISCOS
#ifndef MS_WINDOWS
#if defined(PYOS_OS2) && !defined(PYCC_GCC)
block = !block;
ioctl(s->sock_fd, FIONBIO, (caddr_t)&block, sizeof(block));
#else /* !PYOS_OS2 */
delay_flag = fcntl (s->sock_fd, F_GETFL, 0);
if (block)
delay_flag &= (~O_NDELAY);
else
delay_flag |= O_NDELAY;
fcntl (s->sock_fd, F_SETFL, delay_flag);
#endif /* !PYOS_OS2 */
#else /* MS_WINDOWS */
block = !block;
ioctlsocket(s->sock_fd, FIONBIO, (u_long*)&block);
#endif /* MS_WINDOWS */
#endif /* __BEOS__ */
#endif /* RISCOS */
Py_END_ALLOW_THREADS
s->sock_blocking = block;
/* If we're not using timeouts, actually set the blocking to give
* old python behavior.
*/
if (s->sock_timeout < 0.0)
internal_setblocking(s, block);
Py_INCREF(Py_None);
return Py_None;
......@@ -953,6 +1075,81 @@ static char setblocking_doc[] =
Set the socket to blocking (flag is true) or non-blocking (false).\n\
This uses the FIONBIO ioctl with the O_NDELAY flag.";
/* s.settimeout (float | int | long) method.
* Causes an exception to be raised when the integer number of seconds
* has elapsed when performing a blocking socket operation.
*/
static PyObject *
PySocketSock_settimeout(PySocketSockObject *s, PyObject *arg)
{
double value;
if (arg == Py_None)
value = -1.0;
else {
value = PyFloat_AsDouble(arg);
if (value < 0.0) {
if (!PyErr_Occurred())
PyErr_SetString(PyExc_ValueError,
"Invalid timeout value");
return NULL;
}
}
s->sock_timeout = value;
/* The semantics of setting socket timeouts are:
* If you settimeout(!=None):
* The actual socket gets put in non-blocking mode and the select
* is used to control timeouts.
* Else if you settimeout(None) [then value is -1.0]:
* The old behavior is used AND automatically, the socket is set
* to blocking mode. That means that someone who was doing
* non-blocking stuff before, sets a timeout, and then unsets
* one, will have to call setblocking(0) again if he wants
* non-blocking stuff. This makes sense because timeout stuff is
* blocking by nature.
*/
if (value < 0.0)
internal_setblocking(s, 0);
else
internal_setblocking(s, 1);
s->sock_blocking = 1; /* Always negate setblocking() */
Py_INCREF(Py_None);
return Py_None;
}
static char settimeout_doc[] =
"settimeout(seconds)\n\
\n\
Set a timeout on blocking socket operations. 'seconds' can be a floating,\n\
integer, or long number of seconds, or the None value. Socket operations\n\
will raise an exception if the timeout period has elapsed before the\n\
operation has completed. Setting a timeout of None disables timeouts\n\
on socket operations.";
/* s.gettimeout () method.
* Returns the timeout associated with a socket.
*/
static PyObject *
PySocketSock_gettimeout(PySocketSockObject *s)
{
if (s->sock_timeout < 0.0) {
Py_INCREF(Py_None);
return Py_None;
}
else
return PyFloat_FromDouble(s->sock_timeout);
}
static char gettimeout_doc[] =
"gettimeout()\n\
\n\
Returns the timeout in floating seconds associated with socket \n\
operations. A timeout of None indicates that timeouts on socket \n\
operations are disabled.";
#ifdef RISCOS
/* s.sleeptaskw(1 | 0) method */
......@@ -960,16 +1157,16 @@ This uses the FIONBIO ioctl with the O_NDELAY flag.";
static PyObject *
PySocketSock_sleeptaskw(PySocketSockObject *s,PyObject *args)
{
int block;
int delay_flag;
if (!PyArg_Parse(args, "i", &block))
return NULL;
Py_BEGIN_ALLOW_THREADS
socketioctl(s->sock_fd, 0x80046679, (u_long*)&block);
Py_END_ALLOW_THREADS
int block;
int delay_flag;
if (!PyArg_Parse(args, "i", &block))
return NULL;
Py_BEGIN_ALLOW_THREADS
socketioctl(s->sock_fd, 0x80046679, (u_long*)&block);
Py_END_ALLOW_THREADS
Py_INCREF(Py_None);
return Py_None;
Py_INCREF(Py_None);
return Py_None;
}
static char sleeptaskw_doc[] =
"sleeptaskw(flag)\n\
......@@ -1142,11 +1339,50 @@ PySocketSock_connect(PySocketSockObject *s, PyObject *addro)
if (!getsockaddrarg(s, addro, &addr, &addrlen))
return NULL;
errno = 0; /* Reset the err indicator for use with timeouts */
Py_BEGIN_ALLOW_THREADS
res = connect(s->sock_fd, addr, addrlen);
Py_END_ALLOW_THREADS
if (s->sock_timeout >= 0.0) {
if (res < 0) {
/* Return if we're already connected */
#ifdef MS_WINDOWS
if (errno == WSAEINVAL || errno == WSAEISCONN)
#else
if (errno == EISCONN)
#endif
goto connected;
/* Check if we have an error */
if (!s->sock_blocking)
return s->errorhandler ();
/* Check if we have a true failure for a blocking socket */
#ifdef MS_WINDOWS
if (errno != WSAEWOULDBLOCK)
#else
if (errno != EINPROGRESS && errno != EALREADY &&
errno != EWOULDBLOCK)
#endif
return s->errorhandler();
}
/* Check if we're ready for the connect via select */
if (internal_select(s, 1) <= 0)
return NULL;
/* Complete the connection now */
Py_BEGIN_ALLOW_THREADS
res = connect(s->sock_fd, addr, addrlen);
Py_END_ALLOW_THREADS
}
if (res < 0)
return s->errorhandler();
connected:
Py_INCREF(Py_None);
return Py_None;
}
......@@ -1169,9 +1405,46 @@ PySocketSock_connect_ex(PySocketSockObject *s, PyObject *addro)
if (!getsockaddrarg(s, addro, &addr, &addrlen))
return NULL;
errno = 0; /* Reset the err indicator for use with timeouts */
Py_BEGIN_ALLOW_THREADS
res = connect(s->sock_fd, addr, addrlen);
Py_END_ALLOW_THREADS
if (s->sock_timeout >= 0.0) {
if (res < 0) {
/* Return if we're already connected */
#ifdef MS_WINDOWS
if (errno == WSAEINVAL || errno == WSAEISCONN)
#else
if (errno == EISCONN)
#endif
goto conex_finally;
/* Check if we have an error */
if (!s->sock_blocking)
goto conex_finally;
/* Check if we have a true failure for a blocking socket */
#ifdef MS_WINDOWS
if (errno != WSAEWOULDBLOCK)
#else
if (errno != EINPROGRESS && errno != EALREADY &&
errno != EWOULDBLOCK)
#endif
goto conex_finally;
}
/* Check if we're ready for the connect via select */
if (internal_select(s, 1) <= 0)
return NULL;
/* Complete the connection now */
Py_BEGIN_ALLOW_THREADS
res = connect(s->sock_fd, addr, addrlen);
Py_END_ALLOW_THREADS
}
if (res != 0) {
#ifdef MS_WINDOWS
res = WSAGetLastError();
......@@ -1179,6 +1452,8 @@ PySocketSock_connect_ex(PySocketSockObject *s, PyObject *addro)
res = errno;
#endif
}
conex_finally:
return PyInt_FromLong((long) res);
}
......@@ -1360,7 +1635,7 @@ PySocketSock_makefile(PySocketSockObject *s, PyObject *args)
}
#ifdef USE_GUSI2
/* Workaround for bug in Metrowerks MSL vs. GUSI I/O library */
if (strchr(mode, 'b') != NULL )
if (strchr(mode, 'b') != NULL)
bufsize = 0;
#endif
f = PyFile_FromFile(fp, "<socket>", mode, fclose);
......@@ -1385,19 +1660,31 @@ PySocketSock_recv(PySocketSockObject *s, PyObject *args)
{
int len, n, flags = 0;
PyObject *buf;
if (!PyArg_ParseTuple(args, "i|i:recv", &len, &flags))
return NULL;
if (len < 0) {
if (len < 0) {
PyErr_SetString(PyExc_ValueError,
"negative buffersize in connect");
return NULL;
}
buf = PyString_FromStringAndSize((char *) 0, len);
if (buf == NULL)
return NULL;
if (s->sock_timeout >= 0.0) {
if (s->sock_blocking) {
if (internal_select(s, 0) <= 0)
return NULL;
}
}
Py_BEGIN_ALLOW_THREADS
n = recv(s->sock_fd, PyString_AS_STRING(buf), len, flags);
Py_END_ALLOW_THREADS
if (n < 0) {
Py_DECREF(buf);
return s->errorhandler();
......@@ -1425,16 +1712,25 @@ PySocketSock_recvfrom(PySocketSockObject *s, PyObject *args)
PyObject *buf = NULL;
PyObject *addr = NULL;
PyObject *ret = NULL;
int len, n, flags = 0;
socklen_t addrlen;
if (!PyArg_ParseTuple(args, "i|i:recvfrom", &len, &flags))
return NULL;
if (!getsockaddrlen(s, &addrlen))
return NULL;
buf = PyString_FromStringAndSize((char *) 0, len);
if (buf == NULL)
return NULL;
if (s->sock_timeout >= 0.0) {
if (s->sock_blocking) {
if (internal_select(s, 0) <= 0)
return NULL;
}
}
Py_BEGIN_ALLOW_THREADS
memset(addrbuf, 0, addrlen);
n = recvfrom(s->sock_fd, PyString_AS_STRING(buf), len, flags,
......@@ -1449,18 +1745,22 @@ PySocketSock_recvfrom(PySocketSockObject *s, PyObject *args)
#endif
);
Py_END_ALLOW_THREADS
if (n < 0) {
Py_DECREF(buf);
return s->errorhandler();
}
if (n != len && _PyString_Resize(&buf, n) < 0)
return NULL;
if (!(addr = makesockaddr(s->sock_fd, (struct sockaddr *)addrbuf, addrlen)))
if (!(addr = makesockaddr(s->sock_fd, (struct sockaddr *)addrbuf,
addrlen)))
goto finally;
ret = Py_BuildValue("OO", buf, addr);
finally:
finally:
Py_XDECREF(addr);
Py_XDECREF(buf);
return ret;
......@@ -1471,7 +1771,6 @@ static char recvfrom_doc[] =
\n\
Like recv(buffersize, flags) but also return the sender's address info.";
/* s.send(data [,flags]) method */
static PyObject *
......@@ -1479,11 +1778,21 @@ PySocketSock_send(PySocketSockObject *s, PyObject *args)
{
char *buf;
int len, n, flags = 0;
if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags))
return NULL;
if (s->sock_timeout >= 0.0) {
if (s->sock_blocking) {
if (internal_select(s, 1) <= 0)
return NULL;
}
}
Py_BEGIN_ALLOW_THREADS
n = send(s->sock_fd, buf, len, flags);
Py_END_ALLOW_THREADS
if (n < 0)
return s->errorhandler();
return PyInt_FromLong((long)n);
......@@ -1504,8 +1813,17 @@ PySocketSock_sendall(PySocketSockObject *s, PyObject *args)
{
char *buf;
int len, n, flags = 0;
if (!PyArg_ParseTuple(args, "s#|i:sendall", &buf, &len, &flags))
return NULL;
if (s->sock_timeout >= 0.0) {
if (s->sock_blocking) {
if (internal_select(s, 1) < 0)
return NULL;
}
}
Py_BEGIN_ALLOW_THREADS
do {
n = send(s->sock_fd, buf, len, flags);
......@@ -1515,8 +1833,10 @@ PySocketSock_sendall(PySocketSockObject *s, PyObject *args)
len -= n;
} while (len > 0);
Py_END_ALLOW_THREADS
if (n < 0)
return s->errorhandler();
Py_INCREF(Py_None);
return Py_None;
}
......@@ -1539,6 +1859,7 @@ PySocketSock_sendto(PySocketSockObject *s, PyObject *args)
char *buf;
struct sockaddr *addr;
int addrlen, len, n, flags;
flags = 0;
if (!PyArg_ParseTuple(args, "s#O:sendto", &buf, &len, &addro)) {
PyErr_Clear();
......@@ -1546,11 +1867,21 @@ PySocketSock_sendto(PySocketSockObject *s, PyObject *args)
&buf, &len, &flags, &addro))
return NULL;
}
if (!getsockaddrarg(s, addro, &addr, &addrlen))
return NULL;
if (s->sock_timeout >= 0.0) {
if (s->sock_blocking) {
if (internal_select(s, 1) <= 0)
return NULL;
}
}
Py_BEGIN_ALLOW_THREADS
n = sendto(s->sock_fd, buf, len, flags, addr, addrlen);
Py_END_ALLOW_THREADS
if (n < 0)
return s->errorhandler();
return PyInt_FromLong((long)n);
......@@ -1635,6 +1966,10 @@ static PyMethodDef PySocketSock_methods[] = {
sendto_doc},
{"setblocking", (PyCFunction)PySocketSock_setblocking, METH_O,
setblocking_doc},
{"settimeout", (PyCFunction)PySocketSock_settimeout, METH_O,
settimeout_doc},
{"gettimeout", (PyCFunction)PySocketSock_gettimeout, METH_NOARGS,
gettimeout_doc},
{"setsockopt", (PyCFunction)PySocketSock_setsockopt, METH_VARARGS,
setsockopt_doc},
{"shutdown", (PyCFunction)PySocketSock_shutdown, METH_O,
......@@ -1692,6 +2027,7 @@ PySocketSock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
new = type->tp_alloc(type, 0);
if (new != NULL) {
((PySocketSockObject *)new)->sock_fd = -1;
((PySocketSockObject *)new)->sock_timeout = -1.0;
((PySocketSockObject *)new)->errorhandler = &PySocket_Err;
}
return new;
......@@ -1713,9 +2049,11 @@ PySocketSock_init(PyObject *self, PyObject *args, PyObject *kwds)
"|iii:socket", keywords,
&family, &type, &proto))
return -1;
Py_BEGIN_ALLOW_THREADS
fd = socket(family, type, proto);
Py_END_ALLOW_THREADS
#ifdef MS_WINDOWS
if (fd == INVALID_SOCKET)
#else
......@@ -1731,7 +2069,9 @@ PySocketSock_init(PyObject *self, PyObject *args, PyObject *kwds)
#ifdef SIGPIPE
(void) signal(SIGPIPE, SIG_IGN);
#endif
return 0;
}
......@@ -1885,6 +2225,7 @@ gethost_common(struct hostent *h, struct sockaddr *addr, int alen, int af)
#endif
return NULL;
}
if (h->h_addrtype != af) {
#ifdef HAVE_STRERROR
/* Let's get real error message to return */
......@@ -1895,35 +2236,47 @@ gethost_common(struct hostent *h, struct sockaddr *addr, int alen, int af)
#endif
return NULL;
}
switch (af) {
case AF_INET:
if (alen < sizeof(struct sockaddr_in))
return NULL;
break;
#ifdef ENABLE_IPV6
case AF_INET6:
if (alen < sizeof(struct sockaddr_in6))
return NULL;
break;
#endif
}
if ((name_list = PyList_New(0)) == NULL)
goto err;
if ((addr_list = PyList_New(0)) == NULL)
goto err;
for (pch = h->h_aliases; *pch != NULL; pch++) {
int status;
tmp = PyString_FromString(*pch);
if (tmp == NULL)
goto err;
status = PyList_Append(name_list, tmp);
Py_DECREF(tmp);
if (status)
goto err;
}
for (pch = h->h_addr_list; *pch != NULL; pch++) {
int status;
switch (af) {
case AF_INET:
{
struct sockaddr_in sin;
......@@ -1934,10 +2287,12 @@ gethost_common(struct hostent *h, struct sockaddr *addr, int alen, int af)
#endif
memcpy(&sin.sin_addr, *pch, sizeof(sin.sin_addr));
tmp = makeipaddr((struct sockaddr *)&sin, sizeof(sin));
if (pch == h->h_addr_list && alen >= sizeof(sin))
memcpy((char *) addr, &sin, sizeof(sin));
break;
}
#ifdef ENABLE_IPV6
case AF_INET6:
{
......@@ -1950,24 +2305,31 @@ gethost_common(struct hostent *h, struct sockaddr *addr, int alen, int af)
memcpy(&sin6.sin6_addr, *pch, sizeof(sin6.sin6_addr));
tmp = makeipaddr((struct sockaddr *)&sin6,
sizeof(sin6));
if (pch == h->h_addr_list && alen >= sizeof(sin6))
memcpy((char *) addr, &sin6, sizeof(sin6));
break;
}
#endif
default: /* can't happen */
PyErr_SetString(PySocket_Error,
"unsupported address family");
return NULL;
}
if (tmp == NULL)
goto err;
status = PyList_Append(addr_list, tmp);
Py_DECREF(tmp);
if (status)
goto err;
}
rtn_tuple = Py_BuildValue("sOO", h->h_name, name_list, addr_list);
err:
Py_XDECREF(name_list);
Py_XDECREF(addr_list);
......@@ -2022,10 +2384,14 @@ PySocket_gethostbyname_ex(PyObject *self, PyObject *args)
h = gethostbyname(name);
#endif /* HAVE_GETHOSTBYNAME_R */
Py_END_ALLOW_THREADS
/* Some C libraries would require addr.__ss_family instead of addr.ss_family.
Therefore, we cast the sockaddr_storage into sockaddr to access sa_family. */
/* Some C libraries would require addr.__ss_family instead of
* addr.ss_family.
* Therefore, we cast the sockaddr_storage into sockaddr to
* access sa_family.
*/
sa = (struct sockaddr*)&addr;
ret = gethost_common(h, (struct sockaddr *)&addr, sizeof(addr), sa->sa_family);
ret = gethost_common(h, (struct sockaddr *)&addr, sizeof(addr),
sa->sa_family);
#ifdef USE_GETHOSTBYNAME_LOCK
PyThread_release_lock(gethostbyname_lock);
#endif
......@@ -2170,7 +2536,7 @@ PySocket_getprotobyname(PyObject *self, PyObject *args)
struct protoent *sp;
#ifdef __BEOS__
/* Not available in BeOS yet. - [cjh] */
PyErr_SetString( PySocket_Error, "getprotobyname not supported" );
PyErr_SetString(PySocket_Error, "getprotobyname not supported");
return NULL;
#else
if (!PyArg_ParseTuple(args, "s:getprotobyname", &name))
......
......@@ -83,6 +83,9 @@ typedef struct {
PyObject *(*errorhandler)(void); /* Error handler; checks
errno, returns NULL and
sets a Python exception */
int sock_blocking; /* Flag indicated whether the
socket is in blocking mode */
double sock_timeout; /* Operation timeout value */
} PySocketSockObject;
/* --- C API ----------------------------------------------------*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment