Fix stanza clobbering when replying to errors.
If a stanza handler raised an exception, the exception was processed and replied by the modified stanza, not a stanza with the original content. A copy is now made before handler processing, and if an exception occurs it is the copy that processes the exception using the original content.
This commit is contained in:
		@@ -97,7 +97,7 @@ class Message(RootStanza):
 | 
			
		||||
            clear -- Indicates if existing content should be removed
 | 
			
		||||
                     before replying. Defaults to True.
 | 
			
		||||
        """
 | 
			
		||||
        StanzaBase.reply(self)
 | 
			
		||||
        StanzaBase.reply(self, clear)
 | 
			
		||||
        if self['type'] == 'groupchat':
 | 
			
		||||
            self['to'] = self['to'].bare
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -944,13 +944,14 @@ class XMLStream(object):
 | 
			
		||||
            func -- The event handler to execute.
 | 
			
		||||
            args -- Arguments to the event handler.
 | 
			
		||||
        """
 | 
			
		||||
        orig = copy.copy(args[0])
 | 
			
		||||
        try:
 | 
			
		||||
            func(*args)
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            error_msg = 'Error processing event handler: %s'
 | 
			
		||||
            log.exception(error_msg % str(func))
 | 
			
		||||
            if hasattr(args[0], 'exception'):
 | 
			
		||||
                args[0].exception(e)
 | 
			
		||||
            if hasattr(orig, 'exception'):
 | 
			
		||||
                orig.exception(e)
 | 
			
		||||
 | 
			
		||||
    def _event_runner(self):
 | 
			
		||||
        """
 | 
			
		||||
@@ -973,6 +974,7 @@ class XMLStream(object):
 | 
			
		||||
 | 
			
		||||
                etype, handler = event[0:2]
 | 
			
		||||
                args = event[2:]
 | 
			
		||||
                orig = copy.copy(args[0])
 | 
			
		||||
 | 
			
		||||
                if etype == 'stanza':
 | 
			
		||||
                    try:
 | 
			
		||||
@@ -980,7 +982,7 @@ class XMLStream(object):
 | 
			
		||||
                    except Exception as e:
 | 
			
		||||
                        error_msg = 'Error processing stream handler: %s'
 | 
			
		||||
                        log.exception(error_msg % handler.name)
 | 
			
		||||
                        args[0].exception(e)
 | 
			
		||||
                        orig.exception(e)
 | 
			
		||||
                elif etype == 'schedule':
 | 
			
		||||
                    try:
 | 
			
		||||
                        log.debug('Scheduled event: %s' % args)
 | 
			
		||||
@@ -989,6 +991,7 @@ class XMLStream(object):
 | 
			
		||||
                        log.exception('Error processing scheduled task')
 | 
			
		||||
                elif etype == 'event':
 | 
			
		||||
                    func, threaded, disposable = handler
 | 
			
		||||
                    orig = copy.copy(args[0])
 | 
			
		||||
                    try:
 | 
			
		||||
                        if threaded:
 | 
			
		||||
                            x = threading.Thread(
 | 
			
		||||
@@ -1001,8 +1004,8 @@ class XMLStream(object):
 | 
			
		||||
                    except Exception as e:
 | 
			
		||||
                        error_msg = 'Error processing event handler: %s'
 | 
			
		||||
                        log.exception(error_msg % str(func))
 | 
			
		||||
                        if hasattr(args[0], 'exception'):
 | 
			
		||||
                            args[0].exception(e)
 | 
			
		||||
                        if hasattr(orig, 'exception'):
 | 
			
		||||
                            orig.exception(e)
 | 
			
		||||
                elif etype == 'quit':
 | 
			
		||||
                    log.debug("Quitting event runner thread")
 | 
			
		||||
                    return False
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,35 @@ class TestStreamExceptions(SleekTest):
 | 
			
		||||
        sys.excepthook = sys.__excepthook__
 | 
			
		||||
        self.stream_close()
 | 
			
		||||
 | 
			
		||||
    def testExceptionReply(self):
 | 
			
		||||
        """Test that raising an exception replies with the original stanza."""
 | 
			
		||||
 | 
			
		||||
        def message(msg):
 | 
			
		||||
            msg.reply()
 | 
			
		||||
            msg['body'] = 'Body changed'
 | 
			
		||||
            raise XMPPError(clear=False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        sys.excepthook = lambda *args, **kwargs: None
 | 
			
		||||
        self.stream_start()
 | 
			
		||||
        self.xmpp.add_event_handler('message', message)
 | 
			
		||||
 | 
			
		||||
        self.recv("""
 | 
			
		||||
          <message>
 | 
			
		||||
            <body>This is going to cause an error.</body>
 | 
			
		||||
          </message>
 | 
			
		||||
        """)
 | 
			
		||||
 | 
			
		||||
        self.send("""
 | 
			
		||||
          <message type="error">
 | 
			
		||||
            <body>This is going to cause an error.</body>
 | 
			
		||||
            <error type="cancel" code="500">
 | 
			
		||||
              <undefined-condition
 | 
			
		||||
                  xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
 | 
			
		||||
            </error>
 | 
			
		||||
          </message>
 | 
			
		||||
        """)
 | 
			
		||||
 | 
			
		||||
    def testXMPPErrorException(self):
 | 
			
		||||
        """Test raising an XMPPError exception."""
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user