From: <cg...@us...> - 2007-12-02 00:09:53
|
Revision: 3747 http://jython.svn.sourceforge.net/jython/?rev=3747&view=rev Author: cgroves Date: 2007-12-01 16:09:50 -0800 (Sat, 01 Dec 2007) Log Message: ----------- >From patch #1813837 from nhiro to add Japanese codecs using Java's charsets. This can't be applied directly to trunk since Python 2.3 added registerable error handling functions to codecs, and Java's error actions don't give the information necessary to call out to those functions. Added Paths: ----------- branches/Release_2_2maint/jython/Lib/encodings/ branches/Release_2_2maint/jython/Lib/encodings/euc_jp.py branches/Release_2_2maint/jython/Lib/encodings/iso2022_jp.py branches/Release_2_2maint/jython/Lib/encodings/java_charset_wrapper.py branches/Release_2_2maint/jython/Lib/encodings/ms932.py branches/Release_2_2maint/jython/Lib/encodings/shift_jis.py branches/Release_2_2maint/jython/Lib/test/test_java_charset_wrapper.py Added: branches/Release_2_2maint/jython/Lib/encodings/euc_jp.py =================================================================== --- branches/Release_2_2maint/jython/Lib/encodings/euc_jp.py (rev 0) +++ branches/Release_2_2maint/jython/Lib/encodings/euc_jp.py 2007-12-02 00:09:50 UTC (rev 3747) @@ -0,0 +1,16 @@ +""" +euc_jp.py: Python Unicode Codec for euc_jp + +Written by + NISHIO Hirokazu <py...@ni...> + +>>> u = u'\u3053\u3093\u306b\u3061\u306f' +>>> s = u.encode("euc_jp") +>>> assert s == '\xa4\xb3\xa4\xf3\xa4\xcb\xa4\xc1\xa4\xcf' +>>> u2 = s.decode("euc_jp") +>>> assert u2 == u +""" + +import java_charset_wrapper + +getregentry = java_charset_wrapper.create_getregentry("euc_jp") Added: branches/Release_2_2maint/jython/Lib/encodings/iso2022_jp.py =================================================================== --- branches/Release_2_2maint/jython/Lib/encodings/iso2022_jp.py (rev 0) +++ branches/Release_2_2maint/jython/Lib/encodings/iso2022_jp.py 2007-12-02 00:09:50 UTC (rev 3747) @@ -0,0 +1,20 @@ +""" +iso2022_jp.py: Python Unicode Codec for iso2022_jp + +Written by + NISHIO Hirokazu <py...@ni...> + +\x1b)B signals the following characters will be ASCII, and since there are +no ASCII characters at the end of the s, the signifier is optional. Java's +encoder doesn't add it, but CPython's does. + +>>> u = u'\u3053\u3093\u306b\u3061\u306f' +>>> s = u.encode("iso2022_jp") +>>> assert s == '\x1b$B$3$s$K$A$O' or s == '\x1b$B$3$s$K$A$O\x1b)B' +>>> u2 = s.decode("iso2022_jp") +>>> assert u2 == u +""" + +from encodings import java_charset_wrapper + +getregentry = java_charset_wrapper.create_getregentry("iso2022jp") Added: branches/Release_2_2maint/jython/Lib/encodings/java_charset_wrapper.py =================================================================== --- branches/Release_2_2maint/jython/Lib/encodings/java_charset_wrapper.py (rev 0) +++ branches/Release_2_2maint/jython/Lib/encodings/java_charset_wrapper.py 2007-12-02 00:09:50 UTC (rev 3747) @@ -0,0 +1,75 @@ +""" +Creates codecs and getregentry functions using Java's Charsets. + +Error handling with 'strict', 'ignore' and 'replace' is accomplished by mapping +to similar error actions in Java's system, however the results aren't identical +to CPython. For example, where CPython throws away two bytes in a two byte +encoding when encountering an error in 'replace' mode, Java throws away the +first byte and resumes from the second. It's close enough to be functional +though. +""" + +import codecs + +def create_codec(encoding_name): + '''Creates a Codec class using a Java charset for encoding_name''' + from java.nio import ByteBuffer, CharBuffer + from java.nio.charset import CharacterCodingException, Charset, CodingErrorAction + error_to_action = { + 'strict':CodingErrorAction.REPORT, + 'ignore':CodingErrorAction.IGNORE, + 'replace':CodingErrorAction.REPLACE} + + def _chr(n): + "change from -128..127 to 0..255 and chr it" + return chr((n + 256) % 256) + + def configure_errors(charset, errors): + action = error_to_action[errors] + return charset.onMalformedInput(action).onUnmappableCharacter(action) + + class Codec(codecs.Codec): + def encode(self, data, errors='strict'): + encoder = Charset.forName(encoding_name).newEncoder() + encoder = configure_errors(encoder, errors) + charbuffer = CharBuffer.wrap(data) + try: + bytebuffer = encoder.encode(charbuffer) + except CharacterCodingException: + raise UnicodeError('%s encoding error' % encoding_name) + bytebuffer.rewind() + pys = ''.join([_chr(bytebuffer.get()) for c in xrange(bytebuffer.remaining())]) + return (pys, len(data)) + + def decode(self, data, errors='strict'): + decoder = Charset.forName(encoding_name).newDecoder() + decoder = configure_errors(decoder, errors) + bytebuffer = ByteBuffer.allocate(len(data)) + bytebuffer.put(data) + bytebuffer.rewind() + try: + charbuffer = decoder.decode(bytebuffer) + except CharacterCodingException: + raise UnicodeError('%s decoding error' % (encoding_name)) + return (str(charbuffer.toString()), len(data)) + + return Codec + +def create_getregentry(encoding_name): + "makes a 'getregentry' function as in the encoding api" + Codec = create_codec(encoding_name) + class StreamReader(Codec, codecs.StreamReader): + pass + + class StreamWriter(Codec, codecs.StreamWriter): + pass + + def getregentry(): + return ( + Codec().encode, + Codec().decode, + StreamReader, + StreamWriter, + ) + + return getregentry Added: branches/Release_2_2maint/jython/Lib/encodings/ms932.py =================================================================== --- branches/Release_2_2maint/jython/Lib/encodings/ms932.py (rev 0) +++ branches/Release_2_2maint/jython/Lib/encodings/ms932.py 2007-12-02 00:09:50 UTC (rev 3747) @@ -0,0 +1,16 @@ +""" +ms932.py: Python Unicode Codec for ms932 + +Written by + NISHIO Hirokazu <py...@ni...> + +>>> u = u'\u3053\u3093\u306b\u3061\u306f' +>>> s = u.encode("ms932") +>>> assert s == '\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd' +>>> u2 = s.decode("ms932") +>>> assert u2 == u +""" + +import java_charset_wrapper + +getregentry = java_charset_wrapper.create_getregentry("ms932") Added: branches/Release_2_2maint/jython/Lib/encodings/shift_jis.py =================================================================== --- branches/Release_2_2maint/jython/Lib/encodings/shift_jis.py (rev 0) +++ branches/Release_2_2maint/jython/Lib/encodings/shift_jis.py 2007-12-02 00:09:50 UTC (rev 3747) @@ -0,0 +1,16 @@ +""" +shift_jis.py: Python Unicode Codec for shift_jis + +Written by + NISHIO Hirokazu <py...@ni...> + +>>> u = u'\u3053\u3093\u306b\u3061\u306f' +>>> s = u.encode("shift_jis") +>>> assert s == '\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd' +>>> u2 = s.decode("shift_jis") +>>> assert u2 == u +""" + +from encodings import java_charset_wrapper + +getregentry = java_charset_wrapper.create_getregentry("shift_jis") Added: branches/Release_2_2maint/jython/Lib/test/test_java_charset_wrapper.py =================================================================== --- branches/Release_2_2maint/jython/Lib/test/test_java_charset_wrapper.py (rev 0) +++ branches/Release_2_2maint/jython/Lib/test/test_java_charset_wrapper.py 2007-12-02 00:09:50 UTC (rev 3747) @@ -0,0 +1,38 @@ +import doctest +import test_support +import unittest + +from encodings import shift_jis, euc_jp, iso2022_jp, ms932 + +illegalencoded = '\x80\x00' +unmappablechar = u'\u1234' +class TestErrorHandling(unittest.TestCase): + '''Checks that the Java error handling is correcly mapped into Python''' + + def testStrictRaisesUnicodeError(self): + self.assertRaises(UnicodeError, illegalencoded.decode, 'Shift_JIS') + self.assertRaises(UnicodeError, + illegalencoded.decode, 'Shift_JIS', 'strict') + self.assertRaises(UnicodeError, unmappablechar.encode, 'Shift_JIS') + self.assertRaises(UnicodeError, unmappablechar.encode, 'Shift_JIS', + 'strict') + + def testIgnoreRaisesNoError(self): + self.assertEquals(u'\x00', + illegalencoded.decode('Shift_JIS', 'ignore')) + self.assertEquals('', unmappablechar.encode('Shift_JIS', 'ignore')) + self.assertEquals('', unmappablechar.encode('Shift_JIS', 'ignore')) + + def testReplaceReplaces(self): + self.assertEquals(u'\uFFFD\x00', + illegalencoded.decode('Shift_JIS', 'replace')) + self.assertEquals('?', unmappablechar.encode('Shift_JIS', 'replace')) + +def test_main(): + test_support.run_unittest(TestErrorHandling) + + for mod in shift_jis, euc_jp, iso2022_jp, ms932: + test_support.run_doctest(mod) + +if __name__ == "__main__": + test_main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2008-02-27 12:52:33
|
Revision: 4184 http://jython.svn.sourceforge.net/jython/?rev=4184&view=rev Author: amak Date: 2008-02-27 04:52:31 -0800 (Wed, 27 Feb 2008) Log Message: ----------- Modified the implementation of UDP sockets so that they use the java.nio APIs as much as possible. This was necessary because it was causing some legal code sequences to hang (CF bug #1782548). However, there is still a necessary dichotomy in the use of java.nio vs. java.net, because the java.nio APIs do not support timeouts. Therefore, when a UDP socket is in timeout mode, the java.net DatagramSocket API is still used. Otherwise, the java.nio DatagramChannel API is used. Unit tests updated accordingly. Also replaced some tabs with spaces in test_socket.py Modified Paths: -------------- branches/Release_2_2maint/jython/Lib/socket.py branches/Release_2_2maint/jython/Lib/test/test_socket.py Modified: branches/Release_2_2maint/jython/Lib/socket.py =================================================================== --- branches/Release_2_2maint/jython/Lib/socket.py 2008-02-27 05:20:45 UTC (rev 4183) +++ branches/Release_2_2maint/jython/Lib/socket.py 2008-02-27 12:52:31 UTC (rev 4184) @@ -278,12 +278,86 @@ def finish_connect(self): return self.jchannel.finishConnect() - def receive(self, packet): - self.jsocket.receive(packet) + def disconnect(self): + """ + Disconnect the datagram socket. + cpython appears not to have this operation + """ + self.jchannel.disconnect() - def send(self, packet): + def _do_send_net(self, byte_array, socket_address, flags): + # Need two separate implementations because the java.nio APIs do not support timeouts + num_bytes = len(byte_array) + if socket_address: + packet = java.net.DatagramPacket(byte_array, num_bytes, socket_address) + else: + packet = java.net.DatagramPacket(byte_array, num_bytes) self.jsocket.send(packet) + return num_bytes + def _do_send_nio(self, byte_array, socket_address, flags): + byte_buf = java.nio.ByteBuffer.wrap(byte_array) + bytes_sent = self.jchannel.send(byte_buf, socket_address) + return bytes_sent + + def sendto(self, byte_array, address, flags): + host, port = _unpack_address_tuple(address) + socket_address = java.net.InetSocketAddress(host, port) + if self.mode == MODE_TIMEOUT: + return self._do_send_net(byte_array, socket_address, flags) + else: + return self._do_send_nio(byte_array, socket_address, flags) + + def send(self, byte_array, flags): + if self.mode == MODE_TIMEOUT: + return self._do_send_net(byte_array, None, flags) + else: + return self._do_send_nio(byte_array, None, flags) + + def _do_receive_net(self, return_source_address, num_bytes, flags): + byte_array = jarray.zeros(num_bytes, 'b') + packet = java.net.DatagramPacket(byte_array, num_bytes) + self.jsocket.receive(packet) + bytes_rcvd = packet.getLength() + if bytes_rcvd < num_bytes: + byte_array = byte_array[:bytes_rcvd] + return_data = byte_array.tostring() + if return_source_address: + host = None + if packet.getAddress(): + host = packet.getAddress().getHostAddress() + port = packet.getPort() + return return_data, (host, port) + else: + return return_data + + def _do_receive_nio(self, return_source_address, num_bytes, flags): + byte_array = jarray.zeros(num_bytes, 'b') + byte_buf = java.nio.ByteBuffer.wrap(byte_array) + source_address = self.jchannel.receive(byte_buf) + byte_buf.flip() ; bytes_read = byte_buf.remaining() + if source_address is None and not self.jchannel.isBlocking(): + raise would_block_error() + if bytes_read < num_bytes: + byte_array = byte_array[:bytes_read] + return_data = byte_array.tostring() + if return_source_address: + return return_data, (source_address.getAddress().getHostAddress(), source_address.getPort()) + else: + return return_data + + def recvfrom(self, num_bytes, flags): + if self.mode == MODE_TIMEOUT: + return self._do_receive_net(1, num_bytes, flags) + else: + return self._do_receive_nio(1, num_bytes, flags) + + def recv(self, num_bytes, flags): + if self.mode == MODE_TIMEOUT: + return self._do_receive_net(0, num_bytes, flags) + else: + return self._do_receive_nio(0, num_bytes, flags) + __all__ = [ 'AF_INET', 'SO_REUSEADDR', 'SOCK_DGRAM', 'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET', 'SocketType', 'error', 'herror', 'gaierror', 'timeout', @@ -674,49 +748,29 @@ flags, addr = 0, p1 else: flags, addr = 0, p2 - n = len(data) if not self.sock_impl: self.sock_impl = _datagram_socket_impl() - host, port = addr - bytes = java.lang.String(data).getBytes('iso-8859-1') - a = java.net.InetAddress.getByName(host) - packet = java.net.DatagramPacket(bytes, n, a, port) - self.sock_impl.send(packet) - return n + byte_array = java.lang.String(data).getBytes('iso-8859-1') + result = self.sock_impl.sendto(byte_array, addr, flags) + return result except java.lang.Exception, jlx: raise _map_exception(jlx) - def send(self, data): + def send(self, data, flags=None): if not self.addr: raise error(errno.ENOTCONN, "Socket is not connected") - return self.sendto(data, self.addr) + byte_array = java.lang.String(data).getBytes('iso-8859-1') + return self.sock_impl.send(byte_array, flags) - def recvfrom(self, n): + def recvfrom(self, num_bytes, flags=None): try: assert self.sock_impl - bytes = jarray.zeros(n, 'b') - packet = java.net.DatagramPacket(bytes, n) - self.sock_impl.receive(packet) - host = None - if packet.getAddress(): - host = packet.getAddress().getHostName() - port = packet.getPort() - m = packet.getLength() - if m < n: - bytes = bytes[:m] - return bytes.tostring(), (host, port) + return self.sock_impl.recvfrom(num_bytes, flags) except java.lang.Exception, jlx: raise _map_exception(jlx) - def recv(self, n): + def recv(self, num_bytes, flags=None): try: - assert self.sock_impl - bytes = jarray.zeros(n, 'b') - packet = java.net.DatagramPacket(bytes, n) - self.sock_impl.receive(packet) - m = packet.getLength() - if m < n: - bytes = bytes[:m] - return bytes.tostring() + return self.sock_impl.recv(num_bytes, flags) except java.lang.Exception, jlx: raise _map_exception(jlx) Modified: branches/Release_2_2maint/jython/Lib/test/test_socket.py =================================================================== --- branches/Release_2_2maint/jython/Lib/test/test_socket.py 2008-02-27 05:20:45 UTC (rev 4183) +++ branches/Release_2_2maint/jython/Lib/test/test_socket.py 2008-02-27 12:52:31 UTC (rev 4184) @@ -602,13 +602,24 @@ ThreadedUDPSocketTest.__init__(self, methodName=methodName) def testSendtoAndRecv(self): - # Testing sendto() and Recv() over UDP + # Testing sendto() and recv() over UDP msg = self.serv.recv(len(MSG)) self.assertEqual(msg, MSG) def _testSendtoAndRecv(self): self.cli.sendto(MSG, 0, (HOST, PORT)) + def testSendtoAndRecvTimeoutMode(self): + # Need to test again in timeout mode, which follows + # a different code path + self.serv.settimeout(10) + msg = self.serv.recv(len(MSG)) + self.assertEqual(msg, MSG) + + def _testSendtoAndRecvTimeoutMode(self): + self.cli.settimeout(10) + self.cli.sendto(MSG, 0, (HOST, PORT)) + def testRecvFrom(self): # Testing recvfrom() over UDP msg, addr = self.serv.recvfrom(len(MSG)) @@ -617,6 +628,17 @@ def _testRecvFrom(self): self.cli.sendto(MSG, 0, (HOST, PORT)) + def testRecvFromTimeoutMode(self): + # Need to test again in timeout mode, which follows + # a different code path + self.serv.settimeout(10) + msg, addr = self.serv.recvfrom(len(MSG)) + self.assertEqual(msg, MSG) + + def _testRecvFromTimeoutMode(self): + self.cli.settimeout(10) + self.cli.sendto(MSG, 0, (HOST, PORT)) + def testSendtoEightBitSafe(self): # This test is necessary because java only supports signed bytes msg = self.serv.recv(len(EIGHT_BIT_MSG)) @@ -625,6 +647,17 @@ def _testSendtoEightBitSafe(self): self.cli.sendto(EIGHT_BIT_MSG, 0, (HOST, PORT)) + def testSendtoEightBitSafeTimeoutMode(self): + # Need to test again in timeout mode, which follows + # a different code path + self.serv.settimeout(10) + msg = self.serv.recv(len(EIGHT_BIT_MSG)) + self.assertEqual(msg, EIGHT_BIT_MSG) + + def _testSendtoEightBitSafeTimeoutMode(self): + self.cli.settimeout(10) + self.cli.sendto(EIGHT_BIT_MSG, 0, (HOST, PORT)) + class BasicSocketPairTest(SocketPairTest): def __init__(self, methodName='runTest'): @@ -645,6 +678,7 @@ self.assertEqual(msg, MSG) class NonBlockingTCPServerTests(SocketTCPTest): + def testSetBlocking(self): # Testing whether set blocking works self.serv.setblocking(0) @@ -672,7 +706,6 @@ pass else: self.fail("Error trying to do non-blocking accept.") - class NonBlockingTCPTests(ThreadedTCPSocketTest): @@ -1022,6 +1055,7 @@ self.fail("accept() returned success when we did not expect it") class TCPClientTimeoutTest(unittest.TestCase): + def testClientTimeout(self): cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) cli.settimeout(0.1) @@ -1076,6 +1110,7 @@ self.assert_(issubclass(socket.timeout, socket.error)) class TestJythonExceptions(unittest.TestCase): + def setUp(self): self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2008-05-24 14:34:08
|
Revision: 4441 http://jython.svn.sourceforge.net/jython/?rev=4441&view=rev Author: amak Date: 2008-05-24 07:34:05 -0700 (Sat, 24 May 2008) Log Message: ----------- Fix for bug 1033 http://bugs.jython.org/issue1033 Added a unittest for non-7-bit strings. Modified Paths: -------------- branches/Release_2_2maint/jython/Lib/test/test_zlib.py branches/Release_2_2maint/jython/Lib/zlib.py Modified: branches/Release_2_2maint/jython/Lib/test/test_zlib.py =================================================================== --- branches/Release_2_2maint/jython/Lib/test/test_zlib.py 2008-05-22 20:10:01 UTC (rev 4440) +++ branches/Release_2_2maint/jython/Lib/test/test_zlib.py 2008-05-24 14:34:05 UTC (rev 4441) @@ -40,6 +40,11 @@ self.assertEqual(zlib.adler32("", 1), 1) ##self.assertEqual(zlib.adler32("", 432), 432) + def test_adler32_non7bit(self): + # Introduced on jython to test non-7-bit strings + self.assertEqual(zlib.adler32("\x80", 1), 8454273) + self.assertEqual(zlib.adler32("\x3b3", 1), 11206767) + def assertEqual32(self, seen, expected): # 32-bit values masked -- checksums on 32- vs 64- bit machines # This is important if bit 31 (0x08000000L) is set. Modified: branches/Release_2_2maint/jython/Lib/zlib.py =================================================================== --- branches/Release_2_2maint/jython/Lib/zlib.py 2008-05-22 20:10:01 UTC (rev 4440) +++ branches/Release_2_2maint/jython/Lib/zlib.py 2008-05-24 14:34:05 UTC (rev 4441) @@ -27,11 +27,11 @@ Z_FINISH = 4 _valid_flush_modes = (Z_FINISH,) -def adler32(string, value=1): +def adler32(s, value=1): if value != 1: raise ValueError, "adler32 only support start value of 1" checksum = Adler32() - checksum.update(String.getBytes(string)) + checksum.update(String.getBytes(s, 'iso-8859-1')) return Long(checksum.getValue()).intValue() def crc32(string, value=0): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2008-06-01 22:17:18
|
Revision: 4494 http://jython.svn.sourceforge.net/jython/?rev=4494&view=rev Author: amak Date: 2008-06-01 15:17:16 -0700 (Sun, 01 Jun 2008) Log Message: ----------- - Checking in support for socket options, as many as are supported on java. Unsupported options are documented in dummy test cases. Will update wiki to document options that are supported(includes SO_REUSEADDR, TCP_NODELAY, SO_OOBINLINE, SO_BROADCAST, SO_KEEPALIVE, SO_LINGER, etc). - Split jython specific test cases between TCP and UDP; some are shared, some are unique to either protocol. - Some whitespace cleanup Modified Paths: -------------- branches/Release_2_2maint/jython/Lib/socket.py branches/Release_2_2maint/jython/Lib/test/test_socket.py Modified: branches/Release_2_2maint/jython/Lib/socket.py =================================================================== --- branches/Release_2_2maint/jython/Lib/socket.py 2008-06-01 19:50:00 UTC (rev 4493) +++ branches/Release_2_2maint/jython/Lib/socket.py 2008-06-01 22:17:16 UTC (rev 4494) @@ -12,6 +12,7 @@ AMAK: 20050527: added socket timeouts AMAK: 20070515: Added non-blocking (asynchronous) support AMAK: 20070515: Added client-side SSL support +AMAK: 20080513: Added support for options """ _defaulttimeout = None @@ -19,6 +20,7 @@ import errno import jarray import string +import struct import sys import threading import time @@ -122,7 +124,6 @@ return error(errno.EWOULDBLOCK, 'The socket operation could not complete without blocking') def _map_exception(exc, circumstance=ALL): -# print "Mapping exception: %s" % exc try: mapped_exception = _exception_map[(exc.__class__, circumstance)] mapped_exception.java_exception = exc @@ -140,6 +141,38 @@ SHUT_WR = 1 SHUT_RDWR = 2 +__all__ = [ 'AF_INET', 'SOCK_DGRAM', 'SOCK_RAW', + 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET', + 'SO_BROADCAST', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE', + 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY' + 'SocketType', 'error', 'herror', 'gaierror', 'timeout', + 'getfqdn', 'gethostbyaddr', 'gethostbyname', 'gethostname', + 'socket', 'getaddrinfo', 'getdefaulttimeout', 'setdefaulttimeout', + 'has_ipv6', 'htons', 'htonl', 'ntohs', 'ntohl', + 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR', + ] + +AF_INET = 2 + +SOCK_DGRAM = 1 +SOCK_STREAM = 2 +SOCK_RAW = 3 # not supported +SOCK_RDM = 4 # not supported +SOCK_SEQPACKET = 5 # not supported + +SOL_SOCKET = 0xFFFF + +SO_BROADCAST = 1 +SO_KEEPALIVE = 2 +SO_LINGER = 4 +SO_OOBINLINE = 8 +SO_RCVBUF = 16 +SO_REUSEADDR = 32 +SO_SNDBUF = 64 +SO_TIMEOUT = 128 + +TCP_NODELAY = 256 + class _nio_impl: timeout = None @@ -155,12 +188,6 @@ count = self.jchannel.write(bytebuf) return count - def _setreuseaddress(self, flag): - self.jsocket.setReuseAddress(flag) - - def _getreuseaddress(self, flag): - return self.jsocket.getReuseAddress() - def getpeername(self): return (self.jsocket.getInetAddress().getHostName(), self.jsocket.getPort() ) @@ -174,29 +201,32 @@ self._timeout_millis = int(timeout*1000) self.jsocket.setSoTimeout(self._timeout_millis) - def close1(self): - self.jsocket.close() + def getsockopt(self, option): + if self.options.has_key(option): + result = getattr(self.jsocket, "get%s" % self.options[option])() + if option == SO_LINGER: + if result == -1: + enabled, linger_time = 0, 0 + else: + enabled, linger_time = 1, result + return struct.pack('ii', enabled, linger_time) + return result + else: + raise error(errno.ENOPROTOOPT, "Option not supported on socket(%s): %d" % (str(self.jsocket), option)) - def close2(self): - self.jchannel.close() + def setsockopt(self, option, value): + if self.options.has_key(option): + if option == SO_LINGER: + values = struct.unpack('ii', value) + self.jsocket.setSoLinger(*values) + else: + getattr(self.jsocket, "set%s" % self.options[option])(value) + else: + raise error(errno.ENOPROTOOPT, "Option not supported on socket(%s): %d" % (str(self.jsocket), option)) - def close3(self): - if not self.jsocket.isClosed(): - self.jsocket.close() + def close(self): + self.jsocket.close() - def close4(self): - if not self.jsocket.isClosed(): - if hasattr(self.jsocket, 'shutdownInput') and not self.jsocket.isInputShutdown(): - self.jsocket.shutdownInput() - if hasattr(self.jsocket, 'shutdownOutput') and not self.jsocket.isOutputShutdown(): - self.jsocket.shutdownOutput() - self.jsocket.close() - - close = close1 -# close = close2 -# close = close3 -# close = close4 - def shutdownInput(self): self.jsocket.shutdownInput() @@ -210,6 +240,17 @@ class _client_socket_impl(_nio_impl): + options = { + SO_KEEPALIVE: 'KeepAlive', + SO_LINGER: 'SoLinger', + SO_OOBINLINE: 'OOBInline', + SO_RCVBUF: 'ReceiveBufferSize', + SO_REUSEADDR: 'ReuseAddress', + SO_SNDBUF: 'SendBufferSize', + SO_TIMEOUT: 'SoTimeout', + TCP_NODELAY: 'TcpNoDelay', + } + def __init__(self, socket=None): if socket: self.jchannel = socket.getChannel() @@ -221,7 +262,8 @@ self.port = None self.jsocket = self.jchannel.socket() - def bind(self, host, port): + def bind(self, host, port, reuse_addr): + self.jsocket.setReuseAddress(reuse_addr) self.jsocket.bind(java.net.InetSocketAddress(host, port)) def connect(self, host, port): @@ -237,6 +279,12 @@ class _server_socket_impl(_nio_impl): + options = { + SO_RCVBUF: 'ReceiveBufferSize', + SO_REUSEADDR: 'ReuseAddress', + SO_TIMEOUT: 'SoTimeout', + } + def __init__(self, host, port, backlog, reuse_addr): self.jchannel = java.nio.channels.ServerSocketChannel.open() self.jsocket = self.jchannel.socket() @@ -244,7 +292,7 @@ bindaddr = java.net.InetSocketAddress(host, port) else: bindaddr = java.net.InetSocketAddress(port) - self._setreuseaddress(reuse_addr) + self.jsocket.setReuseAddress(reuse_addr) self.jsocket.bind(bindaddr, backlog) def accept(self): @@ -261,6 +309,14 @@ class _datagram_socket_impl(_nio_impl): + options = { + SO_BROADCAST: 'Broadcast', + SO_RCVBUF: 'ReceiveBufferSize', + SO_REUSEADDR: 'ReuseAddress', + SO_SNDBUF: 'SendBufferSize', + SO_TIMEOUT: 'SoTimeout', + } + def __init__(self, port=None, address=None, reuse_addr=0): self.jchannel = java.nio.channels.DatagramChannel.open() self.jsocket = self.jchannel.socket() @@ -269,8 +325,8 @@ local_address = java.net.InetSocketAddress(address, port) else: local_address = java.net.InetSocketAddress(port) + self.jsocket.setReuseAddress(reuse_addr) self.jsocket.bind(local_address) - self._setreuseaddress(reuse_addr) def connect(self, host, port): self.jchannel.connect(java.net.InetSocketAddress(host, port)) @@ -335,9 +391,9 @@ byte_array = jarray.zeros(num_bytes, 'b') byte_buf = java.nio.ByteBuffer.wrap(byte_array) source_address = self.jchannel.receive(byte_buf) - byte_buf.flip() ; bytes_read = byte_buf.remaining() if source_address is None and not self.jchannel.isBlocking(): raise would_block_error() + byte_buf.flip() ; bytes_read = byte_buf.remaining() if bytes_read < num_bytes: byte_array = byte_array[:bytes_read] return_data = byte_array.tostring() @@ -358,25 +414,6 @@ else: return self._do_receive_nio(0, num_bytes, flags) -__all__ = [ 'AF_INET', 'SO_REUSEADDR', 'SOCK_DGRAM', 'SOCK_RAW', - 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET', - 'SocketType', 'error', 'herror', 'gaierror', 'timeout', - 'getfqdn', 'gethostbyaddr', 'gethostbyname', 'gethostname', - 'socket', 'getaddrinfo', 'getdefaulttimeout', 'setdefaulttimeout', - 'has_ipv6', 'htons', 'htonl', 'ntohs', 'ntohl', - 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR', - ] - -AF_INET = 2 - -SOCK_DGRAM = 1 -SOCK_STREAM = 2 -SOCK_RAW = 3 # not supported -SOCK_RDM = 4 # not supported -SOCK_SEQPACKET = 5 # not supported -SOL_SOCKET = 0xFFFF -SO_REUSEADDR = 4 - def _gethostbyaddr(name): # This is as close as I can get; at least the types are correct... addresses = java.net.InetAddress.getAllByName(gethostbyname(name)) @@ -485,6 +522,11 @@ reference_count = 0 close_lock = threading.Lock() + def __init__(self): + self.pending_options = { + SO_REUSEADDR: 0, + } + def gettimeout(self): return self.timeout @@ -510,9 +552,33 @@ def getblocking(self): return self.mode == MODE_BLOCKING + def setsockopt(self, level, optname, value): + if level != SOL_SOCKET: return + try: + if self.sock_impl: + self.sock_impl.setsockopt(optname, value) + else: + self.pending_options[optname] = value + except java.lang.Exception, jlx: + raise _map_exception(jlx) + + def getsockopt(self, level, optname): + if level != SOL_SOCKET: return + try: + if self.sock_impl: + return self.sock_impl.getsockopt(optname) + else: + return self.pending_options.get(optname, None) + except java.lang.Exception, jlx: + raise _map_exception(jlx) + def _config(self): assert self.mode in _permitted_modes - if self.sock_impl: self.sock_impl.config(self.mode, self.timeout) + if self.sock_impl: + self.sock_impl.config(self.mode, self.timeout) + for k in self.pending_options.keys(): + if k != SO_REUSEADDR: + self.sock_impl.setsockopt(k, self.pending_options[k]) def getchannel(self): if not self.sock_impl: @@ -539,8 +605,10 @@ ostream = None local_addr = None server = 0 - reuse_addr = 0 + def __init__(self): + _nonblocking_api_mixin.__init__(self) + def bind(self, addr): assert not self.sock_impl assert not self.local_addr @@ -557,7 +625,7 @@ host, port = self.local_addr else: host, port = "", 0 - self.sock_impl = _server_socket_impl(host, port, backlog, self.reuse_addr) + self.sock_impl = _server_socket_impl(host, port, backlog, self.pending_options[SO_REUSEADDR]) self._config() except java.lang.Exception, jlx: raise _map_exception(jlx) @@ -572,7 +640,7 @@ if not new_sock: raise would_block_error() cliconn = _tcpsocket() - cliconn.reuse_addr = new_sock.jsocket.getReuseAddress() + cliconn.pending_options[SO_REUSEADDR] = new_sock.jsocket.getReuseAddress() cliconn.sock_impl = new_sock cliconn._setup() return cliconn, new_sock.getpeername() @@ -590,10 +658,9 @@ assert not self.sock_impl host, port = self._get_host_port(addr) self.sock_impl = _client_socket_impl() - self.sock_impl._setreuseaddress(self.reuse_addr) if self.local_addr: # Has the socket been bound to a local address? bind_host, bind_port = self.local_addr - self.sock_impl.bind(bind_host, bind_port) + self.sock_impl.bind(bind_host, bind_port, self.pending_options[SO_REUSEADDR]) self._config() # Configure timeouts, etc, now that the socket exists self.sock_impl.connect(host, port) except java.lang.Exception, jlx: @@ -675,15 +742,7 @@ return (host, port) except java.lang.Exception, jlx: raise _map_exception(jlx) - - def setsockopt(self, level, optname, value): - if optname == SO_REUSEADDR: - self.reuse_addr = value - def getsockopt(self, level, optname): - if optname == SO_REUSEADDR: - return self.reuse_addr - def shutdown(self, how): assert how in (SHUT_RD, SHUT_WR, SHUT_RDWR) assert self.sock_impl @@ -706,17 +765,18 @@ class _udpsocket(_nonblocking_api_mixin): + sock_impl = None + addr = None + def __init__(self): - self.sock_impl = None - self.addr = None - self.reuse_addr = 0 + _nonblocking_api_mixin.__init__(self) def bind(self, addr): try: assert not self.sock_impl host, port = _unpack_address_tuple(addr) host_address = java.net.InetAddress.getByName(host) - self.sock_impl = _datagram_socket_impl(port, host_address, reuse_addr = self.reuse_addr) + self.sock_impl = _datagram_socket_impl(port, host_address, self.pending_options[SO_REUSEADDR]) self._config() except java.lang.Exception, jlx: raise _map_exception(jlx) @@ -784,6 +844,7 @@ raise _map_exception(jlx) def recv(self, num_bytes, flags=None): + if not self.sock_impl: raise error(errno.ENOTCONN, "Socket is not connected") try: return self.sock_impl.recv(num_bytes, flags) except java.lang.Exception, jlx: @@ -817,23 +878,6 @@ except java.lang.Exception, jlx: raise _map_exception(jlx) - def setsockopt(self, level, optname, value): - try: - if optname == SO_REUSEADDR: - self.reuse_addr = value -# self.sock._setreuseaddress(value) - except java.lang.Exception, jlx: - raise _map_exception(jlx) - - def getsockopt(self, level, optname): - try: - if optname == SO_REUSEADDR: - return self.sock_impl._getreuseaddress() - else: - return None - except java.lang.Exception, jlx: - raise _map_exception(jlx) - _socketmethods = ( 'bind', 'connect', 'connect_ex', 'fileno', 'listen', 'getpeername', 'getsockname', 'getsockopt', 'setsockopt', Modified: branches/Release_2_2maint/jython/Lib/test/test_socket.py =================================================================== --- branches/Release_2_2maint/jython/Lib/test/test_socket.py 2008-06-01 19:50:00 UTC (rev 4493) +++ branches/Release_2_2maint/jython/Lib/test/test_socket.py 2008-06-01 22:17:16 UTC (rev 4494) @@ -8,12 +8,13 @@ import test_support import errno +import Queue +import select import socket -import select +import struct +import sys import time import thread, threading -import Queue -import sys from weakref import proxy from StringIO import StringIO @@ -493,6 +494,148 @@ sock.close() self.assertRaises(socket.error, sock.send, "spam") +class TestSocketOptions(unittest.TestCase): + + def setUp(self): + self.test_udp = self.test_tcp_client = self.test_tcp_server = 0 + + def _testSetAndGetOption(self, sock, option, values): + for expected_value in values: + sock.setsockopt(socket.SOL_SOCKET, option, expected_value) + retrieved_value = sock.getsockopt(socket.SOL_SOCKET, option) + self.failUnlessEqual(retrieved_value, expected_value, \ + "Retrieved option(%s) value %s != %s(value set)" % (option, retrieved_value, expected_value)) + + def _testUDPOption(self, option, values): + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self._testSetAndGetOption(sock, option, values) + # now bind the socket i.e. cause the implementation socket to be created + sock.bind( (HOST, PORT) ) + self.failUnlessEqual(sock.getsockopt(socket.SOL_SOCKET, option), values[-1], \ + "Option value '%s'='%s' did not propagate to implementation socket" % (option, values[-1]) ) + self._testSetAndGetOption(sock, option, values) + finally: + sock.close() + + def _testTCPClientOption(self, option, values): + try: + # First listen on a server socket, so that the connection won't be refused. + server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_sock.bind( (HOST, PORT) ) + server_sock.listen() + # Now do the tests + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._testSetAndGetOption(sock, option, values) + # now connect the socket i.e. cause the implementation socket to be created + # First bind, so that the SO_REUSEADDR setting propagates + sock.bind( (HOST, PORT+1) ) + sock.connect( (HOST, PORT) ) + self.failUnlessEqual(sock.getsockopt(socket.SOL_SOCKET, option), values[-1], \ + "Option value '%s'='%s' did not propagate to implementation socket" % (option, values[-1])) + self._testSetAndGetOption(sock, option, values) + finally: + server_sock.close() + sock.close() + + def _testTCPServerOption(self, option, values): + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._testSetAndGetOption(sock, option, values) + # now bind and listen on the socket i.e. cause the implementation socket to be created + sock.bind( (HOST, PORT) ) + sock.listen() + self.failUnlessEqual(sock.getsockopt(socket.SOL_SOCKET, option), values[-1], \ + "Option value '%s'='%s' did not propagate to implementation socket" % (option, values[-1])) + self._testSetAndGetOption(sock, option, values) + finally: + sock.close() + + def _testOption(self, option, values): + for flag, func in [ + (self.test_udp, self._testUDPOption), + (self.test_tcp_server, self._testTCPServerOption), + (self.test_tcp_client, self._testTCPClientOption), + ]: + if flag: + func(option, values) + else: + try: + func(option, values) + except socket.error, se: + self.failUnlessEqual(se[0], errno.ENOPROTOOPT, "Wrong errno from unsupported option exception: %d" % se[0]) + except Exception, x: + self.fail("Wrong exception raised from unsupported option: %s" % str(x)) + else: + self.fail("Setting unsupported option should have raised an exception") + + def testSO_BROADCAST(self): + self.test_udp = 1 ; + self._testOption(socket.SO_BROADCAST, [0, 1]) + + def testSO_KEEPALIVE(self): + self.test_tcp_client = 1 + self._testOption(socket.SO_KEEPALIVE, [0, 1]) + + def testSO_LINGER(self): + self.test_tcp_client = 1 + off = struct.pack('ii', 0, 0) + on_2_seconds = struct.pack('ii', 1, 2) + self._testOption(socket.SO_LINGER, [off, on_2_seconds]) + + def testSO_OOBINLINE(self): + self.test_tcp_client = 1 + self._testOption(socket.SO_OOBINLINE, [0, 1]) + + def testSO_RCVBUF(self): + self.test_udp = 1 + self.test_tcp_client = 1 + self.test_tcp_server = 1 + self._testOption(socket.SO_RCVBUF, [1024, 4096, 16384]) + + def testSO_REUSEADDR(self): + self.test_udp = 1 + self.test_tcp_client = 1 + self.test_tcp_server = 1 + self._testOption(socket.SO_REUSEADDR, [0, 1]) + + def testSO_SNDBUF(self): + self.test_udp = 1 + self.test_tcp_client = 1 + self._testOption(socket.SO_SNDBUF, [1024, 4096, 16384]) + + def testSO_TIMEOUT(self): + self.test_udp = 1 + self.test_tcp_client = 1 + self.test_tcp_server = 1 + self._testOption(socket.SO_TIMEOUT, [0, 1, 1000]) + + def testTCP_NODELAY(self): + self.test_tcp_client = 1 + self._testOption(socket.TCP_NODELAY, [0, 1]) + +class AsYetUnsupportedOptions: + + def testSO_ACCEPTCONN(self): pass + def testSO_DEBUG(self): pass + def testSO_DONTROUTE(self): pass + def testSO_ERROR(self): pass + 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 + # http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6402335 + pass + def testSO_RCVLOWAT(self): pass + def testSO_RCVTIMEO(self): pass + def testSO_REUSEPORT(self): + # not yet supported on java + # http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6432031 + pass + def testSO_SNDLOWAT(self): pass + def testSO_SNDTIMEO(self): pass + def testSO_TYPE(self): pass + def testSO_USELOOPBACK(self): pass + class BasicTCPTest(SocketConnectedTest): def __init__(self, methodName='runTest'): @@ -1109,12 +1252,8 @@ self.assert_(issubclass(socket.gaierror, socket.error)) self.assert_(issubclass(socket.timeout, socket.error)) -class TestJythonExceptions(unittest.TestCase): +class TestJythonExceptionsShared: - def setUp(self): - self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - def tearDown(self): self.s.close() self.s = None @@ -1127,33 +1266,6 @@ except Exception, x: self.fail("Get host name for non-existent host raised wrong exception: %s" % x) - def testConnectionRefused(self): - try: - # This port should not be open at this time - self.s.connect( (HOST, PORT) ) - except socket.error, se: - self.failUnlessEqual(se[0], errno.ECONNREFUSED) - except Exception, x: - self.fail("Connection to non-existent host/port raised wrong exception: %s" % x) - else: - self.fail("Socket (%s,%s) should not have been listening at this time" % (HOST, PORT)) - - def testBindException(self): - # First bind to the target port - self.s.bind( (HOST, PORT) ) - self.s.listen() - try: - # And then try to bind again - t = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - t.bind( (HOST, PORT) ) - t.listen() - except socket.error, se: - self.failUnlessEqual(se[0], errno.EADDRINUSE) - except Exception, x: - self.fail("Binding to already bound host/port raised wrong exception: %s" % x) - else: - self.fail("Binding to already bound host/port should have raised exception") - def testUnresolvedAddress(self): try: self.s.connect( ('non.existent.server', PORT) ) @@ -1173,14 +1285,16 @@ self.fail("Send on unconnected socket raised wrong exception: %s" % x) else: self.fail("Send on unconnected socket raised exception") + + def testSocketNotBound(self): try: result = self.s.recv(1024) except socket.error, se: self.failUnlessEqual(se[0], errno.ENOTCONN) except Exception, x: - self.fail("Receive on unconnected socket raised wrong exception: %s" % x) + self.fail("Receive on unbound socket raised wrong exception: %s" % x) else: - self.fail("Receive on unconnected socket raised exception") + self.fail("Receive on unbound socket raised exception") def testClosedSocket(self): self.s.close() @@ -1202,45 +1316,99 @@ except socket.error, se: self.failUnlessEqual(se[0], errno.EBADF) +class TestJythonTCPExceptions(TestJythonExceptionsShared, unittest.TestCase): + + def setUp(self): + self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + def testConnectionRefused(self): + try: + # This port should not be open at this time + self.s.connect( (HOST, PORT) ) + except socket.error, se: + self.failUnlessEqual(se[0], errno.ECONNREFUSED) + except Exception, x: + self.fail("Connection to non-existent host/port raised wrong exception: %s" % x) + else: + self.fail("Socket (%s,%s) should not have been listening at this time" % (HOST, PORT)) + + def testBindException(self): + # First bind to the target port + self.s.bind( (HOST, PORT) ) + self.s.listen() + try: + # And then try to bind again + t = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + t.bind( (HOST, PORT) ) + t.listen() + except socket.error, se: + self.failUnlessEqual(se[0], errno.EADDRINUSE) + except Exception, x: + self.fail("Binding to already bound host/port raised wrong exception: %s" % x) + else: + self.fail("Binding to already bound host/port should have raised exception") + +class TestJythonUDPExceptions(TestJythonExceptionsShared, unittest.TestCase): + + def setUp(self): + self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + def testBindException(self): + # First bind to the target port + self.s.bind( (HOST, PORT) ) + try: + # And then try to bind again + t = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + t.bind( (HOST, PORT) ) + except socket.error, se: + self.failUnlessEqual(se[0], errno.EADDRINUSE) + except Exception, x: + self.fail("Binding to already bound host/port raised wrong exception: %s" % x) + else: + self.fail("Binding to already bound host/port should have raised exception") + class TestAddressParameters: - def testBindNonTupleEndpointRaisesTypeError(self): - try: - self.socket.bind(HOST, PORT) - except TypeError: - pass - else: - self.fail("Illegal non-tuple bind address did not raise TypeError") + def testBindNonTupleEndpointRaisesTypeError(self): + try: + self.socket.bind(HOST, PORT) + except TypeError: + pass + else: + self.fail("Illegal non-tuple bind address did not raise TypeError") - def testConnectNonTupleEndpointRaisesTypeError(self): - try: - self.socket.connect(HOST, PORT) - except TypeError: - pass - else: - self.fail("Illegal non-tuple connect address did not raise TypeError") + def testConnectNonTupleEndpointRaisesTypeError(self): + try: + self.socket.connect(HOST, PORT) + except TypeError: + pass + else: + self.fail("Illegal non-tuple connect address did not raise TypeError") - def testConnectExNonTupleEndpointRaisesTypeError(self): - try: - self.socket.connect_ex(HOST, PORT) - except TypeError: - pass - else: - self.fail("Illegal non-tuple connect address did not raise TypeError") + def testConnectExNonTupleEndpointRaisesTypeError(self): + try: + self.socket.connect_ex(HOST, PORT) + except TypeError: + pass + else: + self.fail("Illegal non-tuple connect address did not raise TypeError") class TestTCPAddressParameters(unittest.TestCase, TestAddressParameters): - def setUp(self): - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + def setUp(self): + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) class TestUDPAddressParameters(unittest.TestCase, TestAddressParameters): - def setUp(self): - self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + def setUp(self): + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) def test_main(): tests = [ GeneralModuleTests, + TestSocketOptions, BasicTCPTest, TCPTimeoutTest, TCPClientTimeoutTest, @@ -1263,9 +1431,11 @@ if hasattr(socket, "socketpair"): tests.append(BasicSocketPairTest) if sys.platform[:4] == 'java': - tests.append(TestJythonExceptions) + tests.append(TestJythonTCPExceptions) + tests.append(TestJythonUDPExceptions) suites = [unittest.makeSuite(klass, 'test') for klass in tests] test_support.run_suite(unittest.TestSuite(suites)) if __name__ == "__main__": test_main() + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2008-06-02 00:25:38
|
Revision: 4498 http://jython.svn.sourceforge.net/jython/?rev=4498&view=rev Author: amak Date: 2008-06-01 17:25:36 -0700 (Sun, 01 Jun 2008) Log Message: ----------- Merging back some important changes from trunk. Modified Paths: -------------- branches/Release_2_2maint/jython/Lib/socket.py branches/Release_2_2maint/jython/Lib/test/test_socket.py Modified: branches/Release_2_2maint/jython/Lib/socket.py =================================================================== --- branches/Release_2_2maint/jython/Lib/socket.py 2008-06-02 00:12:57 UTC (rev 4497) +++ branches/Release_2_2maint/jython/Lib/socket.py 2008-06-02 00:25:36 UTC (rev 4498) @@ -87,36 +87,36 @@ _exception_map = { -# (<javaexception>, <circumstance>) : lambda: <code that raises the python equivalent> +# (<javaexception>, <circumstance>) : lambda: <code that raises the python equivalent>, or None to stub out as unmapped -(java.io.IOException, ALL) : error(errno.ECONNRESET, 'Software caused connection abort'), -(java.io.InterruptedIOException, ALL) : timeout('timed out'), +(java.io.IOException, ALL) : lambda: error(errno.ECONNRESET, 'Software caused connection abort'), +(java.io.InterruptedIOException, ALL) : lambda: timeout('timed out'), -(java.net.BindException, ALL) : error(errno.EADDRINUSE, 'Address already in use'), -(java.net.ConnectException, ALL) : error(errno.ECONNREFUSED, 'Connection refused'), -(java.net.NoRouteToHostException, ALL) : error(-1, 'Unmapped exception: java.net.NoRouteToHostException'), -(java.net.PortUnreachableException, ALL) : error(-1, 'Unmapped exception: java.net.PortUnreachableException'), -(java.net.ProtocolException, ALL) : error(-1, 'Unmapped exception: java.net.ProtocolException'), -(java.net.SocketException, ALL) : error(-1, 'Unmapped exception: java.net.SocketException'), -(java.net.SocketTimeoutException, ALL) : timeout('timed out'), -(java.net.UnknownHostException, ALL) : gaierror(errno.EGETADDRINFOFAILED, 'getaddrinfo failed'), +(java.net.BindException, ALL) : lambda: error(errno.EADDRINUSE, 'Address already in use'), +(java.net.ConnectException, ALL) : lambda: error(errno.ECONNREFUSED, 'Connection refused'), +(java.net.NoRouteToHostException, ALL) : None, +(java.net.PortUnreachableException, ALL) : None, +(java.net.ProtocolException, ALL) : None, +(java.net.SocketException, ALL) : None, +(java.net.SocketTimeoutException, ALL) : lambda: timeout('timed out'), +(java.net.UnknownHostException, ALL) : lambda: gaierror(errno.EGETADDRINFOFAILED, 'getaddrinfo failed'), -(java.nio.channels.AlreadyConnectedException, ALL) : error(errno.EISCONN, 'Socket is already connected'), -(java.nio.channels.AsynchronousCloseException, ALL) : error(-1, 'Unmapped exception: java.nio.AsynchronousCloseException'), -(java.nio.channels.CancelledKeyException, ALL) : error(-1, 'Unmapped exception: java.nio.CancelledKeyException'), -(java.nio.channels.ClosedByInterruptException, ALL) : error(-1, 'Unmapped exception: java.nio.ClosedByInterruptException'), -(java.nio.channels.ClosedChannelException, ALL) : error(errno.EPIPE, 'Socket closed'), -(java.nio.channels.ClosedSelectorException, ALL) : error(-1, 'Unmapped exception: java.nio.ClosedSelectorException'), -(java.nio.channels.ConnectionPendingException, ALL) : error(-1, 'Unmapped exception: java.nio.ConnectionPendingException'), -(java.nio.channels.IllegalBlockingModeException, ALL) : error(-1, 'Unmapped exception: java.nio.IllegalBlockingModeException'), -(java.nio.channels.IllegalSelectorException, ALL) : error(-1, 'Unmapped exception: java.nio.IllegalSelectorException'), -(java.nio.channels.NoConnectionPendingException, ALL) : error(-1, 'Unmapped exception: java.nio.NoConnectionPendingException'), -(java.nio.channels.NonReadableChannelException, ALL) : error(-1, 'Unmapped exception: java.nio.NonReadableChannelException'), -(java.nio.channels.NonWritableChannelException, ALL) : error(-1, 'Unmapped exception: java.nio.NonWritableChannelException'), -(java.nio.channels.NotYetBoundException, ALL) : error(-1, 'Unmapped exception: java.nio.NotYetBoundException'), -(java.nio.channels.NotYetConnectedException, ALL) : error(-1, 'Unmapped exception: java.nio.NotYetConnectedException'), -(java.nio.channels.UnresolvedAddressException, ALL) : gaierror(errno.EGETADDRINFOFAILED, 'getaddrinfo failed'), -(java.nio.channels.UnsupportedAddressTypeException, ALL) : error(-1, 'Unmapped exception: java.nio.UnsupportedAddressTypeException'), +(java.nio.channels.AlreadyConnectedException, ALL) : lambda: error(errno.EISCONN, 'Socket is already connected'), +(java.nio.channels.AsynchronousCloseException, ALL) : None, +(java.nio.channels.CancelledKeyException, ALL) : None, +(java.nio.channels.ClosedByInterruptException, ALL) : None, +(java.nio.channels.ClosedChannelException, ALL) : lambda: error(errno.EPIPE, 'Socket closed'), +(java.nio.channels.ClosedSelectorException, ALL) : None, +(java.nio.channels.ConnectionPendingException, ALL) : None, +(java.nio.channels.IllegalBlockingModeException, ALL) : None, +(java.nio.channels.IllegalSelectorException, ALL) : None, +(java.nio.channels.NoConnectionPendingException, ALL) : None, +(java.nio.channels.NonReadableChannelException, ALL) : None, +(java.nio.channels.NonWritableChannelException, ALL) : None, +(java.nio.channels.NotYetBoundException, ALL) : None, +(java.nio.channels.NotYetConnectedException, ALL) : None, +(java.nio.channels.UnresolvedAddressException, ALL) : lambda: gaierror(errno.EGETADDRINFOFAILED, 'getaddrinfo failed'), +(java.nio.channels.UnsupportedAddressTypeException, ALL) : None, } @@ -124,12 +124,14 @@ return error(errno.EWOULDBLOCK, 'The socket operation could not complete without blocking') def _map_exception(exc, circumstance=ALL): - try: - mapped_exception = _exception_map[(exc.__class__, circumstance)] - mapped_exception.java_exception = exc - return mapped_exception - except KeyError: - return error(-1, 'Unmapped java exception: <%s:%s>' % (exc.toString(), circumstance)) +# print "Mapping exception: %s" % exc + mapped_exception = _exception_map.get((exc.__class__, circumstance)) + if mapped_exception: + exception = mapped_exception() + else: + exception = error(-1, 'Unmapped exception: %s' % exc) + exception.java_exception = exc + return exception MODE_BLOCKING = 'block' MODE_NONBLOCKING = 'nonblock' @@ -228,10 +230,16 @@ self.jsocket.close() def shutdownInput(self): - self.jsocket.shutdownInput() + try: + self.jsocket.shutdownInput() + except java.lang.Exception, jlx: + raise _map_exception(jlx) def shutdownOutput(self): - self.jsocket.shutdownOutput() + try: + self.jsocket.shutdownOutput() + except java.lang.Exception, jlx: + raise _map_exception(jlx) def getchannel(self): return self.jchannel @@ -306,7 +314,7 @@ # In timeout mode now new_cli_sock = self.jsocket.accept() return _client_socket_impl(new_cli_sock) - + class _datagram_socket_impl(_nio_impl): options = { Modified: branches/Release_2_2maint/jython/Lib/test/test_socket.py =================================================================== --- branches/Release_2_2maint/jython/Lib/test/test_socket.py 2008-06-02 00:12:57 UTC (rev 4497) +++ branches/Release_2_2maint/jython/Lib/test/test_socket.py 2008-06-02 00:25:36 UTC (rev 4498) @@ -124,8 +124,8 @@ self.client_ready.wait() def _tearDown(self): + self.done.wait() self.__tearDown() - self.done.wait() if not self.queue.empty(): msg = self.queue.get() @@ -909,11 +909,27 @@ def _testConnectWithLocalBind(self): # Testing blocking connect with local bind - self.cli.settimeout(1) - self.cli.bind( (HOST, PORT-1) ) - self.cli.connect((HOST, PORT)) + cli_port = PORT - 1 + while True: + # Keep trying until a local port is available + self.cli.settimeout(1) + self.cli.bind( (HOST, cli_port) ) + try: + self.cli.connect((HOST, PORT)) + break + except socket.error, se: + # cli_port is in use (maybe in TIME_WAIT state from a + # previous test run). reset the client socket and try + # again + self.failUnlessEqual(se[0], errno.EADDRINUSE) + try: + self.cli.close() + except socket.error: + pass + self.clientSetUp() + cli_port -= 1 bound_host, bound_port = self.cli.getsockname() - self.failUnlessEqual(bound_port, PORT-1) + self.failUnlessEqual(bound_port, cli_port) def testRecvData(self): # Testing non-blocking recv @@ -1438,4 +1454,3 @@ if __name__ == "__main__": test_main() - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <am...@us...> - 2008-07-15 21:05:15
|
Revision: 4946 http://jython.svn.sourceforge.net/jython/?rev=4946&view=rev Author: amak Date: 2008-07-15 14:05:06 -0700 (Tue, 15 Jul 2008) Log Message: ----------- 1. Support for the <broadcast> address for both server and client UDP sockets 2. Upgrade of the getaddrinfo function to support AF_INET6 and AF_UNSPEC. Still some more work to do on IPv6 support. Modified Paths: -------------- branches/Release_2_2maint/jython/Lib/socket.py branches/Release_2_2maint/jython/Lib/test/test_socket.py Added Paths: ----------- branches/Release_2_2maint/jython/Lib/test/test_socket_ipv6.py Modified: branches/Release_2_2maint/jython/Lib/socket.py =================================================================== --- branches/Release_2_2maint/jython/Lib/socket.py 2008-07-15 19:27:53 UTC (rev 4945) +++ branches/Release_2_2maint/jython/Lib/socket.py 2008-07-15 21:05:06 UTC (rev 4946) @@ -143,8 +143,8 @@ SHUT_WR = 1 SHUT_RDWR = 2 -__all__ = [ 'AF_INET', 'SOCK_DGRAM', 'SOCK_RAW', - 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET', +__all__ = ['AF_UNSPEC', 'AF_INET', 'AF_INET6', 'AI_PASSIVE', 'SOCK_DGRAM', + 'SOCK_RAW', 'SOCK_RDM', 'SOCK_SEQPACKET', 'SOCK_STREAM', 'SOL_SOCKET', 'SO_BROADCAST', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE', 'SO_RCVBUF', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_TIMEOUT', 'TCP_NODELAY', 'SocketType', 'error', 'herror', 'gaierror', 'timeout', @@ -154,8 +154,12 @@ 'SHUT_RD', 'SHUT_WR', 'SHUT_RDWR', ] +AF_UNSPEC = 0 AF_INET = 2 +AF_INET6 = 23 +AI_PASSIVE=1 + SOCK_DGRAM = 1 SOCK_STREAM = 2 SOCK_RAW = 3 # not supported @@ -364,8 +368,7 @@ bytes_sent = self.jchannel.send(byte_buf, socket_address) return bytes_sent - def sendto(self, byte_array, address, flags): - host, port = _unpack_address_tuple(address) + def sendto(self, byte_array, host, port, flags): socket_address = java.net.InetSocketAddress(host, port) if self.mode == MODE_TIMEOUT: return self._do_send_net(byte_array, socket_address, flags) @@ -422,6 +425,10 @@ else: return self._do_receive_nio(0, num_bytes, flags) +# Name and address functions + +has_ipv6 = 1 + def _gethostbyaddr(name): # This is as close as I can get; at least the types are correct... addresses = java.net.InetAddress.getAllByName(gethostbyname(name)) @@ -487,11 +494,29 @@ else: return _udpsocket() -def getaddrinfo(host, port, family=0, socktype=SOCK_STREAM, proto=0, flags=0): - return ( (AF_INET, socktype, 0, "", (gethostbyname(host), port)), ) +def getaddrinfo(host, port, family=None, socktype=None, proto=None, flags=None): + try: + if not family in [AF_INET, AF_INET6, AF_UNSPEC]: + raise NotSupportedError() + filter_fns = [] + filter_fns.append({ + AF_INET: lambda x: isinstance(x, java.net.Inet4Address), + 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 + 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.class] + # TODO: Include flowinfo and scopeid in a 4-tuple for IPv6 addresses + results.append( (family, socktype, proto, a.getCanonicalHostName(), (a.getHostAddress(), port)) ) + return results + except java.lang.Exception, jlx: + raise _map_exception(jlx) -has_ipv6 = 1 - def getnameinfo(sock_addr, flags): raise NotImplementedError("getnameinfo not yet supported on jython.") @@ -598,13 +623,22 @@ def _get_jsocket(self): return self.sock_impl.jsocket -def _unpack_address_tuple(address_tuple): +def _unpack_address_tuple(address_tuple, for_tx=False): + # 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 type(address_tuple) is not type( () ) \ or type(address_tuple[0]) is not type("") \ or type(address_tuple[1]) is not type(0): raise TypeError(error_message) - return address_tuple[0], address_tuple[1] + hostname = address_tuple[0].strip() + if hostname == "<broadcast>": + if for_tx: + hostname = "255.255.255.255" + else: + hostname = "0.0.0.0" + return hostname, address_tuple[1] class _tcpsocket(_nonblocking_api_mixin): @@ -621,7 +655,7 @@ assert not self.sock_impl assert not self.local_addr # Do the address format check - host, port = _unpack_address_tuple(addr) + _unpack_address_tuple(addr) self.local_addr = addr def listen(self, backlog=50): @@ -630,7 +664,7 @@ assert not self.sock_impl self.server = 1 if self.local_addr: - host, port = 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[SO_REUSEADDR]) @@ -667,7 +701,7 @@ 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 = self.local_addr + bind_host, bind_port = _unpack_address_tuple(self.local_addr) self.sock_impl.bind(bind_host, bind_port, self.pending_options[SO_REUSEADDR]) self._config() # Configure timeouts, etc, now that the socket exists self.sock_impl.connect(host, port) @@ -819,8 +853,9 @@ if not self.sock_impl: self.sock_impl = _datagram_socket_impl() self._config() + host, port = _unpack_address_tuple(addr, True) byte_array = java.lang.String(data).getBytes('iso-8859-1') - result = self.sock_impl.sendto(byte_array, addr, flags) + result = self.sock_impl.sendto(byte_array, host, port, flags) return result except java.lang.Exception, jlx: raise _map_exception(jlx) Modified: branches/Release_2_2maint/jython/Lib/test/test_socket.py =================================================================== --- branches/Release_2_2maint/jython/Lib/test/test_socket.py 2008-07-15 19:27:53 UTC (rev 4945) +++ branches/Release_2_2maint/jython/Lib/test/test_socket.py 2008-07-15 21:05:06 UTC (rev 4946) @@ -801,6 +801,21 @@ self.cli.settimeout(10) self.cli.sendto(EIGHT_BIT_MSG, 0, (HOST, PORT)) +class UDPBroadcastTest(ThreadedUDPSocketTest): + + def setUp(self): + self.serv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.serv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + def testBroadcast(self): + self.serv.bind( ("<broadcast>", PORT) ) + msg = self.serv.recv(len(EIGHT_BIT_MSG)) + self.assertEqual(msg, EIGHT_BIT_MSG) + + def _testBroadcast(self): + self.cli.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + self.cli.sendto(EIGHT_BIT_MSG, ("<broadcast>", PORT) ) + class BasicSocketPairTest(SocketPairTest): def __init__(self, methodName='runTest'): @@ -1449,6 +1464,10 @@ if sys.platform[:4] == 'java': tests.append(TestJythonTCPExceptions) tests.append(TestJythonUDPExceptions) + # 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) suites = [unittest.makeSuite(klass, 'test') for klass in tests] test_support.run_suite(unittest.TestSuite(suites)) Added: branches/Release_2_2maint/jython/Lib/test/test_socket_ipv6.py =================================================================== --- branches/Release_2_2maint/jython/Lib/test/test_socket_ipv6.py (rev 0) +++ branches/Release_2_2maint/jython/Lib/test/test_socket_ipv6.py 2008-07-15 21:05:06 UTC (rev 4946) @@ -0,0 +1,61 @@ +""" +AMAK: 20080714: +This module contains IPv6 related tests. +I have placed them in a separate module from the rest of the socket tests, because +1. The tests pass on my IPv6 enabled windows box, on JVMs >= 1.5 +2. They don't pass on Ubuntu, on any JVM version. The equivalent java code to lookup + IPv6 addresses doesn't work either, so I need to research how to configure Ubuntu + so that java.net.Inet6Address.getAllByName() actually returns IPv6 addresses. +3. I don't want to include these tests with the standard socket tests until + the network status and config of the various test environments, e.g. Mac, BSD, Solaris, etc, + are known. +""" + +import unittest +import test_support + +import socket + +class NameLookupTests(unittest.TestCase): + + def testLocalhostV4Lookup(self): + results = socket.getaddrinfo("localhost", 80, socket.AF_INET, socket.SOCK_STREAM, 0) + self.failUnlessEqual(len(results), 1) + self.failUnlessEqual('127.0.0.1', results[0][4][0]) + + def testRemoteV4Lookup(self): + results = socket.getaddrinfo("www.python.org", 80, socket.AF_INET, socket.SOCK_STREAM, 0) + self.failUnlessEqual(len(results), 1) + self.failUnlessEqual('82.94.237.218', results[0][4][0]) + + def testLocalhostV6Lookup(self): + results = socket.getaddrinfo("localhost", 80, socket.AF_INET6, socket.SOCK_STREAM, 0) + self.failUnlessEqual(len(results), 1) + self.failUnlessEqual('0:0:0:0:0:0:0:1', results[0][4][0]) + + def testRemoteV6Lookup(self): + # This test relies on those nice Dutch folks at sixxs.org keeping the same IPv6 address for their gateway + results = socket.getaddrinfo("www.python.org.sixxs.org", 80, socket.AF_INET6, socket.SOCK_STREAM, 0) + self.failUnlessEqual(len(results), 1) + self.failUnlessEqual('2001:838:2:1:2a0:24ff:feab:3b53', results[0][4][0]) + + def testLocalhostV4AndV6Lookup(self): + results = socket.getaddrinfo("localhost", 80, socket.AF_UNSPEC, socket.SOCK_STREAM, 0) + self.failUnlessEqual(len(results), 2) + + def testRemoteV4AndV6Lookup(self): + # Need a remote host with both IPv4 and IPv6 addresses; pass for now + pass + + def testAI_PASSIVE(self): + pass + +def test_main(): + tests = [ + NameLookupTests, + ] + suites = [unittest.makeSuite(klass, 'test') for klass in tests] + test_support.run_suite(unittest.TestSuite(suites)) + +if __name__ == "__main__": + test_main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |