[Jython-checkins] jython: Adding support for SO_ACCEPTCONN, SO_ERROR, and SO_TYPE
alan.kennedy
jython-checkins at python.org
Thu Aug 23 01:31:52 CEST 2012
http://hg.python.org/jython/rev/a39725e023f2
changeset: 6850:a39725e023f2
user: Alan Kennedy <alan at xhaus.com>
date: Thu Aug 23 00:30:24 2012 +0100
summary:
Adding support for SO_ACCEPTCONN, SO_ERROR, and SO_TYPE
files:
Lib/socket.py | 154 +++++++++++++++++----------
Lib/test/test_socket.py | 49 +++++++-
2 files changed, 137 insertions(+), 66 deletions(-)
diff --git a/Lib/socket.py b/Lib/socket.py
--- a/Lib/socket.py
+++ b/Lib/socket.py
@@ -150,13 +150,15 @@
}
-def _map_exception(java_exception, circumstance=ALL):
+def _map_exception(sock_object, java_exception, circumstance=ALL):
mapped_exception = _exception_map.get((java_exception.__class__, circumstance))
if mapped_exception:
py_exception = mapped_exception(java_exception)
else:
py_exception = error(-1, 'Unmapped exception: %s' % java_exception)
setattr(py_exception, 'java_exception', java_exception)
+ if sock_object is not None:
+ setattr(sock_object, '_last_error', py_exception[0])
return _add_exception_attrs(py_exception)
_feature_support_map = {
@@ -243,16 +245,19 @@
IPPROTO_TCP = 6
IPPROTO_UDP = 17
-SO_BROADCAST = 1
-SO_KEEPALIVE = 2
-SO_LINGER = 4
-SO_OOBINLINE = 8
-SO_RCVBUF = 16
-SO_REUSEADDR = 32
-SO_SNDBUF = 64
-SO_TIMEOUT = 128
+SO_ACCEPTCONN = 1
+SO_BROADCAST = 2
+SO_ERROR = 4
+SO_KEEPALIVE = 8
+SO_LINGER = 16
+SO_OOBINLINE = 32
+SO_RCVBUF = 64
+SO_REUSEADDR = 128
+SO_SNDBUF = 256
+SO_TIMEOUT = 512
+SO_TYPE = 1024
-TCP_NODELAY = 256
+TCP_NODELAY = 2048
INADDR_ANY = "0.0.0.0"
INADDR_BROADCAST = "255.255.255.255"
@@ -263,18 +268,15 @@
# They are being added here so that code that refers to them
# will not break with an AttributeError
-SO_ACCEPTCONN = -1
-SO_DEBUG = -2
-SO_DONTROUTE = -4
-SO_ERROR = -8
-SO_EXCLUSIVEADDRUSE = -16
-SO_RCVLOWAT = -32
-SO_RCVTIMEO = -64
-SO_REUSEPORT = -128
-SO_SNDLOWAT = -256
-SO_SNDTIMEO = -512
-SO_TYPE = -1024
-SO_USELOOPBACK = -2048
+SO_DEBUG = -1
+SO_DONTROUTE = -1
+SO_EXCLUSIVEADDRUSE = -8
+SO_RCVLOWAT = -16
+SO_RCVTIMEO = -32
+SO_REUSEPORT = -64
+SO_SNDLOWAT = -128
+SO_SNDTIMEO = -256
+SO_USELOOPBACK = -512
__all__ = [
# Families
@@ -692,13 +694,13 @@
try:
return asPyString(java.net.InetAddress.getLocalHost().getHostName())
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(None, jlx)
def gethostbyname(name):
try:
return asPyString(java.net.InetAddress.getByName(name).getHostAddress())
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(None, jlx)
def gethostbyaddr(name):
names, addrs = _gethostbyaddr(name)
@@ -974,7 +976,7 @@
results.append((family, result_socktype, result_proto, canonname, sock_tuple))
return results
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(None, jlx)
def _getnameinfo_get_host(address, flags):
if not isinstance(address, basestring):
@@ -1057,7 +1059,7 @@
bytes.append(byte)
return "".join([chr(byte) for byte in bytes])
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(None, jlx)
def inet_ntop(family, packed_ip):
try:
@@ -1073,7 +1075,7 @@
ia = java.net.InetAddress.getByAddress(jByteArray)
return ia.getHostAddress()
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(None, jlx)
def inet_aton(ip_string):
return inet_pton(AF_INET, ip_string)
@@ -1083,9 +1085,9 @@
class _nonblocking_api_mixin:
- mode = MODE_BLOCKING
+ mode = MODE_BLOCKING
reference_count = 0
- close_lock = threading.Lock()
+ close_lock = threading.Lock()
def __init__(self):
self.timeout = _defaulttimeout
@@ -1122,40 +1124,52 @@
def setsockopt(self, level, optname, value):
try:
+ self._last_error = 0
if self.sock_impl:
self.sock_impl.setsockopt(level, optname, value)
else:
self.pending_options[ (level, optname) ] = value
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def getsockopt(self, level, optname):
+ # Handle "pseudo" options first
+ if level == SOL_SOCKET and optname == SO_TYPE:
+ return getattr(self, "type")
+ if level == SOL_SOCKET and optname == SO_ERROR:
+ return_value = self._last_error
+ self._last_error = 0
+ return return_value
+ # Now handle "real" options
try:
if self.sock_impl:
return self.sock_impl.getsockopt(level, optname)
else:
return self.pending_options.get( (level, optname), None)
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def shutdown(self, how):
assert how in (SHUT_RD, SHUT_WR, SHUT_RDWR)
if not self.sock_impl:
raise error(errno.ENOTCONN, "Transport endpoint is not connected")
try:
+ self._last_error = 0
self.sock_impl.shutdown(how)
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def close(self):
try:
+ self._last_error = 0
if self.sock_impl:
self.sock_impl.close()
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def getsockname(self):
try:
+ self._last_error = 0
if self.sock_impl is None:
# If the user has already bound an address, return that
if self.local_addr:
@@ -1165,15 +1179,16 @@
raise error(errno.EINVAL, "Invalid argument")
return self.sock_impl.getsockname()
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def getpeername(self):
try:
+ self._last_error = 0
if self.sock_impl is None:
raise error(errno.ENOTCONN, "Socket is not connected")
return self.sock_impl.getpeername()
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def _config(self):
assert self.mode in _permitted_modes
@@ -1198,16 +1213,23 @@
class _tcpsocket(_nonblocking_api_mixin):
- sock_impl = None
- istream = None
- ostream = None
- local_addr = None
- server = 0
+ sock_impl = None
+ istream = None
+ ostream = None
+ local_addr = None
+ server = 0
+ _last_error = 0
def __init__(self):
_nonblocking_api_mixin.__init__(self)
+ def getsockopt(self, level, optname):
+ if level == SOL_SOCKET and optname == SO_ACCEPTCONN:
+ return self.server
+ return _nonblocking_api_mixin.getsockopt(self, level, optname)
+
def bind(self, addr):
+ self._last_error = 0
assert not self.sock_impl
assert not self.local_addr
# Do the address format check
@@ -1217,22 +1239,25 @@
def listen(self, backlog):
"This signifies a server socket"
try:
+ self._last_error = 0
assert not self.sock_impl
self.server = 1
self.sock_impl = _server_socket_impl(_get_jsockaddr(self.local_addr, self.family, self.type, self.proto, AI_PASSIVE),
backlog, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ])
self._config()
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def accept(self):
"This signifies a server socket"
try:
+ self._last_error = 0
if not self.sock_impl:
self.listen()
assert self.server
new_sock = self.sock_impl.accept()
if not new_sock:
+ self._last_error = errno.EWOULDBLOCK
raise would_block_error()
cliconn = _tcpsocket()
cliconn.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ] = new_sock.jsocket.getReuseAddress()
@@ -1240,10 +1265,11 @@
cliconn._setup()
return cliconn, new_sock.getpeername()
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def _do_connect(self, addr):
try:
+ self._last_error = 0
assert not self.sock_impl
self.sock_impl = _client_socket_impl()
if self.local_addr: # Has the socket been bound to a local address?
@@ -1252,7 +1278,7 @@
self._config() # Configure timeouts, etc, now that the socket exists
self.sock_impl.connect(_get_jsockaddr(addr, self.family, self.type, self.proto, 0))
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def connect(self, addr):
"This signifies a client socket"
@@ -1277,6 +1303,7 @@
def recv(self, n):
try:
+ self._last_error = 0
if not self.sock_impl: raise error(errno.ENOTCONN, 'Socket is not connected')
if self.sock_impl.jchannel.isConnectionPending():
self.sock_impl.jchannel.finishConnect()
@@ -1286,33 +1313,37 @@
return ""
elif m <= 0:
if self.mode == MODE_NONBLOCKING:
+ self._last_error = errno.EWOULDBLOCK
raise would_block_error()
return ""
if m < n:
data = data[:m]
return data.tostring()
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def recvfrom(self, n):
return self.recv(n), None
def send(self, s):
try:
+ self._last_error = 0
if not self.sock_impl: raise error(errno.ENOTCONN, 'Socket is not connected')
if self.sock_impl.jchannel.isConnectionPending():
self.sock_impl.jchannel.finishConnect()
numwritten = self.sock_impl.write(s)
if numwritten == 0 and self.mode == MODE_NONBLOCKING:
+ self._last_error = errno.EWOULDBLOCK
raise would_block_error()
return numwritten
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
sendall = send
def close(self):
try:
+ self._last_error = 0
if self.istream:
self.istream.close()
if self.ostream:
@@ -1320,20 +1351,22 @@
if self.sock_impl:
self.sock_impl.close()
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
class _udpsocket(_nonblocking_api_mixin):
- sock_impl = None
- connected = False
- local_addr = None
+ sock_impl = None
+ connected = False
+ local_addr = None
+ _last_error = 0
def __init__(self):
_nonblocking_api_mixin.__init__(self)
def bind(self, addr):
try:
+ self._last_error = 0
assert not self.sock_impl
assert not self.local_addr
# Do the address format check
@@ -1343,10 +1376,11 @@
self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ])
self._config()
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def _do_connect(self, addr):
try:
+ self._last_error = 0
assert not self.connected, "Datagram Socket is already connected"
if not self.sock_impl:
self.sock_impl = _datagram_socket_impl()
@@ -1354,7 +1388,7 @@
self.sock_impl.connect(_get_jsockaddr(addr, self.family, self.type, self.proto, 0))
self.connected = True
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def connect(self, addr):
self._do_connect(addr)
@@ -1366,6 +1400,7 @@
def sendto(self, data, p1, p2=None):
try:
+ self._last_error = 0
if not p2:
flags, addr = 0, p1
else:
@@ -1377,9 +1412,10 @@
result = self.sock_impl.sendto(byte_array, _get_jsockaddr(addr, self.family, self.type, self.proto, 0), flags)
return result
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def send(self, data, flags=None):
+ self._last_error = 0
if not self.connected: raise error(errno.ENOTCONN, "Socket is not connected")
byte_array = java.lang.String(data).getBytes('iso-8859-1')
return self.sock_impl.send(byte_array, flags)
@@ -1393,6 +1429,7 @@
http://bugs.sun.com/view_bug.do?bug_id=6621689
"""
try:
+ self._last_error = 0
# This is the old 2.1 behaviour
#assert self.sock_impl
# This is amak's preferred interpretation
@@ -1403,14 +1440,15 @@
self._config()
return self.sock_impl.recvfrom(num_bytes, flags)
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def recv(self, num_bytes, flags=None):
if not self.sock_impl: raise error(errno.ENOTCONN, "Socket is not connected")
try:
+ self._last_error = 0
return self.sock_impl.recv(num_bytes, flags)
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def __del__(self):
self.close()
@@ -1812,7 +1850,7 @@
self._in_buf = java.io.BufferedInputStream(self.ssl_sock.getInputStream())
self._out_buf = java.io.BufferedOutputStream(self.ssl_sock.getOutputStream())
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def _make_ssl_socket(self, plain_socket, auto_close=0):
java_net_socket = plain_socket._get_jsocket()
@@ -1835,7 +1873,7 @@
data = data[:m]
return data.tostring()
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def write(self, s):
try:
@@ -1843,13 +1881,13 @@
self._out_buf.flush()
return len(s)
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def _get_server_cert(self):
try:
return self.ssl_sock.getSession().getPeerCertificates()[0]
except java.lang.Exception, jlx:
- raise _map_exception(jlx)
+ raise _map_exception(self, jlx)
def server(self):
cert = self._get_server_cert()
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -898,10 +898,48 @@
self._testOption(socket.IPPROTO_TCP, socket.TCP_NODELAY, [0, 1])
self._testInheritedOption(socket.IPPROTO_TCP, socket.TCP_NODELAY, [0, 1])
-class TestUnsupportedOptions(TestSocketOptions):
+class TestPseudoOptions(unittest.TestCase):
def testSO_ACCEPTCONN(self):
- self.failUnless(hasattr(socket, 'SO_ACCEPTCONN'))
+ for socket_type, listen, expected_result in [
+ #(socket.SOCK_STREAM, 0, 0),
+ #(socket.SOCK_STREAM, 1, 1),
+ (socket.SOCK_DGRAM, 0, Exception),
+ ]:
+ s = socket.socket(socket.AF_INET, socket_type)
+ if listen:
+ s.listen(1)
+ try:
+ result = s.getsockopt(socket.SOL_SOCKET, socket.SO_ACCEPTCONN)
+ if expected_result is not Exception:
+ self.failUnlessEqual(result, expected_result)
+ except socket.error, se:
+ if expected_result is Exception:
+ if se[0] != errno.ENOPROTOOPT:
+ self.fail("getsockopt(SO_ACCEPTCONN) on wrong socket type raised wrong exception: %s" % str(se))
+ else:
+ self.fail("getsocket(SO_ACCEPTCONN) on valid socket type should not have raised exception: %s" % (str(se)))
+
+ def testSO_ERROR(self):
+ for socket_type in [socket.SOCK_STREAM, socket.SOCK_DGRAM]:
+ s = socket.socket(socket.AF_INET, socket_type)
+ self.failUnlessEqual(s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR), 0)
+ try:
+ # Now cause an error
+ s.connect(("localhost", 100000))
+ self.fail("Operation '%s' that should have failed to generate SO_ERROR did not" % operation)
+ except socket.error, se:
+ so_error = s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
+ self.failUnlessEqual(so_error, se[0])
+ # Now retrieve the option again - it should be zero
+ self.failUnlessEqual(s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR), 0)
+
+ def testSO_TYPE(self):
+ for socket_type in [socket.SOCK_STREAM, socket.SOCK_DGRAM]:
+ s = socket.socket(socket.AF_INET, socket_type)
+ self.failUnlessEqual(s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE), socket_type)
+
+class TestUnsupportedOptions(TestSocketOptions):
def testSO_DEBUG(self):
self.failUnless(hasattr(socket, 'SO_DEBUG'))
@@ -909,9 +947,6 @@
def testSO_DONTROUTE(self):
self.failUnless(hasattr(socket, 'SO_DONTROUTE'))
- def testSO_ERROR(self):
- self.failUnless(hasattr(socket, 'SO_ERROR'))
-
def testSO_EXCLUSIVEADDRUSE(self):
# this is an MS specific option that will not be appearing on java
# http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6421091
@@ -935,9 +970,6 @@
def testSO_SNDTIMEO(self):
self.failUnless(hasattr(socket, 'SO_SNDTIMEO'))
- def testSO_TYPE(self):
- self.failUnless(hasattr(socket, 'SO_TYPE'))
-
def testSO_USELOOPBACK(self):
self.failUnless(hasattr(socket, 'SO_USELOOPBACK'))
@@ -2429,6 +2461,7 @@
GeneralModuleTests,
IPAddressTests,
TestSupportedOptions,
+ TestPseudoOptions,
TestUnsupportedOptions,
BasicTCPTest,
TCPServerTimeoutTest,
--
Repository URL: http://hg.python.org/jython
More information about the Jython-checkins
mailing list