From 5cb241b8c0e8cdb491050c321706be957c17e042 Mon Sep 17 00:00:00 2001 From: junglecow Date: Tue, 28 Nov 2006 01:02:31 +0000 Subject: [PATCH] [trunk] - Typo [pyopenssl] - Better exception handling - Prevent 100% cpu usage when ssl handshake is slow --- src/common/xmpp/client_nb.py | 4 +- src/common/xmpp/transports_nb.py | 120 +++++++++++++++++++++---------- 2 files changed, 84 insertions(+), 40 deletions(-) diff --git a/src/common/xmpp/client_nb.py b/src/common/xmpp/client_nb.py index 40753ef4e..7db5a1eeb 100644 --- a/src/common/xmpp/client_nb.py +++ b/src/common/xmpp/client_nb.py @@ -74,10 +74,8 @@ class NBCommonClient(CommonClient): ''' Called on disconnection. Calls disconnect handlers and cleans things up. ''' self.connected='' self.DEBUG(self.DBG,'Disconnect detected','stop') - self.disconnect_handlers.reverse() - for i in self.disconnect_handlers: + for i in reversed(self.disconnect_handlers): i() - self.disconnect_handlers.reverse() if self.__dict__.has_key('NonBlockingRoster'): self.NonBlockingRoster.PlugOut() if self.__dict__.has_key('NonBlockingBind'): diff --git a/src/common/xmpp/transports_nb.py b/src/common/xmpp/transports_nb.py index 4a8d270b2..d5865db6e 100644 --- a/src/common/xmpp/transports_nb.py +++ b/src/common/xmpp/transports_nb.py @@ -24,6 +24,7 @@ from transports import * import sys import os import errno +import time import traceback import thread @@ -67,14 +68,55 @@ def torf(cond, tv, fv): if cond: return tv return fv -class SSLWrapper: - def Error(Exception): - parent = Exception - def __init__(this, *args): - this.parent.__init__(this, *args) +def gattr(obj, attr, default=None): + try: + return getattr(obj, attr) + except: + return default - def __init__(this, sslobj): +class SSLWrapper: + class Error(IOError): + def __init__(this, sock=None, exc=None, errno=None, strerror=None, peer=None): + this.parent = IOError + + this.exc = exc + + this.errno = errno or gattr(this.exc, 'errno', 0) + this.strerror = strerror or gattr(this.exc, 'strerror') or gattr(this.exc, 'args') + + this.parent.__init__(this, errno, strerror) + this.peer = peer + this.exc_name = None + + if this.exc is not None: + this.exc_name = str(this.exc.__class__) + this.exc_args = gattr(this.exc, 'args') + this.exc_str = str(this.exc) + this.exc_repr = repr(this.exc) + + if this.peer is None and sock is not None: + try: + ppeer = this.obj.getpeername() + if len(ppeer) == 2 and isinstance(ppeer[0], basestring) \ + and isinstance(ppeer[1], int): + this.peer = ppeer + except: pass + + def __str__(this): + s = str(this.__class__) + if this.peer: s += "for %s:%d" % this.peer + if this.errno is not None: s += ": [Errno: %d]" % this.errno + if this.strerror: s += " (%s)" % this.strerror + if this.exc_name: + s += ", Caused by %s" % this.exc_name + if this.exc_str: + if this.strerror: s += "(%s)" % this.exc_str + else: s += "(%s)" % this.exc_args + return s + + def __init__(this, sslobj, sock=None): this.sslobj = sslobj + this.sock = sock print "init called with", sslobj # We can return None out of this function to signal that no data is @@ -91,9 +133,9 @@ class SSLWrapper: class PyOpenSSLWrapper(SSLWrapper): '''Wrapper class for PyOpenSSL's recv() and send() methods''' - parent = SSLWrapper def __init__(this, *args): + this.parent = SSLWrapper this.parent.__init__(this, *args) def is_numtoolarge(this, e): @@ -111,20 +153,19 @@ class PyOpenSSLWrapper(SSLWrapper): except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: log.debug("Recv: " + repr(e)) except OpenSSL.SSL.SysCallError, e: - log.error("Got OpenSSL.SSL.SysCallError: " + repr(e)) + log.error("Recv: Got OpenSSL.SSL.SysCallError: " + repr(e)) traceback.print_exc() - raise SSLWrapper.Error(('OpenSSL.SSL.SysCallError', e.args)) + raise SSLWrapper.Error(this.sock or this.sslobj, e) except OpenSSL.SSL.Error, e: - "Recv: Caught OpenSSL.SSL.Error:" - traceback.print_exc() - print "Current Stack:" - traceback.print_stack() if this.is_numtoolarge(e): - # print an error but ignore this exception - log.warning("Recv: OpenSSL: asn1enc: first num too large (eaten)") + # warn, but ignore this exception + log.warning("Recv: OpenSSL: asn1enc: first num too large (ignored)") else: - raise - + log.warning("Recv: Caught OpenSSL.SSL.Error:") + traceback.print_exc() + print "Current Stack:" + traceback.print_stack() + raise SSLWrapper.Error(this.sock or this.sslobj, e) return retval def send(this, data, flags=None): @@ -134,23 +175,28 @@ class PyOpenSSLWrapper(SSLWrapper): else: return this.sslobj.send(data, flags) except (OpenSSL.SSL.WantReadError, OpenSSL.SSL.WantWriteError), e: log.debug("Send: " + repr(e)) - except OpenSSL.SSL.Error, e: - print "Send: Caught OpenSSL.SSL.Error:" + time.sleep(0.1) # prevent 100% CPU usage + except OpenSSL.SSL.SysCallError, e: + log.error("Recv: Got OpenSSL.SSL.SysCallError: " + repr(e)) traceback.print_exc() - print "Current Stack:" - traceback.print_stack() + raise SSLWrapper.Error(this.sock or this.sslobj, e) + except OpenSSL.SSL.Error, e: if this.is_numtoolarge(e): # warn, but ignore this exception - log.warning("Send: OpenSSL: asn1enc: first num too large (ignoring)") + log.warning("Send: OpenSSL: asn1enc: first num too large (ignored)") else: - raise + log.warning("Send: Caught OpenSSL.SSL.Error:") + traceback.print_exc() + print "Current Stack:" + traceback.print_stack() + raise SSLWrapper.Error(this.sock or this.sslobj, e) return 0 class StdlibSSLWrapper(SSLWrapper): '''Wrapper class for Python's socket.ssl read() and write() methods''' - parent = SSLWrapper def __init__(this, *args): + this.parent = SSLWrapper this.parent.__init__(this, *args) def recv(this, bufsize, flags=None): @@ -165,7 +211,7 @@ class StdlibSSLWrapper(SSLWrapper): print "Current Stack:" traceback.print_stack() if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE): - raise + raise SSLWrapper.Error(this.sock or this.sslobj, e) return retval @@ -180,7 +226,7 @@ class StdlibSSLWrapper(SSLWrapper): print "Current Stack:" traceback.print_stack() if e.args[0] not in (socket.SSL_ERROR_WANT_READ, socket.SSL_ERROR_WANT_WRITE): - raise + raise SSLWrapper.Error(this.sock or this.sslobj, e) return 0 class NonBlockingTcp(PlugIn, IdleObject): @@ -305,12 +351,10 @@ class NonBlockingTcp(PlugIn, IdleObject): self.remove_timeout() self._owner.disconnected() self.idlequeue.unplug_idle(self.fd) - try: - self._sock.shutdown(socket.SHUT_RDWR) - self._sock.close() - except: - traceback.print_exc() - # socket is already closed + try: self._sock.shutdown(socket.SHUT_RDWR) + except: traceback.print_exc() + try: self._sock.close() + except: traceback.print_exc() # socket descriptor cannot be (un)plugged anymore self.fd = -1 if self.on_disconnect: @@ -374,9 +418,11 @@ class NonBlockingTcp(PlugIn, IdleObject): traceback.print_stack() errnum = ERR_OTHER errtxt = repr("socket.sslerror: " + e.args) - except SSLWrapper.Error: - errnum = ERR_OTHER - errtxt = repr(e.args) + except SSLWrapper.Error, e: + print "caught " + str(e) + errnum = gattr(e, 'errno', ERR_OTHER) + if errnum == 0: errnum = ERR_OTHER # unset, but we must put a status + errtxt = gattr(e, 'strerror') or repr(e.args) # Should we really do this? In C, recv() will happily return 0 # in nonblocking mode when there is no data waiting, and in @@ -400,7 +446,7 @@ class NonBlockingTcp(PlugIn, IdleObject): self.DEBUG(errtxt, 'error') log.error("Error: " + errtxt) if self.state >= 0: - self.disconnect() + self.pollend() return received = '' @@ -682,7 +728,7 @@ class NonBlockingTLS(PlugIn): tcpsock._sock.setblocking(False) tcpsock._sslIssuer = tcpsock._sslObj.issuer() tcpsock._sslServer = tcpsock._sslObj.server() - wrapper = StdlibSSLWrapper(tcpsock._sslObj) + wrapper = StdlibSSLWrapper(tcpsock._sslObj, tcpsock._sock) tcpsock._recv = wrapper.recv tcpsock._send = wrapper.send self.starttls='success'