From: <am...@us...> - 2011-02-13 20:50:04
|
Revision: 7198 http://jython.svn.sourceforge.net/jython/?rev=7198&view=rev Author: amak Date: 2011-02-13 20:49:57 +0000 (Sun, 13 Feb 2011) Log Message: ----------- Checking in support for Internationalized Domain Names (RFC 3490), plus unit-tests for same. This new support only works on Java 6. The new feature and workarounds for Java 5 are documented on the jython wiki. http://wiki.python.org/jython/NewSocketModule Partially fixes http://bugs.jython.org/issue1153 Modified Paths: -------------- trunk/jython/Lib/socket.py trunk/jython/Lib/test/test_socket.py Modified: trunk/jython/Lib/socket.py =================================================================== --- trunk/jython/Lib/socket.py 2011-02-12 01:50:38 UTC (rev 7197) +++ trunk/jython/Lib/socket.py 2011-02-13 20:49:57 UTC (rev 7198) @@ -161,6 +161,17 @@ exception.java_exception = exc return exception +_feature_support_map = { + 'ipv6': True, + 'idna': False, + 'tipc': False, +} + +def supports(feature, *args): + if len(args) == 1: + _feature_support_map[feature] = args[0] + return _feature_support_map.get(feature, False) + MODE_BLOCKING = 'block' MODE_NONBLOCKING = 'nonblock' MODE_TIMEOUT = 'timeout' @@ -591,6 +602,36 @@ assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets" return _udpsocket() +# +# Attempt to provide IDNA (RFC 3490) support. +# +# Try java.net.IDN, built into java 6 +# + +idna_libraries = [ + ('java.net.IDN', 'toASCII', java.lang.IllegalArgumentException) +] + +for idna_lib, idna_fn_name, exc in idna_libraries: + try: + m = __import__(idna_lib, globals(), locals(), [idna_fn_name]) + idna_fn = getattr(m, idna_fn_name) + def _encode_idna(name): + try: + return idna_fn(name) + except exc: + raise UnicodeEncodeError(name) + supports('idna', True) + break + except (AttributeError, ImportError), e: + pass +else: + _encode_idna = lambda x: x.encode("ascii") + +# +# Define data structures to support IPV4 and IPV6. +# + class _ip_address_t: pass class _ipv4_address_t(_ip_address_t): @@ -668,10 +709,7 @@ 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() + hostname = _encode_idna(hostname) if len(address_object) == 4: # There is no way to get a Inet6Address: Inet6Address.getByName() simply calls # InetAddress.getByName,() which also returns Inet4Address objects @@ -701,6 +739,8 @@ }[family]) if host == "": host = java.net.InetAddress.getLocalHost().getHostName() + if isinstance(host, unicode): + host = _encode_idna(host) passive_mode = flags is not None and flags & AI_PASSIVE canonname_mode = flags is not None and flags & AI_CANONNAME results = [] Modified: trunk/jython/Lib/test/test_socket.py =================================================================== --- trunk/jython/Lib/test/test_socket.py 2011-02-12 01:50:38 UTC (rev 7197) +++ trunk/jython/Lib/test/test_socket.py 2011-02-13 20:49:57 UTC (rev 7198) @@ -1742,6 +1742,47 @@ def _testUnicodeHostname(self): self.cli.connect((unicode(self.HOST), self.PORT)) +class IDNATest(unittest.TestCase): + + def testGetAddrInfoIDNAHostname(self): + idna_domain = u"al\u00e1n.com" + if socket.supports('idna'): + try: + addresses = socket.getaddrinfo(idna_domain, 80) + self.failUnless(len(addresses) > 0, "No addresses returned for test IDNA domain '%s'" % repr(idna_domain)) + except Exception, x: + self.fail("Unexpected exception raised for socket.getaddrinfo(%s)" % repr(idna_domain)) + else: + try: + socket.getaddrinfo(idna_domain, 80) + except UnicodeEncodeError: + pass + except Exception, x: + self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError, not %s" % (repr(idna_domain), str(x))) + else: + self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError: no exception raised" % repr(idna_domain)) + + def testAddrTupleIDNAHostname(self): + idna_domain = u"al\u00e1n.com" + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if socket.supports('idna'): + try: + s.bind( (idna_domain, 80) ) + except socket.error: + # We're not worried about socket errors, i.e. bind problems, etc. + pass + except Exception, x: + self.fail("Unexpected exception raised for socket.bind(%s)" % repr(idna_domain)) + else: + try: + s.bind( (idna_domain, 80) ) + except UnicodeEncodeError: + pass + except Exception, x: + self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError, not %s" % (repr(idna_domain), str(x))) + else: + self.fail("Non ascii domain '%s' should have raised UnicodeEncodeError: no exception raised" % repr(idna_domain)) + class TestInvalidUsage(unittest.TestCase): def setUp(self): @@ -1793,6 +1834,7 @@ LineBufferedFileObjectClassTestCase, SmallBufferedFileObjectClassTestCase, UnicodeTest, + IDNATest, ] if hasattr(socket, "socketpair"): tests.append(BasicSocketPairTest) @@ -1804,7 +1846,6 @@ # 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)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |