- Typo
[pyopenssl]
- Better exception handling
- Prevent 100% cpu usage when ssl handshake is slow
This commit is contained in:
junglecow 2006-11-28 01:02:31 +00:00
parent 3c77ffd406
commit 5cb241b8c0
2 changed files with 84 additions and 40 deletions

View File

@ -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'):

View File

@ -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:"
if this.is_numtoolarge(e):
# warn, but ignore this exception
log.warning("Recv: OpenSSL: asn1enc: first num too large (ignored)")
else:
log.warning("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)")
else:
raise
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))
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()
raise SSLWrapper.Error(this.sock or this.sslobj, e)
except OpenSSL.SSL.Error, e:
print "Send: Caught OpenSSL.SSL.Error:"
if this.is_numtoolarge(e):
# warn, but ignore this exception
log.warning("Send: OpenSSL: asn1enc: first num too large (ignored)")
else:
log.warning("Send: Caught OpenSSL.SSL.Error:")
traceback.print_exc()
print "Current Stack:"
traceback.print_stack()
if this.is_numtoolarge(e):
# warn, but ignore this exception
log.warning("Send: OpenSSL: asn1enc: first num too large (ignoring)")
else:
raise
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'