Revision: 7193
http://jython.svn.sourceforge.net/jython/?rev=7193&view=rev
Author: amak
Date: 2011-02-03 21:56:23 +0000 (Thu, 03 Feb 2011)
Log Message:
-----------
Checking in more support for IPV6, including support for 4-tuples including scope, etc.
Modified Paths:
--------------
trunk/jython/Lib/socket.py
trunk/jython/Lib/test/test_socket.py
trunk/jython/NEWS
Modified: trunk/jython/Lib/socket.py
===================================================================
--- trunk/jython/Lib/socket.py 2011-02-02 10:36:55 UTC (rev 7192)
+++ trunk/jython/Lib/socket.py 2011-02-03 21:56:23 UTC (rev 7193)
@@ -314,26 +314,20 @@
def __init__(self, socket=None):
if socket:
self.jchannel = socket.getChannel()
- self.host = socket.getInetAddress().getHostAddress()
- self.port = socket.getPort()
else:
self.jchannel = java.nio.channels.SocketChannel.open()
- self.host = None
- self.port = None
self.jsocket = self.jchannel.socket()
self.socketio = org.python.core.io.SocketIO(self.jchannel, 'rw')
- def bind(self, host, port, reuse_addr):
+ def bind(self, jsockaddr, reuse_addr):
self.jsocket.setReuseAddress(reuse_addr)
- self.jsocket.bind(java.net.InetSocketAddress(host, port))
+ self.jsocket.bind(jsockaddr)
- def connect(self, host, port):
- self.host = host
- self.port = port
+ def connect(self, jsockaddr):
if self.mode == MODE_TIMEOUT:
- self.jsocket.connect(java.net.InetSocketAddress(self.host, self.port), self._timeout_millis)
+ self.jsocket.connect (jsockaddr, self._timeout_millis)
else:
- self.jchannel.connect(java.net.InetSocketAddress(self.host, self.port))
+ self.jchannel.connect(jsockaddr)
def finish_connect(self):
return self.jchannel.finishConnect()
@@ -382,15 +376,11 @@
(SOL_SOCKET, SO_TIMEOUT): 'SoTimeout',
}
- def __init__(self, host, port, backlog, reuse_addr):
+ def __init__(self, jsockaddr, backlog, reuse_addr):
self.jchannel = java.nio.channels.ServerSocketChannel.open()
self.jsocket = self.jchannel.socket()
- if host:
- bindaddr = java.net.InetSocketAddress(host, port)
- else:
- bindaddr = java.net.InetSocketAddress(port)
self.jsocket.setReuseAddress(reuse_addr)
- self.jsocket.bind(bindaddr, backlog)
+ self.jsocket.bind(jsockaddr, backlog)
self.socketio = org.python.core.io.ServerSocketIO(self.jchannel, 'rw')
def accept(self):
@@ -422,20 +412,16 @@
(SOL_SOCKET, SO_TIMEOUT): 'SoTimeout',
}
- def __init__(self, port=None, address=None, reuse_addr=0):
+ def __init__(self, jsockaddr=None, reuse_addr=0):
self.jchannel = java.nio.channels.DatagramChannel.open()
self.jsocket = self.jchannel.socket()
- if port is not None:
- if address is not None:
- local_address = java.net.InetSocketAddress(address, port)
- else:
- local_address = java.net.InetSocketAddress(port)
+ if jsockaddr is not None:
self.jsocket.setReuseAddress(reuse_addr)
- self.jsocket.bind(local_address)
+ self.jsocket.bind(jsockaddr)
self.socketio = org.python.core.io.DatagramSocketIO(self.jchannel, 'rw')
- def connect(self, host, port):
- self.jchannel.connect(java.net.InetSocketAddress(host, port))
+ def connect(self, jsockaddr):
+ self.jchannel.connect(jsockaddr)
def disconnect(self):
"""
@@ -469,12 +455,11 @@
bytes_sent = self.jchannel.send(byte_buf, socket_address)
return bytes_sent
- def sendto(self, byte_array, host, port, flags):
- socket_address = java.net.InetSocketAddress(host, port)
+ def sendto(self, byte_array, jsockaddr, flags):
if self.mode == MODE_TIMEOUT:
- return self._do_send_net(byte_array, socket_address, flags)
+ return self._do_send_net(byte_array, jsockaddr, flags)
else:
- return self._do_send_nio(byte_array, socket_address, flags)
+ return self._do_send_nio(byte_array, jsockaddr, flags)
def send(self, byte_array, flags):
if self.mode == MODE_TIMEOUT:
@@ -526,8 +511,7 @@
else:
return self._do_receive_nio(0, num_bytes, flags)
-# For now, we DO NOT have complete IPV6 support.
-has_ipv6 = False
+has_ipv6 = True # IPV6 FTW!
# Name and address functions
@@ -607,6 +591,88 @@
assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets"
return _udpsocket()
+class _ip_address_t:
+
+ def __str__(self):
+ return "('%s', %d)" % (self.sockaddr, self.port)
+
+class _ipv4_address_t(_ip_address_t):
+
+ def __init__(self, sockaddr, port, jaddress):
+ self.sockaddr = sockaddr
+ self.port = port
+ self.jaddress = jaddress
+
+ def __getitem__(self, index):
+ if 0 == index:
+ return self.sockaddr
+ elif 1 == index:
+ return self.port
+ else:
+ raise IndexError()
+
+ def __len__(self):
+ return 2
+
+class _ipv6_address_t(_ip_address_t):
+
+ def __init__(self, sockaddr, port, jaddress):
+ self.sockaddr = sockaddr
+ self.port = port
+ self.jaddress = jaddress
+
+ def __getitem__(self, index):
+ if 0 == index:
+ return self.sockaddr
+ elif 1 == index:
+ return self.port
+ elif 2 == index:
+ return 0
+ elif 3 == index:
+ return self.jaddress.scopeId
+ else:
+ raise IndexError()
+
+ def __len__(self):
+ return 4
+
+def _get_jsockaddr(address_object, for_udp=False):
+ if address_object is None:
+ return java.net.InetSocketAddress(0) # Let the system pick an ephemeral port
+ if isinstance(address_object, _ip_address_t):
+ return java.net.InetSocketAddress(address_object.jaddress, address_object[1])
+ error_message = "Address must be a 2-tuple (ipv4: (host, port)) or a 4-tuple (ipv6: (host, port, flow, scope))"
+ if not isinstance(address_object, tuple) or \
+ len(address_object) not in [2,4] or \
+ not isinstance(address_object[0], basestring) or \
+ not isinstance(address_object[1], (int, long)):
+ raise TypeError(error_message)
+ if len(address_object) == 4 and not isinstance(address_object[3], (int, long)):
+ raise TypeError(error_message)
+ hostname, port = address_object[0].strip(), address_object[1]
+ if for_udp:
+ if hostname == "":
+ hostname = INADDR_ANY
+ elif hostname == "<broadcast>":
+ hostname = INADDR_BROADCAST
+ else:
+ if hostname == "":
+ hostname = None
+ if hostname is None:
+ return java.net.InetSocketAddress(port)
+ if isinstance(hostname, unicode):
+ # XXX: Should be encode('idna') (See CPython
+ # socketmodule::getsockaddrarg), but Jython's idna support is
+ # currently broken
+ hostname = hostname.encode()
+ if len(address_object) == 4:
+ # There is no way to get a Inet6Address: Inet6Address.getByName() simply calls
+ # InetAddress.getByName,() which also returns Inet4Address objects
+ # If users want to use IPv6 address, scoped or not,
+ # they should use getaddrinfo(family=AF_INET6)
+ pass
+ return java.net.InetSocketAddress(java.net.InetAddress.getByName(hostname), port)
+
_ipv4_addresses_only = False
def _use_ipv4_addresses_only(value):
@@ -639,11 +705,12 @@
else:
canonname = asPyString(a.getCanonicalHostName())
if host is None and passive_mode and not canonname_mode:
- sockname = INADDR_ANY
+ sockaddr = INADDR_ANY
else:
- sockname = asPyString(a.getHostAddress())
+ sockaddr = asPyString(a.getHostAddress())
# TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses
- results.append((family, socktype, proto, canonname, (sockname, port)))
+ sock_tuple = {AF_INET : _ipv4_address_t, AF_INET6 : _ipv6_address_t}[family](sockaddr, port, a)
+ results.append((family, socktype, proto, canonname, sock_tuple))
return results
except java.lang.Exception, jlx:
raise _map_exception(jlx)
@@ -800,24 +867,6 @@
def _get_jsocket(self):
return self.sock_impl.jsocket
-def _unpack_address_tuple(address_tuple):
- # TODO: Upgrade to support the 4-tuples used for IPv6 addresses
- # which include flowinfo and scope_id.
- # To be upgraded in synch with getaddrinfo
- error_message = "Address must be a tuple of (hostname, port)"
- if not isinstance(address_tuple, tuple) or \
- not isinstance(address_tuple[0], basestring) or \
- not isinstance(address_tuple[1], (int, long)):
- raise TypeError(error_message)
- hostname = address_tuple[0]
- if isinstance(hostname, unicode):
- # XXX: Should be encode('idna') (See CPython
- # socketmodule::getsockaddrarg), but Jython's idna support is
- # currently broken
- hostname = hostname.encode()
- hostname = hostname.strip()
- return hostname, address_tuple[1]
-
class _tcpsocket(_nonblocking_api_mixin):
sock_impl = None
@@ -833,7 +882,7 @@
assert not self.sock_impl
assert not self.local_addr
# Do the address format check
- _unpack_address_tuple(addr)
+ _get_jsockaddr(addr)
self.local_addr = addr
def listen(self, backlog):
@@ -841,11 +890,7 @@
try:
assert not self.sock_impl
self.server = 1
- if self.local_addr:
- host, port = _unpack_address_tuple(self.local_addr)
- else:
- host, port = "", 0
- self.sock_impl = _server_socket_impl(host, port, backlog, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ])
+ self.sock_impl = _server_socket_impl(_get_jsockaddr(self.local_addr), backlog, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ])
self._config()
except java.lang.Exception, jlx:
raise _map_exception(jlx)
@@ -867,22 +912,14 @@
except java.lang.Exception, jlx:
raise _map_exception(jlx)
- def _get_host_port(self, addr):
- host, port = _unpack_address_tuple(addr)
- if host == "":
- host = java.net.InetAddress.getLocalHost()
- return host, port
-
def _do_connect(self, addr):
try:
assert not self.sock_impl
- host, port = self._get_host_port(addr)
self.sock_impl = _client_socket_impl()
if self.local_addr: # Has the socket been bound to a local address?
- bind_host, bind_port = _unpack_address_tuple(self.local_addr)
- self.sock_impl.bind(bind_host, bind_port, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ])
+ self.sock_impl.bind(_get_jsockaddr(self.local_addr), self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ])
self._config() # Configure timeouts, etc, now that the socket exists
- self.sock_impl.connect(host, port)
+ self.sock_impl.connect(_get_jsockaddr(addr))
except java.lang.Exception, jlx:
raise _map_exception(jlx)
@@ -983,7 +1020,7 @@
class _udpsocket(_nonblocking_api_mixin):
sock_impl = None
- addr = None
+ connected = False
def __init__(self):
_nonblocking_api_mixin.__init__(self)
@@ -991,24 +1028,19 @@
def bind(self, addr):
try:
assert not self.sock_impl
- host, port = _unpack_address_tuple(addr)
- if host == "":
- host = INADDR_ANY
- host_address = java.net.InetAddress.getByName(host)
- self.sock_impl = _datagram_socket_impl(port, host_address, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ])
+ self.sock_impl = _datagram_socket_impl(_get_jsockaddr(addr, True), self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ])
self._config()
except java.lang.Exception, jlx:
raise _map_exception(jlx)
def _do_connect(self, addr):
try:
- host, port = _unpack_address_tuple(addr)
- assert not self.addr
- self.addr = addr
+ assert not self.connected, "Datagram Socket is already connected"
if not self.sock_impl:
self.sock_impl = _datagram_socket_impl()
self._config()
- self.sock_impl.connect(host, port)
+ self.sock_impl.connect(_get_jsockaddr(addr))
+ self.connected = True
except java.lang.Exception, jlx:
raise _map_exception(jlx)
@@ -1029,17 +1061,14 @@
if not self.sock_impl:
self.sock_impl = _datagram_socket_impl()
self._config()
- host, port = _unpack_address_tuple(addr)
- if host == "<broadcast>":
- host = INADDR_BROADCAST
byte_array = java.lang.String(data).getBytes('iso-8859-1')
- result = self.sock_impl.sendto(byte_array, host, port, flags)
+ result = self.sock_impl.sendto(byte_array, _get_jsockaddr(addr, True), flags)
return result
except java.lang.Exception, jlx:
raise _map_exception(jlx)
def send(self, data, flags=None):
- if not self.addr: raise error(errno.ENOTCONN, "Socket is not connected")
+ 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)
Modified: trunk/jython/Lib/test/test_socket.py
===================================================================
--- trunk/jython/Lib/test/test_socket.py 2011-02-02 10:36:55 UTC (rev 7192)
+++ trunk/jython/Lib/test/test_socket.py 2011-02-03 21:56:23 UTC (rev 7193)
@@ -1502,6 +1502,73 @@
doAddressTest(socket.getaddrinfo("localhost", 0, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, 0))
socket._use_ipv4_addresses_only(False)
+ def testAddrTupleTypes(self):
+ ipv4_address_tuple = socket.getaddrinfo("localhost", 80, socket.AF_INET, socket.SOCK_STREAM, 0, 0)[0][4]
+ self.failUnlessEqual(ipv4_address_tuple[0], "127.0.0.1")
+ self.failUnlessEqual(ipv4_address_tuple[1], 80)
+ self.failUnlessRaises(IndexError, lambda: ipv4_address_tuple[2])
+ self.failUnlessEqual(str(ipv4_address_tuple), "('127.0.0.1', 80)")
+
+ ipv6_address_tuple = socket.getaddrinfo("localhost", 80, socket.AF_INET6, socket.SOCK_STREAM, 0, 0)[0][4]
+ self.failUnless (ipv6_address_tuple[0] in ["::1", "0:0:0:0:0:0:0:1"])
+ self.failUnlessEqual(ipv6_address_tuple[1], 80)
+ self.failUnlessEqual(ipv6_address_tuple[2], 0)
+ # Can't have an expectation for scope
+ try:
+ ipv6_address_tuple[3]
+ except IndexError:
+ self.fail("Failed to retrieve third element of ipv6 4-tuple")
+ self.failUnlessRaises(IndexError, lambda: ipv6_address_tuple[4])
+ self.failUnless(str(ipv6_address_tuple) in ["('::1', 80)", "('0:0:0:0:0:0:0:1', 80)"])
+
+class TestJython_get_jsockaddr(unittest.TestCase):
+ "These tests are specific to jython: they test a key internal routine"
+
+ def testIPV4AddressesFromGetAddrInfo(self):
+ local_addr = socket.getaddrinfo("localhost", 80, socket.AF_INET, socket.SOCK_STREAM, 0, 0)[0][4]
+ sockaddr = socket._get_jsockaddr(local_addr)
+ self.failUnless(isinstance(sockaddr, java.net.InetSocketAddress), "_get_jsockaddr returned wrong type: '%s'" % str(type(sockaddr)))
+ self.failUnlessEqual(sockaddr.address.hostAddress, "127.0.0.1")
+ self.failUnlessEqual(sockaddr.port, 80)
+
+ def testIPV6AddressesFromGetAddrInfo(self):
+ local_addr = socket.getaddrinfo("localhost", 80, socket.AF_INET6, socket.SOCK_STREAM, 0, 0)[0][4]
+ sockaddr = socket._get_jsockaddr(local_addr)
+ self.failUnless(isinstance(sockaddr, java.net.InetSocketAddress), "_get_jsockaddr returned wrong type: '%s'" % str(type(sockaddr)))
+ self.failUnless(sockaddr.address.hostAddress in ["::1", "0:0:0:0:0:0:0:1"])
+ self.failUnlessEqual(sockaddr.port, 80)
+
+ def testAddressesFrom2Tuple(self):
+ for addr_tuple in [
+ ("localhost", 80),
+ ]:
+ sockaddr = socket._get_jsockaddr(addr_tuple)
+ self.failUnless(isinstance(sockaddr, java.net.InetSocketAddress), "_get_jsockaddr returned wrong type: '%s'" % str(type(sockaddr)))
+ self.failUnless(sockaddr.address.hostAddress in ["127.0.0.1", "::1", "0:0:0:0:0:0:0:1"])
+ self.failUnlessEqual(sockaddr.port, 80)
+
+ def testAddressesFrom4Tuple(self):
+ # This test disabled: cannot construct IPV6 addresses from 4-tuple
+ return
+ for addr_tuple in [
+ ("localhost", 80, 0, 0),
+ ]:
+ sockaddr = socket._get_jsockaddr(addr_tuple)
+ self.failUnless(isinstance(sockaddr, java.net.InetSocketAddress), "_get_jsockaddr returned wrong type: '%s'" % str(type(sockaddr)))
+ self.failUnless(isinstance(sockaddr.address, java.net.Inet6Address), "_get_jsockaddr returned wrong address type: '%s'" % str(type(sockaddr.address)))
+ self.failUnless(sockaddr.address.hostAddress in ["::1", "0:0:0:0:0:0:0:1"])
+ self.failUnlessEqual(sockaddr.address.scopeId, 0)
+ self.failUnlessEqual(sockaddr.port, 80)
+
+ def testSpecialHostnames(self):
+ for addr_tuple, for_udp, expected_hostname in [
+ ( ("", 80), False, socket.INADDR_ANY),
+ ( ("", 80), True, socket.INADDR_ANY),
+ ( ("<broadcast>", 80), True, socket.INADDR_BROADCAST),
+ ]:
+ sockaddr = socket._get_jsockaddr(addr_tuple, for_udp)
+ self.failUnlessEqual(sockaddr.hostName, expected_hostname, "_get_jsockaddr returned wrong hostname '%s' for special hostname '%s'" % (sockaddr.hostName, expected_hostname))
+
class TestExceptions(unittest.TestCase):
def testExceptionTree(self):
@@ -1728,10 +1795,12 @@
if sys.platform[:4] == 'java':
tests.append(TestJythonTCPExceptions)
tests.append(TestJythonUDPExceptions)
+ tests.append(TestJython_get_jsockaddr)
# TODO: Broadcast requires permission, and is blocked by some firewalls
# Need some way to discover the network setup on the test machine
if False:
tests.append(UDPBroadcastTest)
+# tests = [TestJython_get_jsockaddr]
suites = [unittest.makeSuite(klass, 'test') for klass in tests]
test_support.run_suite(unittest.TestSuite(suites))
Modified: trunk/jython/NEWS
===================================================================
--- trunk/jython/NEWS 2011-02-02 10:36:55 UTC (rev 7192)
+++ trunk/jython/NEWS 2011-02-03 21:56:23 UTC (rev 7193)
@@ -5,6 +5,7 @@
- [ 1667 ] thread.local subclasses with constructor params fail
- [ 1698 ] warnings module fails under JSR-223
- [ 1697 ] Wrong error message when http connection can not be established
+ - [ 1210 ] Lib/socket.py doesn't allow IPv6 sockets and fails with an AssertionError
- [ 1700 ] "virtualenv is not compatible" to 2.5.2rc3
Jython 2.5.2rc3
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|