From: <am...@us...> - 2008-10-18 19:03:47
|
Revision: 5461 http://jython.svn.sourceforge.net/jython/?rev=5461&view=rev Author: amak Date: 2008-10-18 19:03:40 +0000 (Sat, 18 Oct 2008) Log Message: ----------- 1. Fixed a bug whereby timeouts were not being honoured when recv()ing (http://bugs.jython.com/issue1154) 2. Fixed another (previously unreported) bug that resulted in an exception when switching from non-blocking to timeout mode. 3. Back-ported some of pjenvey's changes relating to getaddrinfo, non-blocking connects and file buffer sizes. 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-10-18 01:58:29 UTC (rev 5460) +++ branches/Release_2_2maint/jython/Lib/socket.py 2008-10-18 19:03:40 UTC (rev 5461) @@ -6,8 +6,6 @@ XXX Restrictions: - Only INET sockets -- No asynchronous behavior -- No socket options - Can't do a very good gethostbyaddr() right... AMAK: 20050527: added socket timeouts AMAK: 20070515: Added non-blocking (asynchronous) support @@ -201,16 +199,6 @@ timeout = None mode = MODE_BLOCKING - def read(self, buf): - bytebuf = java.nio.ByteBuffer.wrap(buf) - count = self.jchannel.read(bytebuf) - return count - - def write(self, buf): - bytebuf = java.nio.ByteBuffer.wrap(buf) - count = self.jchannel.write(bytebuf) - return count - def getpeername(self): return (self.jsocket.getInetAddress().getHostName(), self.jsocket.getPort() ) @@ -221,6 +209,7 @@ if self.mode == MODE_NONBLOCKING: self.jchannel.configureBlocking(0) if self.mode == MODE_TIMEOUT: + self.jchannel.configureBlocking(1) self._timeout_millis = int(timeout*1000) self.jsocket.setSoTimeout(self._timeout_millis) @@ -310,6 +299,36 @@ def finish_connect(self): return self.jchannel.finishConnect() + def _do_read_net(self, buf): + # Need two separate implementations because the java.nio APIs do not support timeouts + return self.jsocket.getInputStream().read(buf) + + def _do_read_nio(self, buf): + bytebuf = java.nio.ByteBuffer.wrap(buf) + count = self.jchannel.read(bytebuf) + return count + + def _do_write_net(self, buf): + self.jsocket.getOutputStream().write(buf) + return len(buf) + + def _do_write_nio(self, buf): + bytebuf = java.nio.ByteBuffer.wrap(buf) + count = self.jchannel.write(bytebuf) + return count + + def read(self, buf): + if self.mode == MODE_TIMEOUT: + return self._do_read_net(buf) + else: + return self._do_read_nio(buf) + + def write(self, buf): + if self.mode == MODE_TIMEOUT: + return self._do_write_net(buf) + else: + return self._do_write_nio(buf) + class _server_socket_impl(_nio_impl): options = { @@ -515,10 +534,10 @@ else: return _udpsocket() -def getaddrinfo(host, port, family=None, socktype=None, proto=0, flags=None): +def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=None): try: if not family in [AF_INET, AF_INET6, AF_UNSPEC]: - raise NotSupportedError() + raise gaierror(errno.EIO, 'ai_family not supported') filter_fns = [] filter_fns.append({ AF_INET: lambda x: isinstance(x, java.net.Inet4Address), @@ -736,9 +755,12 @@ def connect_ex(self, addr): "This signifies a client socket" - self._do_connect(addr) + if not self.sock_impl: + self._do_connect(addr) if self.sock_impl.finish_connect(): self._setup() + if self.mode == MODE_NONBLOCKING: + return errno.EISCONN return 0 return errno.EINPROGRESS @@ -775,6 +797,8 @@ if self.sock_impl.jchannel.isConnectionPending(): self.sock_impl.jchannel.finishConnect() numwritten = self.sock_impl.write(s) + if numwritten == 0 and self.mode == MODE_NONBLOCKING: + raise would_block_error() return numwritten except java.lang.Exception, jlx: raise _map_exception(jlx) @@ -861,8 +885,11 @@ self._do_connect(addr) def connect_ex(self, addr): - self._do_connect(addr) + if not self.sock_impl: + self._do_connect(addr) if self.sock_impl.finish_connect(): + if self.mode == MODE_NONBLOCKING: + return errno.EISCONN return 0 return errno.EINPROGRESS @@ -1151,7 +1178,7 @@ self._rbuf = "" while True: left = size - buf_len - recv_size = max(self._rbufsize, left) + recv_size = min(self._rbufsize, left) data = self._sock.recv(recv_size) if not data: break Modified: branches/Release_2_2maint/jython/Lib/test/test_socket.py =================================================================== --- branches/Release_2_2maint/jython/Lib/test/test_socket.py 2008-10-18 01:58:29 UTC (rev 5460) +++ branches/Release_2_2maint/jython/Lib/test/test_socket.py 2008-10-18 19:03:40 UTC (rev 5461) @@ -292,6 +292,22 @@ 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 @@ -670,6 +686,16 @@ def _testRecv(self): self.serv_conn.send(MSG) + def testRecvTimeoutMode(self): + # Do this test in timeout mode, because the code path is different + self.cli_conn.settimeout(10) + msg = self.cli_conn.recv(1024) + self.assertEqual(msg, MSG) + + def _testRecvTimeoutMode(self): + self.serv_conn.settimeout(10) + self.serv_conn.send(MSG) + def testOverFlowRecv(self): # Testing receive in chunks over TCP seg1 = self.cli_conn.recv(len(MSG) - 3) @@ -1226,14 +1252,14 @@ bufsize = 2 # Exercise the buffering code -class TCPTimeoutTest(SocketTCPTest): +class TCPServerTimeoutTest(SocketTCPTest): - def testTCPTimeout(self): + def testAcceptTimeout(self): def raise_timeout(*args, **kwargs): self.serv.settimeout(1.0) self.serv.accept() self.failUnlessRaises(socket.timeout, raise_timeout, - "Error generating a timeout exception (TCP)") + "TCP socket accept failed to generate a timeout exception (TCP)") def testTimeoutZero(self): ok = False @@ -1249,9 +1275,9 @@ if not ok: self.fail("accept() returned success when we did not expect it") -class TCPClientTimeoutTest(unittest.TestCase): +class TCPClientTimeoutTest(SocketTCPTest): - def testClientTimeout(self): + def testConnectTimeout(self): cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM) cli.settimeout(0.1) host = '192.168.192.168' @@ -1266,8 +1292,45 @@ socket.timeout. This tries to connect to %s in the assumption that it isn't used, but if it is on your network this failure is bogus.''' % host) - + def testRecvTimeout(self): + def raise_timeout(*args, **kwargs): + cli_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + cli_sock.connect( (HOST, PORT) ) + cli_sock.settimeout(1) + cli_sock.recv(1024) + self.failUnlessRaises(socket.timeout, raise_timeout, + "TCP socket recv failed to generate a timeout exception (TCP)") + # Disable this test, but leave it present for documentation purposes + # socket timeouts only work for read and accept, not for write + # http://java.sun.com/j2se/1.4.2/docs/api/java/net/SocketTimeoutException.html + def estSendTimeout(self): + def raise_timeout(*args, **kwargs): + cli_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + cli_sock.connect( (HOST, PORT) ) + # First fill the socket + cli_sock.settimeout(1) + sent = 0 + while True: + bytes_sent = cli_sock.send(MSG) + sent += bytes_sent + self.failUnlessRaises(socket.timeout, raise_timeout, + "TCP socket send failed to generate a timeout exception (TCP)") + + def testSwitchModes(self): + cli_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + cli_sock.connect( (HOST, PORT) ) + # set non-blocking mode + cli_sock.setblocking(0) + # then set timeout mode + cli_sock.settimeout(1) + try: + cli_sock.send(MSG) + except Exception, x: + self.fail("Switching mode from non-blocking to timeout raised exception: %s" % x) + else: + pass + # # AMAK: 20070307 # Corrected the superclass of UDPTimeoutTest @@ -1487,7 +1550,7 @@ TestSupportedOptions, TestUnsupportedOptions, BasicTCPTest, - TCPTimeoutTest, + TCPServerTimeoutTest, TCPClientTimeoutTest, TestExceptions, TestInvalidUsage, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |