Issue #7523: Add SOCK_CLOEXEC and SOCK_NONBLOCK to the socket module,
where supported by the system. Patch by Nikita Vetoshkin.
This commit is contained in:
parent
f4061dac60
commit
b1c5496738
|
@ -157,6 +157,21 @@ The module :mod:`socket` exports the following constants and functions:
|
||||||
:func:`socket`. (Only :const:`SOCK_STREAM` and :const:`SOCK_DGRAM` appear to be
|
:func:`socket`. (Only :const:`SOCK_STREAM` and :const:`SOCK_DGRAM` appear to be
|
||||||
generally useful.)
|
generally useful.)
|
||||||
|
|
||||||
|
.. data:: SOCK_CLOEXEC
|
||||||
|
SOCK_NONBLOCK
|
||||||
|
|
||||||
|
These two constants, if defined, can be combined with the socket types and
|
||||||
|
allow you to set some flags atomically (thus avoiding possible race
|
||||||
|
conditions and the need for separate calls).
|
||||||
|
|
||||||
|
.. seealso::
|
||||||
|
|
||||||
|
`Secure File Descriptor Handling <http://udrepper.livejournal.com/20407.html>`_
|
||||||
|
for a more thorough explanation.
|
||||||
|
|
||||||
|
Availability: Linux >= 2.6.27.
|
||||||
|
|
||||||
|
.. versionadded:: 3.2
|
||||||
|
|
||||||
.. data:: SO_*
|
.. data:: SO_*
|
||||||
SOMAXCONN
|
SOMAXCONN
|
||||||
|
|
|
@ -692,7 +692,8 @@ class BaseTestAPI(unittest.TestCase):
|
||||||
s = asyncore.dispatcher()
|
s = asyncore.dispatcher()
|
||||||
s.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
s.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
self.assertEqual(s.socket.family, socket.AF_INET)
|
self.assertEqual(s.socket.family, socket.AF_INET)
|
||||||
self.assertEqual(s.socket.type, socket.SOCK_STREAM)
|
SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', 0)
|
||||||
|
self.assertEqual(s.socket.type, socket.SOCK_STREAM | SOCK_NONBLOCK)
|
||||||
|
|
||||||
def test_bind(self):
|
def test_bind(self):
|
||||||
s1 = asyncore.dispatcher()
|
s1 = asyncore.dispatcher()
|
||||||
|
|
|
@ -17,6 +17,10 @@ import contextlib
|
||||||
from weakref import proxy
|
from weakref import proxy
|
||||||
import signal
|
import signal
|
||||||
import math
|
import math
|
||||||
|
try:
|
||||||
|
import fcntl
|
||||||
|
except ImportError:
|
||||||
|
fcntl = False
|
||||||
|
|
||||||
def try_address(host, port=0, family=socket.AF_INET):
|
def try_address(host, port=0, family=socket.AF_INET):
|
||||||
"""Try to bind a socket on the given host:port and return True
|
"""Try to bind a socket on the given host:port and return True
|
||||||
|
@ -902,6 +906,26 @@ class NonBlockingTCPTests(ThreadedTCPSocketTest):
|
||||||
def _testSetBlocking(self):
|
def _testSetBlocking(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if hasattr(socket, "SOCK_NONBLOCK"):
|
||||||
|
def testInitNonBlocking(self):
|
||||||
|
# reinit server socket
|
||||||
|
self.serv.close()
|
||||||
|
self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM |
|
||||||
|
socket.SOCK_NONBLOCK)
|
||||||
|
self.port = support.bind_port(self.serv)
|
||||||
|
self.serv.listen(1)
|
||||||
|
# actual testing
|
||||||
|
start = time.time()
|
||||||
|
try:
|
||||||
|
self.serv.accept()
|
||||||
|
except socket.error:
|
||||||
|
pass
|
||||||
|
end = time.time()
|
||||||
|
self.assertTrue((end - start) < 1.0, "Error creating with non-blocking mode.")
|
||||||
|
|
||||||
|
def _testInitNonBlocking(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def testAccept(self):
|
def testAccept(self):
|
||||||
# Testing non-blocking accept
|
# Testing non-blocking accept
|
||||||
self.serv.setblocking(0)
|
self.serv.setblocking(0)
|
||||||
|
@ -1801,6 +1825,56 @@ class ContextManagersTest(ThreadedTCPSocketTest):
|
||||||
self.assertTrue(sock._closed)
|
self.assertTrue(sock._closed)
|
||||||
self.assertRaises(socket.error, sock.sendall, b'foo')
|
self.assertRaises(socket.error, sock.sendall, b'foo')
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(socket, "SOCK_CLOEXEC"),
|
||||||
|
"SOCK_CLOEXEC not defined")
|
||||||
|
@unittest.skipUnless(fcntl, "module fcntl not available")
|
||||||
|
class CloexecConstantTest(unittest.TestCase):
|
||||||
|
def test_SOCK_CLOEXEC(self):
|
||||||
|
s = socket.socket(socket.AF_INET,
|
||||||
|
socket.SOCK_STREAM | socket.SOCK_CLOEXEC)
|
||||||
|
self.assertTrue(s.type & socket.SOCK_CLOEXEC)
|
||||||
|
self.assertTrue(fcntl.fcntl(s, fcntl.F_GETFD) & fcntl.FD_CLOEXEC)
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(hasattr(socket, "SOCK_NONBLOCK"),
|
||||||
|
"SOCK_NONBLOCK not defined")
|
||||||
|
class NonblockConstantTest(unittest.TestCase):
|
||||||
|
def checkNonblock(self, s, nonblock=True, timeout=0.0):
|
||||||
|
if nonblock:
|
||||||
|
self.assertTrue(s.type & socket.SOCK_NONBLOCK)
|
||||||
|
self.assertEqual(s.gettimeout(), timeout)
|
||||||
|
else:
|
||||||
|
self.assertFalse(s.type & socket.SOCK_NONBLOCK)
|
||||||
|
self.assertEqual(s.gettimeout(), None)
|
||||||
|
|
||||||
|
def test_SOCK_NONBLOCK(self):
|
||||||
|
# a lot of it seems silly and redundant, but I wanted to test that
|
||||||
|
# changing back and forth worked ok
|
||||||
|
s = socket.socket(socket.AF_INET,
|
||||||
|
socket.SOCK_STREAM | socket.SOCK_NONBLOCK)
|
||||||
|
self.checkNonblock(s)
|
||||||
|
s.setblocking(1)
|
||||||
|
self.checkNonblock(s, False)
|
||||||
|
s.setblocking(0)
|
||||||
|
self.checkNonblock(s)
|
||||||
|
s.settimeout(None)
|
||||||
|
self.checkNonblock(s, False)
|
||||||
|
s.settimeout(2.0)
|
||||||
|
self.checkNonblock(s, timeout=2.0)
|
||||||
|
s.setblocking(1)
|
||||||
|
self.checkNonblock(s, False)
|
||||||
|
# defaulttimeout
|
||||||
|
t = socket.getdefaulttimeout()
|
||||||
|
socket.setdefaulttimeout(0.0)
|
||||||
|
self.checkNonblock(socket.socket())
|
||||||
|
socket.setdefaulttimeout(None)
|
||||||
|
self.checkNonblock(socket.socket(), False)
|
||||||
|
socket.setdefaulttimeout(2.0)
|
||||||
|
self.checkNonblock(socket.socket(), timeout=2.0)
|
||||||
|
socket.setdefaulttimeout(None)
|
||||||
|
self.checkNonblock(socket.socket(), False)
|
||||||
|
socket.setdefaulttimeout(t)
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest,
|
tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest,
|
||||||
|
@ -1820,6 +1894,8 @@ def test_main():
|
||||||
NetworkConnectionAttributesTest,
|
NetworkConnectionAttributesTest,
|
||||||
NetworkConnectionBehaviourTest,
|
NetworkConnectionBehaviourTest,
|
||||||
ContextManagersTest,
|
ContextManagersTest,
|
||||||
|
CloexecConstantTest,
|
||||||
|
NonblockConstantTest
|
||||||
])
|
])
|
||||||
if hasattr(socket, "socketpair"):
|
if hasattr(socket, "socketpair"):
|
||||||
tests.append(BasicSocketPairTest)
|
tests.append(BasicSocketPairTest)
|
||||||
|
|
|
@ -846,6 +846,7 @@ Alexandre Vassalotti
|
||||||
Frank Vercruesse
|
Frank Vercruesse
|
||||||
Mike Verdone
|
Mike Verdone
|
||||||
Jaap Vermeulen
|
Jaap Vermeulen
|
||||||
|
Nikita Vetoshkin
|
||||||
Al Vezza
|
Al Vezza
|
||||||
Jacques A. Vidrine
|
Jacques A. Vidrine
|
||||||
John Viega
|
John Viega
|
||||||
|
|
|
@ -21,12 +21,14 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
- Issue #Issue10063: file:// scheme will stop accessing remote hosts via ftp
|
- Issue #7523: Add SOCK_CLOEXEC and SOCK_NONBLOCK to the socket module,
|
||||||
|
where supported by the system. Patch by Nikita Vetoshkin.
|
||||||
|
|
||||||
|
- Issue #10063: file:// scheme will stop accessing remote hosts via ftp
|
||||||
protocol. file:// urls had fallback to access remote hosts via ftp. This was
|
protocol. file:// urls had fallback to access remote hosts via ftp. This was
|
||||||
not correct, change is made to raise a URLError when a remote host is tried
|
not correct, change is made to raise a URLError when a remote host is tried
|
||||||
to access via file:// scheme.
|
to access via file:// scheme.
|
||||||
|
|
||||||
|
|
||||||
- Issue #1710703: Write structures for an empty ZIP archive when a ZipFile is
|
- Issue #1710703: Write structures for an empty ZIP archive when a ZipFile is
|
||||||
created in modes 'a' or 'w' and then closed without adding any files. Raise
|
created in modes 'a' or 'w' and then closed without adding any files. Raise
|
||||||
BadZipfile (rather than IOError) when opening small non-ZIP files.
|
BadZipfile (rather than IOError) when opening small non-ZIP files.
|
||||||
|
|
|
@ -615,6 +615,12 @@ internal_setblocking(PySocketSockObject *s, int block)
|
||||||
#ifndef MS_WINDOWS
|
#ifndef MS_WINDOWS
|
||||||
int delay_flag;
|
int delay_flag;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef SOCK_NONBLOCK
|
||||||
|
if (block)
|
||||||
|
s->sock_type &= (~SOCK_NONBLOCK);
|
||||||
|
else
|
||||||
|
s->sock_type |= SOCK_NONBLOCK;
|
||||||
|
#endif
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
#ifndef MS_WINDOWS
|
#ifndef MS_WINDOWS
|
||||||
|
@ -764,12 +770,18 @@ init_sockobject(PySocketSockObject *s,
|
||||||
s->sock_family = family;
|
s->sock_family = family;
|
||||||
s->sock_type = type;
|
s->sock_type = type;
|
||||||
s->sock_proto = proto;
|
s->sock_proto = proto;
|
||||||
s->sock_timeout = defaulttimeout;
|
|
||||||
|
|
||||||
s->errorhandler = &set_error;
|
s->errorhandler = &set_error;
|
||||||
|
#ifdef SOCK_NONBLOCK
|
||||||
if (defaulttimeout >= 0.0)
|
if (type & SOCK_NONBLOCK)
|
||||||
internal_setblocking(s, 0);
|
s->sock_timeout = 0.0;
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
s->sock_timeout = defaulttimeout;
|
||||||
|
if (defaulttimeout >= 0.0)
|
||||||
|
internal_setblocking(s, 0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1645,7 +1657,9 @@ sock_accept(PySocketSockObject *s)
|
||||||
PyObject *addr = NULL;
|
PyObject *addr = NULL;
|
||||||
PyObject *res = NULL;
|
PyObject *res = NULL;
|
||||||
int timeout;
|
int timeout;
|
||||||
|
#ifdef HAVE_ACCEPT4
|
||||||
|
int flags = 0;
|
||||||
|
#endif
|
||||||
if (!getsockaddrlen(s, &addrlen))
|
if (!getsockaddrlen(s, &addrlen))
|
||||||
return NULL;
|
return NULL;
|
||||||
memset(&addrbuf, 0, addrlen);
|
memset(&addrbuf, 0, addrlen);
|
||||||
|
@ -1656,8 +1670,15 @@ sock_accept(PySocketSockObject *s)
|
||||||
BEGIN_SELECT_LOOP(s)
|
BEGIN_SELECT_LOOP(s)
|
||||||
Py_BEGIN_ALLOW_THREADS
|
Py_BEGIN_ALLOW_THREADS
|
||||||
timeout = internal_select_ex(s, 0, interval);
|
timeout = internal_select_ex(s, 0, interval);
|
||||||
if (!timeout)
|
if (!timeout) {
|
||||||
|
#ifdef HAVE_ACCEPT4
|
||||||
|
/* inherit socket flags and use accept4 call */
|
||||||
|
flags = s->sock_type & (SOCK_CLOEXEC | SOCK_NONBLOCK);
|
||||||
|
newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen, flags);
|
||||||
|
#else
|
||||||
newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
|
newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
|
||||||
|
#endif /* HAVE_ACCEPT4 */
|
||||||
|
}
|
||||||
Py_END_ALLOW_THREADS
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
if (timeout == 1) {
|
if (timeout == 1) {
|
||||||
|
@ -4599,6 +4620,12 @@ PyInit__socket(void)
|
||||||
#if defined(SOCK_RDM)
|
#if defined(SOCK_RDM)
|
||||||
PyModule_AddIntConstant(m, "SOCK_RDM", SOCK_RDM);
|
PyModule_AddIntConstant(m, "SOCK_RDM", SOCK_RDM);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef SOCK_CLOEXEC
|
||||||
|
PyModule_AddIntConstant(m, "SOCK_CLOEXEC", SOCK_CLOEXEC);
|
||||||
|
#endif
|
||||||
|
#ifdef SOCK_NONBLOCK
|
||||||
|
PyModule_AddIntConstant(m, "SOCK_NONBLOCK", SOCK_NONBLOCK);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef SO_DEBUG
|
#ifdef SO_DEBUG
|
||||||
PyModule_AddIntConstant(m, "SO_DEBUG", SO_DEBUG);
|
PyModule_AddIntConstant(m, "SO_DEBUG", SO_DEBUG);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# From configure.in Revision: 85349 .
|
# From configure.in Revision: 85422 .
|
||||||
# Guess values for system-dependent variables and create Makefiles.
|
# Guess values for system-dependent variables and create Makefiles.
|
||||||
# Generated by GNU Autoconf 2.65 for python 3.2.
|
# Generated by GNU Autoconf 2.65 for python 3.2.
|
||||||
#
|
#
|
||||||
|
@ -9302,7 +9302,7 @@ fi
|
||||||
$as_echo "MACHDEP_OBJS" >&6; }
|
$as_echo "MACHDEP_OBJS" >&6; }
|
||||||
|
|
||||||
# checks for library functions
|
# checks for library functions
|
||||||
for ac_func in alarm setitimer getitimer bind_textdomain_codeset chown \
|
for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
|
||||||
clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
|
clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
|
||||||
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
|
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
|
||||||
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
|
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
|
||||||
|
|
|
@ -2550,7 +2550,7 @@ fi
|
||||||
AC_MSG_RESULT(MACHDEP_OBJS)
|
AC_MSG_RESULT(MACHDEP_OBJS)
|
||||||
|
|
||||||
# checks for library functions
|
# checks for library functions
|
||||||
AC_CHECK_FUNCS(alarm setitimer getitimer bind_textdomain_codeset chown \
|
AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
|
||||||
clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
|
clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
|
||||||
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
|
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
|
||||||
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
|
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
|
||||||
|
|
|
@ -40,6 +40,9 @@
|
||||||
the case on Motorola V4 (R40V4.2) */
|
the case on Motorola V4 (R40V4.2) */
|
||||||
#undef GETTIMEOFDAY_NO_TZ
|
#undef GETTIMEOFDAY_NO_TZ
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `accept4' function. */
|
||||||
|
#undef HAVE_ACCEPT4
|
||||||
|
|
||||||
/* Define to 1 if you have the `acosh' function. */
|
/* Define to 1 if you have the `acosh' function. */
|
||||||
#undef HAVE_ACOSH
|
#undef HAVE_ACOSH
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue