From: <am...@us...> - 2009-07-03 15:45:09
|
Revision: 6506 http://jython.svn.sourceforge.net/jython/?rev=6506&view=rev Author: amak Date: 2009-07-03 15:45:08 +0000 (Fri, 03 Jul 2009) Log Message: ----------- 1. Added support for the AI_PASSIVE flag 2. Added support for the AI_CANONNAME flag 3. Added unit tests for same 4. Split out getaddrinfo unit tests into their own test case class 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 2009-06-30 06:11:20 UTC (rev 6505) +++ trunk/jython/Lib/socket.py 2009-07-03 15:45:08 UTC (rev 6506) @@ -76,12 +76,12 @@ # Javax.net.ssl classes import javax.net.ssl.SSLSocketFactory -# Javax.net.ssl exceptions -javax.net.ssl.SSLException -javax.net.ssl.SSLHandshakeException -javax.net.ssl.SSLKeyException -javax.net.ssl.SSLPeerUnverifiedException -javax.net.ssl.SSLProtocolException +# Javax.net.ssl exceptions +javax.net.ssl.SSLException +javax.net.ssl.SSLHandshakeException +javax.net.ssl.SSLKeyException +javax.net.ssl.SSLPeerUnverifiedException +javax.net.ssl.SSLProtocolException import org.python.core.io.DatagramSocketIO import org.python.core.io.ServerSocketIO @@ -128,16 +128,16 @@ (java.nio.channels.NotYetConnectedException, ALL) : None, (java.nio.channels.UnresolvedAddressException, ALL) : lambda: gaierror(errno.EGETADDRINFOFAILED, 'getaddrinfo failed'), (java.nio.channels.UnsupportedAddressTypeException, ALL) : None, - -# These error codes are currently wrong: getting them correct is going to require -# some investigation. Cpython 2.6 introduced extensive SSL support. - -(javax.net.ssl.SSLException, ALL) : lambda: sslerror(-1, 'SSL exception'), -(javax.net.ssl.SSLHandshakeException, ALL) : lambda: sslerror(-1, 'SSL handshake exception'), -(javax.net.ssl.SSLKeyException, ALL) : lambda: sslerror(-1, 'SSL key exception'), -(javax.net.ssl.SSLPeerUnverifiedException, ALL) : lambda: sslerror(-1, 'SSL peer unverified exception'), -(javax.net.ssl.SSLProtocolException, ALL) : lambda: sslerror(-1, 'SSL protocol exception'), +# These error codes are currently wrong: getting them correct is going to require +# some investigation. Cpython 2.6 introduced extensive SSL support. + +(javax.net.ssl.SSLException, ALL) : lambda: sslerror(-1, 'SSL exception'), +(javax.net.ssl.SSLHandshakeException, ALL) : lambda: sslerror(-1, 'SSL handshake exception'), +(javax.net.ssl.SSLKeyException, ALL) : lambda: sslerror(-1, 'SSL key exception'), +(javax.net.ssl.SSLPeerUnverifiedException, ALL) : lambda: sslerror(-1, 'SSL peer unverified exception'), +(javax.net.ssl.SSLProtocolException, ALL) : lambda: sslerror(-1, 'SSL protocol exception'), + } def would_block_error(exc=None): @@ -168,7 +168,14 @@ AF_INET6 = 23 AI_PASSIVE=1 +AI_CANONNAME=2 +# For some reason, probably historical, SOCK_DGRAM and SOCK_STREAM are opposite values of what they are on cpython. +# I.E. The following is the way they are on cpython +# SOCK_STREAM = 1 +# SOCK_DGRAM = 2 +# At some point, we should probably switch them around, which *should* not affect anybody + SOCK_DGRAM = 1 SOCK_STREAM = 2 SOCK_RAW = 3 # not supported @@ -189,10 +196,10 @@ SO_TIMEOUT = 128 TCP_NODELAY = 256 - -INADDR_ANY = "0.0.0.0" -INADDR_BROADCAST = "255.255.255.255" +INADDR_ANY = "0.0.0.0" +INADDR_BROADCAST = "255.255.255.255" + # Options with negative constants are not supported # They are being added here so that code that refers to them # will not break with an AttributeError @@ -209,11 +216,11 @@ SO_SNDTIMEO = -512 SO_TYPE = -1024 SO_USELOOPBACK = -2048 - + __all__ = ['AF_UNSPEC', 'AF_INET', 'AF_INET6', 'AI_PASSIVE', 'SOCK_DGRAM', 'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET', 'SO_BROADCAST', 'SO_ERROR', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE', - 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY', + 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY', 'INADDR_ANY', 'INADDR_BROADCAST', 'IPPROTO_TCP', 'IPPROTO_UDP', 'SocketType', 'error', 'herror', 'gaierror', 'timeout', 'getfqdn', 'gethostbyaddr', 'gethostbyname', 'gethostname', @@ -222,15 +229,15 @@ 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR', ] -def _constant_to_name(const_value): - sock_module = sys.modules['socket'] - try: - for name in dir(sock_module): - if getattr(sock_module, name) is const_value: - return name - return "Unknown" - finally: - sock_module = None +def _constant_to_name(const_value): + sock_module = sys.modules['socket'] + try: + for name in dir(sock_module): + if getattr(sock_module, name) is const_value: + return name + return "Unknown" + finally: + sock_module = None class _nio_impl: @@ -285,15 +292,15 @@ class _client_socket_impl(_nio_impl): - options = { + options = { (SOL_SOCKET, SO_KEEPALIVE): 'KeepAlive', (SOL_SOCKET, SO_LINGER): 'SoLinger', (SOL_SOCKET, SO_OOBINLINE): 'OOBInline', (SOL_SOCKET, SO_RCVBUF): 'ReceiveBufferSize', (SOL_SOCKET, SO_REUSEADDR): 'ReuseAddress', (SOL_SOCKET, SO_SNDBUF): 'SendBufferSize', - (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', - (IPPROTO_TCP, TCP_NODELAY): 'TcpNoDelay', + (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', + (IPPROTO_TCP, TCP_NODELAY): 'TcpNoDelay', } def __init__(self, socket=None): @@ -361,10 +368,10 @@ class _server_socket_impl(_nio_impl): - options = { + options = { (SOL_SOCKET, SO_RCVBUF): 'ReceiveBufferSize', (SOL_SOCKET, SO_REUSEADDR): 'ReuseAddress', - (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', + (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', } def __init__(self, host, port, backlog, reuse_addr): @@ -399,12 +406,12 @@ class _datagram_socket_impl(_nio_impl): - options = { + options = { (SOL_SOCKET, SO_BROADCAST): 'Broadcast', (SOL_SOCKET, SO_RCVBUF): 'ReceiveBufferSize', (SOL_SOCKET, SO_REUSEADDR): 'ReuseAddress', (SOL_SOCKET, SO_SNDBUF): 'SendBufferSize', - (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', + (SOL_SOCKET, SO_TIMEOUT): 'SoTimeout', } def __init__(self, port=None, address=None, reuse_addr=0): @@ -515,7 +522,7 @@ has_ipv6 = False # Name and address functions - + def _gethostbyaddr(name): # This is as close as I can get; at least the types are correct... addresses = java.net.InetAddress.getAllByName(gethostbyname(name)) @@ -576,12 +583,12 @@ assert family == AF_INET, "Only AF_INET sockets are currently supported on jython" assert type in (SOCK_DGRAM, SOCK_STREAM), "Only SOCK_STREAM and SOCK_DGRAM sockets are currently supported on jython" if type == SOCK_STREAM: - if protocol != 0: - assert protocol == IPPROTO_TCP, "Only IPPROTO_TCP supported on SOCK_STREAM sockets" + if protocol != 0: + assert protocol == IPPROTO_TCP, "Only IPPROTO_TCP supported on SOCK_STREAM sockets" return _tcpsocket() else: - if protocol != 0: - assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets" + if protocol != 0: + assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets" return _udpsocket() def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=None): @@ -594,16 +601,23 @@ AF_INET6: lambda x: isinstance(x, java.net.Inet6Address), AF_UNSPEC: lambda x: isinstance(x, java.net.InetAddress), }[family]) - # Cant see a way to support AI_PASSIVE right now. - # if flags and flags & AI_PASSIVE: - # pass + if host == "": + host = java.net.InetAddress.getLocalHost().getHostName() + passive_mode = flags is not None and flags & AI_PASSIVE + canonname_mode = flags is not None and flags & AI_CANONNAME results = [] for a in java.net.InetAddress.getAllByName(host): if len([f for f in filter_fns if f(a)]): family = {java.net.Inet4Address: AF_INET, java.net.Inet6Address: AF_INET6}[a.getClass()] + if passive_mode and not canonname_mode: + canonname = "" + else: + canonname = asPyString(a.getCanonicalHostName()) + if host is None and passive_mode and not canonname_mode: + sockname = INADDR_ANY + else: + sockname = asPyString(a.getHostAddress()) # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses - canonname = asPyString(a.getCanonicalHostName()) - sockname = asPyString(a.getHostAddress()) results.append((family, socktype, proto, canonname, (sockname, port))) return results except java.lang.Exception, jlx: @@ -674,9 +688,9 @@ close_lock = threading.Lock() def __init__(self): - self.timeout = _defaulttimeout - if self.timeout is not None: - self.mode = MODE_TIMEOUT + self.timeout = _defaulttimeout + if self.timeout is not None: + self.mode = MODE_TIMEOUT self.pending_options = { (SOL_SOCKET, SO_REUSEADDR): 0, } @@ -844,7 +858,7 @@ self.sock_impl.bind(bind_host, bind_port, self.pending_options[ (SOL_SOCKET, SO_REUSEADDR) ]) self._config() # Configure timeouts, etc, now that the socket exists self.sock_impl.connect(host, port) - except java.lang.Exception, jlx: + except java.lang.Exception, jlx: raise _map_exception(jlx) def connect(self, addr): @@ -952,8 +966,8 @@ def bind(self, addr): try: assert not self.sock_impl - host, port = _unpack_address_tuple(addr) - if host == "": + 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) ]) @@ -994,8 +1008,8 @@ if not self.sock_impl: self.sock_impl = _datagram_socket_impl() self._config() - host, port = _unpack_address_tuple(addr) - if host == "<broadcast>": + 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) @@ -1409,11 +1423,11 @@ class ssl: - def __init__(self, plain_sock, keyfile=None, certfile=None): + def __init__(self, plain_sock, keyfile=None, certfile=None): try: self.ssl_sock = self._make_ssl_socket(plain_sock) self._in_buf = java.io.BufferedInputStream(self.ssl_sock.getInputStream()) - self._out_buf = java.io.BufferedOutputStream(self.ssl_sock.getOutputStream()) + self._out_buf = java.io.BufferedOutputStream(self.ssl_sock.getOutputStream()) except java.lang.Exception, jlx: raise _map_exception(jlx) @@ -1429,7 +1443,7 @@ return ssl_socket def read(self, n=4096): - try: + try: data = jarray.zeros(n, 'b') m = self._in_buf.read(data, 0, n) if m <= 0: @@ -1441,7 +1455,7 @@ raise _map_exception(jlx) def write(self, s): - try: + try: self._out_buf.write(s) self._out_buf.flush() return len(s) @@ -1449,7 +1463,7 @@ raise _map_exception(jlx) def _get_server_cert(self): - try: + try: return self.ssl_sock.getSession().getPeerCertificates()[0] except java.lang.Exception, jlx: raise _map_exception(jlx) Modified: trunk/jython/Lib/test/test_socket.py =================================================================== --- trunk/jython/Lib/test/test_socket.py 2009-06-30 06:11:20 UTC (rev 6505) +++ trunk/jython/Lib/test/test_socket.py 2009-07-03 15:45:08 UTC (rev 6506) @@ -2,6 +2,8 @@ AMAK: 20050515: This module is the test_socket.py from cpython 2.4, ported to jython. """ +import java + import unittest from test import test_support @@ -308,22 +310,6 @@ if not fqhn in all_host_names: self.fail("Error testing host resolution mechanisms.") - def testGetAddrInfo(self): - try: - socket.getaddrinfo(HOST, PORT, 9999) - except socket.gaierror, gaix: - self.failUnlessEqual(gaix[0], errno.EIO) - except Exception, x: - self.fail("getaddrinfo with bad family raised wrong exception: %s" % x) - else: - self.fail("getaddrinfo with bad family should have raised exception") - - addrinfos = socket.getaddrinfo(HOST, PORT) - for addrinfo in addrinfos: - family, socktype, proto, canonname, sockaddr = addrinfo - self.assert_(isinstance(canonname, str)) - self.assert_(isinstance(sockaddr[0], str)) - def testRefCountGetNameInfo(self): # Testing reference count for getnameinfo import sys @@ -529,7 +515,7 @@ class TestSocketOptions(unittest.TestCase): - def setUp(self): + def setUp(self): self.test_udp = self.test_tcp_client = self.test_tcp_server = 0 def _testSetAndGetOption(self, sock, level, option, values): @@ -621,7 +607,7 @@ class TestSupportedOptions(TestSocketOptions): - def testSO_BROADCAST(self): + def testSO_BROADCAST(self): self.test_udp = 1 self._testOption(socket.SOL_SOCKET, socket.SO_BROADCAST, [0, 1]) @@ -821,32 +807,32 @@ def _testDup(self): self.serv_conn.send(MSG) self.serv_conn.send('and ' + MSG) - -class UDPBindTest(unittest.TestCase): - + +class UDPBindTest(unittest.TestCase): + HOST = HOST PORT = PORT - def setUp(self): + def setUp(self): self.sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - + def testBindSpecific(self): self.sock.bind( (self.HOST, self.PORT) ) # Use a specific port actual_port = self.sock.getsockname()[1] self.failUnless(actual_port == self.PORT, "Binding to specific port number should have returned same number: %d != %d" % (actual_port, self.PORT)) - def testBindEphemeral(self): + def testBindEphemeral(self): self.sock.bind( (self.HOST, 0) ) # let system choose a free port - self.failUnless(self.sock.getsockname()[1] != 0, "Binding to port zero should have allocated an ephemeral port number") + self.failUnless(self.sock.getsockname()[1] != 0, "Binding to port zero should have allocated an ephemeral port number") def testShutdown(self): self.sock.bind( (self.HOST, self.PORT) ) self.sock.shutdown(socket.SHUT_RDWR) - def tearDown(self): - self.sock.close() - + def tearDown(self): + self.sock.close() + class BasicUDPTest(ThreadedUDPSocketTest): def __init__(self, methodName='runTest'): @@ -1354,7 +1340,7 @@ used, but if it is on your network this failure is bogus.''' % host) def testConnectDefaultTimeout(self): - _saved_timeout = socket.getdefaulttimeout() + _saved_timeout = socket.getdefaulttimeout() socket.setdefaulttimeout(0.1) cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) host = '192.168.192.168' @@ -1437,6 +1423,49 @@ if not ok: self.fail("recv() returned success when we did not expect it") +class TestGetAddrInfo(unittest.TestCase): + + def testBadFamily(self): + try: + socket.getaddrinfo(HOST, PORT, 9999) + except socket.gaierror, gaix: + self.failUnlessEqual(gaix[0], errno.EIO) + except Exception, x: + self.fail("getaddrinfo with bad family raised wrong exception: %s" % x) + else: + self.fail("getaddrinfo with bad family should have raised exception") + + def testReturnsAreStrings(self): + addrinfos = socket.getaddrinfo(HOST, PORT) + for addrinfo in addrinfos: + family, socktype, proto, canonname, sockaddr = addrinfo + self.assert_(isinstance(canonname, str)) + self.assert_(isinstance(sockaddr[0], str)) + + def testAI_PASSIVE(self): + IPV4_LOOPBACK = "127.0.0.1" + local_hostname = java.net.InetAddress.getLocalHost().getHostName() + local_ip_address = java.net.InetAddress.getLocalHost().getHostAddress() + for flags, host_param, expected_canonname, expected_sockaddr in [ + # First passive flag + (socket.AI_PASSIVE, None, "", socket.INADDR_ANY), + (socket.AI_PASSIVE, "", "", local_ip_address), + (socket.AI_PASSIVE, "localhost", "", IPV4_LOOPBACK), + (socket.AI_PASSIVE, local_hostname, "", local_ip_address), + # Now passive flag AND canonname flag + (socket.AI_PASSIVE|socket.AI_CANONNAME, None, "127.0.0.1", "127.0.0.1"), + (socket.AI_PASSIVE|socket.AI_CANONNAME, "", local_hostname, local_ip_address), + # The following result seems peculiar to me, and may be to do with my local machine setup + # Also may be caused by a security permission failure. + # If you have problems with the following result, just comment it out. + (socket.AI_PASSIVE|socket.AI_CANONNAME, "localhost", IPV4_LOOPBACK, IPV4_LOOPBACK), + (socket.AI_PASSIVE|socket.AI_CANONNAME, local_hostname, local_hostname, local_ip_address), + ]: + addrinfos = socket.getaddrinfo(host_param, 0, socket.AF_INET, socket.SOCK_STREAM, 0, flags) + for family, socktype, proto, canonname, sockaddr in addrinfos: + self.failUnlessEqual(expected_canonname, canonname, "For hostname '%s' and flags %d, canonname '%s' != '%s'" % (host_param, flags, expected_canonname, canonname) ) + self.failUnlessEqual(expected_sockaddr, sockaddr[0], "For hostname '%s' and flags %d, sockaddr '%s' != '%s'" % (host_param, flags, expected_sockaddr, sockaddr[0]) ) + class TestExceptions(unittest.TestCase): def testExceptionTree(self): @@ -1640,6 +1669,7 @@ TCPClientTimeoutTest, TestExceptions, TestInvalidUsage, + TestGetAddrInfo, TestTCPAddressParameters, TestUDPAddressParameters, UDPBindTest, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |