Ticket #3407: xmpp-router.patch

File xmpp-router.patch, 44.3 KB (added by ralphm, 8 years ago)

Patch bomb resulting in a XMPP router plugin for twistd.

  • twisted/plugins/twisted_words.py

    diff -r 32bd907eefae -r e255d835eef1 twisted/plugins/twisted_words.py
    a b  
    2020    "A modern words server",
    2121    "words")
    2222
     23TwistedXMPPRouter = ServiceMaker(
     24    "XMPP Router",
     25    "twisted.words.xmpproutertap",
     26    "An XMPP Router server",
     27    "xmpp-router")
     28
    2329class RelayChatInterface(object):
    2430    classProvides(IPlugin, iwords.IProtocolPlugin)
    2531
  • twisted/words/protocols/jabber/client.py

    diff -r 32bd907eefae -r e255d835eef1 twisted/words/protocols/jabber/client.py
    a b  
    11# -*- test-case-name: twisted.words.test.test_jabberclient -*-
    22#
    3 # Copyright (c) 2001-2005 Twisted Matrix Laboratories.
     3# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
    44# See LICENSE for details.
    55
    66from twisted.internet import defer
     
    1717PlaintextAuthQry = xpath.internQuery("/iq/query/password")
    1818
    1919def basicClientFactory(jid, secret):
    20     a = BasicAuthenticator(jid, secret)
    21     return xmlstream.XmlStreamFactory(a)
     20    return xmlstream.XmlStreamFactory(BasicAuthenticator, jid, secret)
    2221
    2322class IQ(domish.Element):
    2423    """
     
    298297    @return: XML stream factory.
    299298    @rtype: L{xmlstream.XmlStreamFactory}
    300299    """
    301     a = XMPPAuthenticator(jid, password)
    302     return xmlstream.XmlStreamFactory(a)
     300    return xmlstream.XmlStreamFactory(XMPPAuthenticator, jid, password)
    303301
    304302
    305303
     
    339337
    340338    namespace = 'jabber:client'
    341339
    342     def __init__(self, jid, password):
    343         xmlstream.ConnectAuthenticator.__init__(self, jid.host)
     340    def __init__(self, xs, jid, password):
     341        xmlstream.ConnectAuthenticator.__init__(self, xs, jid.host)
    344342        self.jid = jid
    345343        self.password = password
    346 
    347 
    348     def associateWithStream(self, xs):
    349         """
    350         Register with the XML stream.
    351 
    352         Populates stream's list of initializers, along with their
    353         requiredness. This list is used by
    354         L{ConnectAuthenticator.initializeStream} to perform the initalization
    355         steps.
    356         """
    357         xmlstream.ConnectAuthenticator.associateWithStream(self, xs)
    358344
    359345        xs.initializers = [CheckVersionInitializer(xs)]
    360346        inits = [ (xmlstream.TLSInitiatingInitializer, False),
  • twisted/words/protocols/jabber/component.py

    diff -r 32bd907eefae -r e255d835eef1 twisted/words/protocols/jabber/component.py
    a b  
    11# -*- test-case-name: twisted.words.test.test_jabbercomponent -*-
    22#
    3 # Copyright (c) 2001-2007 Twisted Matrix Laboratories.
     3# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
    44# See LICENSE for details.
    55
    66"""
     
    2121from zope.interface import implements
    2222
    2323from twisted.application import service
    24 from twisted.internet import defer
     24from twisted.internet import defer, reactor
     25from twisted.python import log
    2526from twisted.words.xish import domish
    26 from twisted.words.protocols.jabber import ijabber, jstrports, xmlstream
     27from twisted.words.protocols.jabber import error, ijabber, jstrports, xmlstream
     28from twisted.words.protocols.jabber.jid import internJID as JID
     29
     30NS_COMPONENT_ACCEPT = 'jabber:component:accept'
    2731
    2832def componentFactory(componentid, password):
    2933    """
     
    3438    @param password: password used to authenticate to the server.
    3539    @type password: L{str}
    3640    """
    37     a = ConnectComponentAuthenticator(componentid, password)
    38     return xmlstream.XmlStreamFactory(a)
     41    return xmlstream.XmlStreamFactory(ConnectComponentAuthenticator,
     42                                      componentid, password)
     43
     44
    3945
    4046class ComponentInitiatingInitializer(object):
    4147    """
     
    6874        self.xmlstream.thisEntity = self.xmlstream.otherEntity
    6975        self._deferred.callback(None)
    7076
     77
     78
    7179class ConnectComponentAuthenticator(xmlstream.ConnectAuthenticator):
    7280    """
    7381    Authenticator to permit an XmlStream to authenticate against a Jabber
    7482    server as an external component (where the Authenticator is initiating the
    7583    stream).
    7684    """
    77     namespace = 'jabber:component:accept'
     85    namespace = NS_COMPONENT_ACCEPT
    7886
    79     def __init__(self, componentjid, password):
     87    def __init__(self, xs, componentjid, password):
    8088        """
    8189        @type componentjid: L{str}
    8290        @param componentjid: Jabber ID that this component wishes to bind to.
     
    8593        @param password: Password/secret this component uses to authenticate.
    8694        """
    8795        # Note that we are sending 'to' our desired component JID.
    88         xmlstream.ConnectAuthenticator.__init__(self, componentjid)
     96        xs.version = (0, 0)
     97        xmlstream.ConnectAuthenticator.__init__(self, xs, componentjid)
     98        xs.initializers = [ComponentInitiatingInitializer(xs)]
    8999        self.password = password
    90100
    91     def associateWithStream(self, xs):
     101
     102
     103class ListenComponentAuthenticator(xmlstream.ListenAuthenticator):
     104    """
     105    Authenticator for accepting components.
     106    """
     107    namespace = NS_COMPONENT_ACCEPT
     108
     109    def __init__(self, xs, secret):
    92110        xs.version = (0, 0)
    93         xmlstream.ConnectAuthenticator.associateWithStream(self, xs)
     111        self.secret = secret
     112        xmlstream.ListenAuthenticator.__init__(self, xs)
    94113
    95         xs.initializers = [ComponentInitiatingInitializer(xs)]
    96114
    97 class ListenComponentAuthenticator(xmlstream.Authenticator):
    98     """
    99     Placeholder for listening components.
    100     """
     115    def streamStarted(self, rootElement):
     116        xmlstream.ListenAuthenticator.streamStarted(self, rootElement)
     117
     118        if rootElement.defaultUri != self.namespace:
     119            exc = error.StreamError('invalid-namespace')
     120            self.xmlstream.sendStreamError(exc)
     121            return
     122
     123        # self.xmlstream.thisEntity is set to the address the component
     124        # wants to assume. This should probably be checked.
     125        if not self.xmlstream.thisEntity:
     126            exc = error.StreamError('improper-addressing')
     127            self.xmlstream.sendStreamError(exc)
     128            return
     129
     130        self.xmlstream.sid = 'random' # FIXME
     131
     132        self.xmlstream.sendHeader()
     133        self.xmlstream.addOnetimeObserver('/*', self.onElement)
     134
     135
     136    def onElement(self, element):
     137        if (element.uri, element.name) == (self.namespace, 'handshake'):
     138            self.onHandshake(unicode(element))
     139        else:
     140            exc = error.streamError('not-authorized')
     141            self.xmlstream.sendStreamError(exc)
     142
     143
     144    def onHandshake(self, handshake):
     145        calculatedHash = xmlstream.hashPassword(self.xmlstream.sid, self.secret)
     146        if handshake != calculatedHash:
     147            exc = error.StreamError('not-authorized', text='Invalid hash')
     148            self.xmlstream.sendStreamError(exc)
     149        else:
     150            self.xmlstream.send('<handshake/>')
     151            self.xmlstream.dispatch(self.xmlstream,
     152                                    xmlstream.STREAM_AUTHD_EVENT)
     153
     154
    101155
    102156class Service(service.Service):
    103157    """
     
    227281    client_svc = jstrports.client(strport, svc.getFactory())
    228282    client_svc.setServiceParent(svc)
    229283    return svc
     284
     285
     286
     287class RouterService(service.Service):
     288    """
     289    XMPP Server's Router Service.
     290
     291    This service connects the different components of the XMPP service and
     292    routes messages between them based on the given routing table.
     293
     294    Connected components are trusted to have correct addressing in the
     295    stanzas they offer for routing.
     296
     297    A route destination of C{None} adds a default route. Traffic for which no
     298    specific route exists, will be routed to this default route.
     299
     300    @ivar routes: Routes based on the host part of JIDs. Maps host names to the
     301                  L{EventDispatcher<utility.EventDispatcher>}s that should
     302                  receive the traffic. A key of C{None} means the default
     303                  route.
     304    @type routes: C{dict}
     305    """
     306
     307    def __init__(self):
     308        self.routes = {}
     309
     310
     311    def addRoute(self, destination, xs):
     312        """
     313        Add a new route.
     314
     315        The passed XML Stream C{xs} will have an observer for all stanzas
     316        added to route its outgoing traffic. In turn, traffic for
     317        C{destination} will be passed to this stream.
     318
     319        @param destination: Destination of the route to be added as a host name
     320                            or C{None} for the default route.
     321        @type destination: C{str} or C{NoneType}.
     322        @param xs: XML Stream to register the route for.
     323        @type xs: L{EventDispatcher<utility.EventDispatcher>}.
     324        """
     325        self.routes[destination] = xs
     326        xs.addObserver('/*', self.route)
     327
     328
     329    def removeRoute(self, destination, xs):
     330        """
     331        Remove a route.
     332
     333        @param destination: Destination of the route that should be removed.
     334        @type destination: C{str}.
     335        @param xs: XML Stream to remove the route for.
     336        @type xs: L{EventDispatcher<utility.EventDispatcher>}.
     337        """
     338        xs.removeObserver('/*', self.route)
     339        if (xs == self.routes[destination]):
     340            del self.routes[destination]
     341
     342
     343    def route(self, stanza):
     344        """
     345        Route a stanza.
     346
     347        @param stanza: The stanza to be routed.
     348        @type stanza: L{domish.Element}.
     349        """
     350        if not list(stanza.elements()):
     351            return
     352
     353        destination = JID(stanza['to'])
     354
     355        log.msg("Routing to %s: %r" % (destination.full(), stanza.toXml()))
     356
     357        if destination.host in self.routes:
     358            self.routes[destination.host].send(stanza)
     359        else:
     360            self.routes[None].send(stanza)
     361
     362
     363
     364class ComponentServer(service.Service):
     365    """
     366    XMPP Component Server service.
     367
     368    This service accepts XMPP external component connections and makes
     369    the router service route traffic for a component's bound domain
     370    to that component.
     371    """
     372
     373    logTraffic = False
     374
     375    def __init__(self, router, port=5347, secret='secret'):
     376        self.router = router
     377        self.port = port
     378        self.secret = secret
     379
     380        self.factory = xmlstream.XmlStreamServerFactory(
     381                ListenComponentAuthenticator, self.secret)
     382        self.factory.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT,
     383                                  self.makeConnection)
     384        self.factory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT,
     385                                  self.connectionInitialized)
     386
     387        self.serial = 0
     388
     389
     390    def startService(self):
     391        service.Service.startService(self)
     392        reactor.listenTCP(self.port, self.factory)
     393
     394
     395    def makeConnection(self, xs):
     396        """
     397        Called when a component connection was made.
     398
     399        This enables traffic debugging on incoming streams.
     400        """
     401        xs.serial = self.serial
     402        self.serial += 1
     403
     404        def logDataIn(buf):
     405            log.msg("RECV (%d): %r" % (xs.serial, buf))
     406
     407        def logDataOut(buf):
     408            log.msg("SEND (%d): %r" % (xs.serial, buf))
     409
     410        if self.logTraffic:
     411            xs.rawDataInFn = logDataIn
     412            xs.rawDataOutFn = logDataOut
     413
     414        xs.addObserver(xmlstream.STREAM_ERROR_EVENT, self.onError)
     415
     416
     417    def connectionInitialized(self, xs):
     418        """
     419        Called when a component has succesfully authenticated.
     420
     421        Add the component to the routing table and establish a handler
     422        for a closed connection.
     423        """
     424        destination = xs.thisEntity.host
     425
     426        self.router.addRoute(destination, xs)
     427        xs.addObserver(xmlstream.STREAM_END_EVENT, self.connectionLost, 0,
     428                                                   destination, xs)
     429
     430
     431    def onError(self, reason):
     432        log.err(reason, "Stream Error")
     433
     434
     435    def connectionLost(self, destination, xs, reason):
     436        self.router.removeRoute(destination, xs)
  • twisted/words/protocols/jabber/xmlstream.py

    diff -r 32bd907eefae -r e255d835eef1 twisted/words/protocols/jabber/xmlstream.py
    a b  
    1313
    1414from zope.interface import directlyProvides, implements
    1515
    16 from twisted.internet import defer
     16from twisted.internet import defer, protocol
    1717from twisted.internet.error import ConnectionLost
    1818from twisted.python import failure, log
    1919from twisted.words.protocols.jabber import error, ijabber, jid
     
    5757    Rules:
    5858      1. The Authenticator MUST dispatch a L{STREAM_AUTHD_EVENT} when the
    5959         stream has been completely initialized.
    60       2. The Authenticator SHOULD reset all state information when
    61          L{associateWithStream} is called.
    62       3. The Authenticator SHOULD override L{streamStarted}, and start
     60      2. The Authenticator SHOULD override L{streamStarted}, and start
    6361         initialization there.
    6462
    6563    @type xmlstream: L{XmlStream}
     
    7068           of XML stanzas.
    7169    """
    7270
    73     def __init__(self):
    74         self.xmlstream = None
     71    def __init__(self, xs):
     72        self.xmlstream = xs
    7573
    7674
    7775    def connectionMade(self):
     
    116114        self.xmlstream.version = min(self.xmlstream.version, version)
    117115
    118116
    119     def associateWithStream(self, xmlstream):
    120         """
    121         Called by the XmlStreamFactory when a connection has been made
    122         to the requested peer, and an XmlStream object has been
    123         instantiated.
    124 
    125         The default implementation just saves a handle to the new
    126         XmlStream.
    127 
    128         @type xmlstream: L{XmlStream}
    129         @param xmlstream: The XmlStream that will be passing events to this
    130                           Authenticator.
    131 
    132         """
    133         self.xmlstream = xmlstream
    134 
    135 
    136117
    137118class ConnectAuthenticator(Authenticator):
    138119    """
     
    141122
    142123    namespace = None
    143124
    144     def __init__(self, otherHost):
     125    def __init__(self, xs, otherHost):
     126        Authenticator.__init__(self, xs)
    145127        self.otherHost = otherHost
    146128
    147129
     
    240222
    241223    namespace = None
    242224
    243     def associateWithStream(self, xmlstream):
    244         """
    245         Called by the XmlStreamFactory when a connection has been made.
    246 
    247         Extend L{Authenticator.associateWithStream} to set the L{XmlStream}
    248         to be non-initiating.
    249         """
    250         Authenticator.associateWithStream(self, xmlstream)
    251         self.xmlstream.initiating = False
     225    def __init__(self, xs):
     226        xs.initiating = False
     227        Authenticator.__init__(self, xs)
    252228
    253229
    254230    def streamStarted(self, rootElement):
     
    475451
    476452    _headerSent = False     # True if the stream header has been sent
    477453
    478     def __init__(self, authenticator):
     454    def __init__(self, authenticatorClass, *args, **kwargs):
    479455        xmlstream.XmlStream.__init__(self)
    480456
    481457        self.prefixes = {NS_STREAMS: 'stream'}
    482         self.authenticator = authenticator
    483458        self.initializers = []
    484459        self.features = {}
    485 
    486         # Reset the authenticator
    487         authenticator.associateWithStream(self)
     460        self.authenticator = authenticatorClass(self, *args, **kwargs)
    488461
    489462
    490463    def _callLater(self, *args, **kwargs):
     
    639612class XmlStreamFactory(xmlstream.XmlStreamFactory):
    640613    """
    641614    Factory for Jabber XmlStream objects as a reconnecting client.
    642 
    643     Note that this differs from L{xmlstream.XmlStreamFactory} in that
    644     it generates Jabber specific L{XmlStream} instances that have
    645     authenticators.
    646615    """
    647616
    648617    protocol = XmlStream
    649618
    650     def __init__(self, authenticator):
    651         xmlstream.XmlStreamFactory.__init__(self, authenticator)
    652         self.authenticator = authenticator
     619
     620
     621class XmlStreamServerFactory(xmlstream.XmlStreamFactoryMixin,
     622                             protocol.ServerFactory):
     623    """
     624    Factory for Jabber XmlStream objects as a server.
     625    """
     626
     627    protocol = XmlStream
    653628
    654629
    655630
  • twisted/words/test/test_jabberclient.py

    diff -r 32bd907eefae -r e255d835eef1 twisted/words/test/test_jabberclient.py
    a b  
    1 # Copyright (c) 2001-2007 Twisted Matrix Laboratories.
     1# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
    22# See LICENSE for details.
    33
    44"""
     
    1212
    1313class CheckVersionInitializerTest(unittest.TestCase):
    1414    def setUp(self):
    15         a = xmlstream.Authenticator()
    16         xs = xmlstream.XmlStream(a)
     15        xs = xmlstream.XmlStream(xmlstream.Authenticator)
    1716        self.init = client.CheckVersionInitializer(xs)
    1817
    1918    def testSupported(self):
     
    3534class InitiatingInitializerHarness(object):
    3635    def setUp(self):
    3736        self.output = []
    38         self.authenticator = xmlstream.ConnectAuthenticator('example.org')
    39         self.xmlstream = xmlstream.XmlStream(self.authenticator)
     37        self.xmlstream = xmlstream.XmlStream(xmlstream.ConnectAuthenticator,
     38                                             'example.org')
    4039        self.xmlstream.send = self.output.append
    4140        self.xmlstream.connectionMade()
    4241        self.xmlstream.dataReceived("<stream:stream xmlns='jabber:client' "
     
    4847    def setUp(self):
    4948        super(IQAuthInitializerTest, self).setUp()
    5049        self.init = client.IQAuthInitializer(self.xmlstream)
    51         self.authenticator.jid = jid.JID('user@example.com/resource')
    52         self.authenticator.password = 'secret'
     50        self.xmlstream.authenticator.jid = jid.JID('user@example.com/resource')
     51        self.xmlstream.authenticator.password = 'secret'
    5352
    5453    def testBasic(self):
    5554        """
     
    158157    def setUp(self):
    159158        super(BindInitializerTest, self).setUp()
    160159        self.init = client.BindInitializer(self.xmlstream)
    161         self.authenticator.jid = jid.JID('user@example.com/resource')
     160        self.xmlstream.authenticator.jid = jid.JID('user@example.com/resource')
    162161
    163162    def testBasic(self):
    164163        """
     
    168167        """
    169168        def cb(result):
    170169            self.assertEquals(jid.JID('user@example.com/other resource'),
    171                               self.authenticator.jid)
     170                              self.xmlstream.authenticator.jid)
    172171
    173172        d = self.init.start().addCallback(cb)
    174173        iq = self.output[-1]
  • twisted/words/test/test_jabbercomponent.py

    diff -r 32bd907eefae -r e255d835eef1 twisted/words/test/test_jabbercomponent.py
    a b  
    1 # Copyright (c) 2001-2007 Twisted Matrix Laboratories.
     1# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
    22# See LICENSE for details.
    33
    44"""
     
    66"""
    77
    88import sha
     9
     10from twisted.python import failure
     11from twisted.test import proto_helpers
    912from twisted.trial import unittest
    10 
    11 from twisted.words.protocols.jabber import component
    12 from twisted.words.protocols import jabber
    13 from twisted.words.protocols.jabber import xmlstream
     13from twisted.words.protocols.jabber import component, xmlstream
     14from twisted.words.protocols.jabber.jid import JID
     15from twisted.words.xish import domish
     16from twisted.words.xish.utility import XmlPipe
    1417
    1518class DummyTransport:
    1619    def __init__(self, list):
     
    2326    def setUp(self):
    2427        self.output = []
    2528
    26         self.authenticator = xmlstream.Authenticator()
    27         self.authenticator.password = 'secret'
    28         self.xmlstream = xmlstream.XmlStream(self.authenticator)
     29        self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator)
     30        self.xmlstream.authenticator.password = 'secret'
    2931        self.xmlstream.namespace = 'test:component'
    3032        self.xmlstream.send = self.output.append
    3133        self.xmlstream.connectionMade()
     
    6668        self.authComplete = False
    6769        outlist = []
    6870
    69         ca = component.ConnectComponentAuthenticator("cjid", "secret")
    70         xs = xmlstream.XmlStream(ca)
     71        xs = xmlstream.XmlStream(component.ConnectComponentAuthenticator,
     72                                 "cjid", "secret")
    7173        xs.transport = DummyTransport(outlist)
    7274
    7375        xs.addObserver(xmlstream.STREAM_AUTHD_EVENT,
     
    8789        self.assertEquals(self.authComplete, True)
    8890
    8991
    90 class JabberServiceHarness(jabber.component.Service):
     92class JabberServiceHarness(component.Service):
    9193    def __init__(self):
    9294        self.componentConnectedFlag = False
    9395        self.componentDisconnectedFlag = False
     
    106108class TestJabberServiceManager(unittest.TestCase):
    107109    def testSM(self):
    108110        # Setup service manager and test harnes
    109         sm = jabber.component.ServiceManager("foo", "password")
     111        sm = component.ServiceManager("foo", "password")
    110112        svc = JabberServiceHarness()
    111113        svc.setServiceParent(sm)
    112114
     
    135137
    136138        # Ensure the test service harness got notified
    137139        self.assertEquals(True, svc.componentDisconnectedFlag)
     140
     141
     142
     143class RouterServiceTest(unittest.TestCase):
     144    """
     145    Tests for L{component.RouterService}.
     146    """
     147
     148    def test_addRoute(self):
     149        """
     150        Test route registration and routing on incoming stanzas.
     151        """
     152        router = component.RouterService()
     153        routed = []
     154        router.route = lambda element: routed.append(element)
     155
     156        pipe = XmlPipe()
     157        router.addRoute('example.org', pipe.sink)
     158        self.assertEquals(1, len(router.routes))
     159        self.assertEquals(pipe.sink, router.routes['example.org'])
     160
     161        element = domish.Element(('testns', 'test'))
     162        pipe.source.send(element)
     163        self.assertEquals([element], routed)
     164
     165
     166    def test_route(self):
     167        """
     168        Test routing of a message.
     169        """
     170        component1 = XmlPipe()
     171        component2 = XmlPipe()
     172        router = component.RouterService()
     173        router.addRoute('component1.example.org', component1.sink)
     174        router.addRoute('component2.example.org', component2.sink)
     175
     176        outgoing = []
     177        component2.source.addObserver('/*',
     178                                      lambda element: outgoing.append(element))
     179        stanza = domish.Element((None, 'route'))
     180        stanza['from'] = 'component1.example.org'
     181        stanza['to'] = 'component2.example.org'
     182        stanza.addElement('presence')
     183        component1.source.send(stanza)
     184        self.assertEquals([stanza], outgoing)
     185
     186
     187    def test_routeDefault(self):
     188        """
     189        Test routing of a message using the default route.
     190
     191        The default route is the one with C{None} as its key in the
     192        routing table. It is taken when there is no more specific route
     193        in the routing table that matches the stanza's destination.
     194        """
     195        component1 = XmlPipe()
     196        s2s = XmlPipe()
     197        router = component.RouterService()
     198        router.addRoute('component1.example.org', component1.sink)
     199        router.addRoute(None, s2s.sink)
     200
     201        outgoing = []
     202        s2s.source.addObserver('/*', lambda element: outgoing.append(element))
     203        stanza = domish.Element((None, 'route'))
     204        stanza['from'] = 'component1.example.org'
     205        stanza['to'] = 'example.com'
     206        stanza.addElement('presence')
     207        component1.source.send(stanza)
     208        self.assertEquals([stanza], outgoing)
     209
     210
     211
     212class ListenComponentAuthenticatorTest(unittest.TestCase):
     213    """
     214    Tests for L{component.ListenComponentAuthenticator}.
     215    """
     216
     217    def setUp(self):
     218        self.output = []
     219        authenticator = component.ListenComponentAuthenticator
     220        self.xmlstream = xmlstream.XmlStream(authenticator, 'secret')
     221        self.xmlstream.send = self.output.append
     222
     223
     224    def loseConnection(self):
     225        """
     226        Stub loseConnection because we are a transport.
     227        """
     228        self.xmlstream.connectionLost("no reason")
     229
     230
     231    def test_streamStarted(self):
     232        observers = []
     233
     234        def addOnetimeObserver(event, observerfn):
     235            observers.append((event, observerfn))
     236
     237        xs = self.xmlstream
     238        xs.addOnetimeObserver = addOnetimeObserver
     239
     240        xs.makeConnection(self)
     241        self.assertIdentical(None, xs.sid)
     242        self.assertFalse(xs._headerSent)
     243
     244        xs.dataReceived("<stream:stream xmlns='jabber:component:accept' "
     245                         "xmlns:stream='http://etherx.jabber.org/streams' "
     246                         "to='component.example.org'>")
     247        self.assertEqual((0, 0), xs.version)
     248        self.assertNotIdentical(None, xs.sid)
     249        self.assertTrue(xs._headerSent)
     250        self.assertEquals(('/*', xs.authenticator.onElement), observers[-1])
     251
     252
     253    def test_streamStartedWrongNamespace(self):
     254        """
     255        The received stream header should have a correct namespace.
     256        """
     257        streamErrors = []
     258
     259        xs = self.xmlstream
     260        xs.sendStreamError = streamErrors.append
     261        xs.makeConnection(self)
     262        xs.dataReceived("<stream:stream xmlns='jabber:client' "
     263                         "xmlns:stream='http://etherx.jabber.org/streams' "
     264                         "to='component.example.org'>")
     265        self.assertEquals(1, len(streamErrors))
     266        self.assertEquals('invalid-namespace', streamErrors[-1].condition)
     267
     268
     269    def test_streamStartedNoTo(self):
     270        streamErrors = []
     271
     272        xs = self.xmlstream
     273        xs.sendStreamError = streamErrors.append
     274        xs.makeConnection(self)
     275        xs.dataReceived("<stream:stream xmlns='jabber:component:accept' "
     276                         "xmlns:stream='http://etherx.jabber.org/streams'>")
     277        self.assertEquals(1, len(streamErrors))
     278        self.assertEquals('improper-addressing', streamErrors[-1].condition)
     279
     280
     281    def test_onElement(self):
     282        """
     283        We expect a handshake element with a hash.
     284        """
     285        handshakes = []
     286
     287        xs = self.xmlstream
     288        xs.authenticator.onHandshake = handshakes.append
     289
     290        handshake = domish.Element(('jabber:component:accept', 'handshake'))
     291        handshake.addContent('1234')
     292        xs.authenticator.onElement(handshake)
     293        self.assertEqual('1234', handshakes[-1])
     294
     295    def test_onHandshake(self):
     296        xs = self.xmlstream
     297        xs.sid = '1234'
     298        theHash = '32532c0f7dbf1253c095b18b18e36d38d94c1256'
     299        xs.authenticator.onHandshake(theHash)
     300        self.assertEqual('<handshake/>', self.output[-1])
     301
     302
     303    def test_onHandshakeWrongHash(self):
     304        streamErrors = []
     305        authd = []
     306
     307        def authenticated(self, xs):
     308            authd.append(xs)
     309
     310        xs = self.xmlstream
     311        xs.addOnetimeObserver(xmlstream.STREAM_AUTHD_EVENT, authenticated)
     312        xs.sendStreamError = streamErrors.append
     313
     314        xs.sid = '1234'
     315        theHash = '1234'
     316        xs.authenticator.onHandshake(theHash)
     317        self.assertEquals('not-authorized', streamErrors[-1].condition)
     318        self.assertEquals(0, len(authd))
     319
     320
     321
     322class ComponentServerTest(unittest.TestCase):
     323    """
     324    Tests for L{component.ComponentServer}.
     325    """
     326
     327    def setUp(self):
     328        self.router = component.RouterService()
     329        self.server = component.ComponentServer(self.router)
     330        self.xmlstream = self.server.factory.buildProtocol(None)
     331        self.xmlstream.thisEntity = JID('component.example.org')
     332
     333
     334    def test_makeConnection(self):
     335        """
     336        A new connection increases the stream serial count. No logs by default.
     337        """
     338        self.xmlstream.dispatch(self.xmlstream,
     339                                xmlstream.STREAM_CONNECTED_EVENT)
     340        self.assertEqual(0, self.xmlstream.serial)
     341        self.assertEqual(1, self.server.serial)
     342        self.assertIdentical(None, self.xmlstream.rawDataInFn)
     343        self.assertIdentical(None, self.xmlstream.rawDataOutFn)
     344
     345
     346    def test_makeConnectionLogTraffic(self):
     347        """
     348        Setting logTraffic should set up raw data loggers.
     349        """
     350        self.server.logTraffic = True
     351        self.xmlstream.dispatch(self.xmlstream,
     352                                xmlstream.STREAM_CONNECTED_EVENT)
     353        self.assertNotIdentical(None, self.xmlstream.rawDataInFn)
     354        self.assertNotIdentical(None, self.xmlstream.rawDataOutFn)
     355
     356
     357    def test_onError(self):
     358        """
     359        An observer for stream errors should trigger onError to log it.
     360        """
     361        self.xmlstream.dispatch(self.xmlstream,
     362                                xmlstream.STREAM_CONNECTED_EVENT)
     363
     364        class TestError(Exception):
     365            pass
     366
     367        reason = failure.Failure(TestError())
     368        self.xmlstream.dispatch(reason, xmlstream.STREAM_ERROR_EVENT)
     369        self.assertEqual(1, len(self.flushLoggedErrors(TestError)))
     370
     371
     372    def test_connectionInitialized(self):
     373        """
     374        Make sure a new stream is added to the routing table.
     375        """
     376        self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
     377        self.assertIn('component.example.org', self.router.routes)
     378        self.assertIdentical(self.xmlstream,
     379                             self.router.routes['component.example.org'])
     380
     381
     382    def test_connectionLost(self):
     383        """
     384        Make sure a stream is removed from the routing table on disconnect.
     385        """
     386        self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
     387        self.xmlstream.dispatch(None, xmlstream.STREAM_END_EVENT)
     388        self.assertNotIn('component.example.org', self.router.routes)
  • twisted/words/test/test_jabbersasl.py

    diff -r 32bd907eefae -r e255d835eef1 twisted/words/test/test_jabbersasl.py
    a b  
    1 # Copyright (c) 2001-2007 Twisted Matrix Laboratories.
     1# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
    22# See LICENSE for details.
    33
    44from zope.interface import implements
     
    6060    def setUp(self):
    6161        self.output = []
    6262
    63         self.authenticator = xmlstream.Authenticator()
    64         self.xmlstream = xmlstream.XmlStream(self.authenticator)
     63        self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator)
    6564        self.xmlstream.send = self.output.append
    6665        self.xmlstream.connectionMade()
    6766        self.xmlstream.dataReceived("<stream:stream xmlns='jabber:client' "
  • twisted/words/test/test_jabberxmlstream.py

    diff -r 32bd907eefae -r e255d835eef1 twisted/words/test/test_jabberxmlstream.py
    a b  
    2727    """
    2828
    2929    def setUp(self):
    30         authenticator = xmlstream.ConnectAuthenticator('otherhost')
    31         authenticator.namespace = 'testns'
    32         self.xmlstream = xmlstream.XmlStream(authenticator)
     30        self.xmlstream = xmlstream.XmlStream(xmlstream.ConnectAuthenticator,
     31                                             'otherhost')
    3332        self.clock = task.Clock()
     33        self.xmlstream.authenticator.namespace = 'testns'
    3434        self.xmlstream._callLater = self.clock.callLater
    3535        self.xmlstream.makeConnection(proto_helpers.StringTransport())
    3636        self.xmlstream.dataReceived(
     
    206206        self.gotStreamStart = False
    207207        self.gotStreamEnd = False
    208208        self.gotStreamError = False
    209         xs = xmlstream.XmlStream(xmlstream.Authenticator())
     209        xs = xmlstream.XmlStream(xmlstream.Authenticator)
    210210        xs.addObserver('//event/stream/start', self.onStreamStart)
    211211        xs.addObserver('//event/stream/end', self.onStreamEnd)
    212212        xs.addObserver('//event/stream/error', self.onStreamError)
     
    395395        streamStartedCalls = []
    396396        associateWithStreamCalls = []
    397397
    398         class TestAuthenticator:
     398        class TestAuthenticator(xmlstream.Authenticator):
    399399            def connectionMade(self):
    400400                connectionMadeCalls.append(None)
    401401
    402402            def streamStarted(self, rootElement):
    403403                streamStartedCalls.append(rootElement)
    404404
    405             def associateWithStream(self, xs):
    406                 associateWithStreamCalls.append(xs)
    407 
    408         a = TestAuthenticator()
    409         xs = xmlstream.XmlStream(a)
    410         self.assertEqual([xs], associateWithStreamCalls)
     405        xs = xmlstream.XmlStream(TestAuthenticator)
    411406        xs.connectionMade()
    412407        self.assertEqual([None], connectionMadeCalls)
    413408        xs.dataReceived("<stream:stream xmlns='jabber:client' "
     
    426421
    427422class AuthenticatorTest(unittest.TestCase):
    428423    def setUp(self):
    429         self.authenticator = xmlstream.ListenAuthenticator()
    430         self.xmlstream = xmlstream.XmlStream(self.authenticator)
     424        self.xmlstream = xmlstream.XmlStream(xmlstream.ListenAuthenticator)
    431425
    432426
    433427    def test_streamStart(self):
     
    493487    def setUp(self):
    494488        self.gotAuthenticated = False
    495489        self.initFailure = None
    496         self.authenticator = xmlstream.ConnectAuthenticator('otherHost')
    497         self.xmlstream = xmlstream.XmlStream(self.authenticator)
     490        self.xmlstream = xmlstream.XmlStream(xmlstream.ConnectAuthenticator,
     491                                             'otherHost')
    498492        self.xmlstream.addObserver('//event/stream/authd', self.onAuthenticated)
    499493        self.xmlstream.addObserver('//event/xmpp/initfailed', self.onInitFailed)
    500494
     
    518512        init = Initializer()
    519513        self.xmlstream.initializers = [init]
    520514
    521         self.authenticator.initializeStream()
     515        self.xmlstream.authenticator.initializeStream()
    522516        self.assertEqual([], self.xmlstream.initializers)
    523517        self.assertTrue(self.gotAuthenticated)
    524518
     
    534528        init = Initializer()
    535529        self.xmlstream.initializers = [init]
    536530
    537         self.authenticator.initializeStream()
     531        self.xmlstream.authenticator.initializeStream()
    538532        self.assertEqual([init], self.xmlstream.initializers)
    539533        self.assertFalse(self.gotAuthenticated)
    540534        self.assertNotIdentical(None, self.initFailure)
     
    546540        Test streamStart to fill the appropriate attributes from the
    547541        stream header.
    548542        """
    549         self.authenticator.namespace = 'testns'
     543        self.xmlstream.authenticator.namespace = 'testns'
    550544        xs = self.xmlstream
    551545        xs.makeConnection(proto_helpers.StringTransport())
    552546        xs.dataReceived("<stream:stream xmlns='jabber:client' "
     
    569563
    570564class ListenAuthenticatorTest(unittest.TestCase):
    571565    def setUp(self):
    572         self.authenticator = xmlstream.ListenAuthenticator()
    573         self.xmlstream = xmlstream.XmlStream(self.authenticator)
     566        self.xmlstream = xmlstream.XmlStream(xmlstream.ListenAuthenticator)
    574567
    575568
    576569    def test_streamStart(self):
     
    599592
    600593        self.savedSSL = xmlstream.ssl
    601594
    602         self.authenticator = xmlstream.Authenticator()
    603         self.xmlstream = xmlstream.XmlStream(self.authenticator)
     595        self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator)
    604596        self.xmlstream.send = self.output.append
    605597        self.xmlstream.connectionMade()
    606598        self.xmlstream.dataReceived("<stream:stream xmlns='jabber:client' "
     
    720712class BaseFeatureInitiatingInitializerTest(unittest.TestCase):
    721713
    722714    def setUp(self):
    723         self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator())
     715        self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator)
    724716        self.init = TestFeatureInitializer(self.xmlstream)
    725717
    726718
     
    894886                self.doneMade = True
    895887
    896888        handler = TestXMPPHandler()
    897         xs = xmlstream.XmlStream(xmlstream.Authenticator())
     889        xs = xmlstream.XmlStream(xmlstream.Authenticator)
    898890        handler.makeConnection(xs)
    899891        self.assertTrue(handler.doneMade)
    900892        self.assertIdentical(xs, handler.xmlstream)
     
    905897        Test that connectionLost forgets the XML stream.
    906898        """
    907899        handler = xmlstream.XMPPHandler()
    908         xs = xmlstream.XmlStream(xmlstream.Authenticator())
     900        xs = xmlstream.XmlStream(xmlstream.Authenticator)
    909901        handler.makeConnection(xs)
    910902        handler.connectionLost(Exception())
    911903        self.assertIdentical(None, handler.xmlstream)
     
    985977        sm = self.streamManager
    986978        handler = DummyXMPPHandler()
    987979        handler.setHandlerParent(sm)
    988         xs = xmlstream.XmlStream(xmlstream.Authenticator())
     980        xs = xmlstream.XmlStream(xmlstream.Authenticator)
    989981        sm._connected(xs)
    990982        self.assertEquals(1, handler.doneMade)
    991983        self.assertEquals(0, handler.doneInitialized)
     
    999991        sm = self.streamManager
    1000992        handler = DummyXMPPHandler()
    1001993        handler.setHandlerParent(sm)
    1002         xs = xmlstream.XmlStream(xmlstream.Authenticator())
     994        xs = xmlstream.XmlStream(xmlstream.Authenticator)
    1003995        sm._connected(xs)
    1004996        self.assertIdentical(None, xs.rawDataInFn)
    1005997        self.assertIdentical(None, xs.rawDataOutFn)
     
    10131005        sm.logTraffic = True
    10141006        handler = DummyXMPPHandler()
    10151007        handler.setHandlerParent(sm)
    1016         xs = xmlstream.XmlStream(xmlstream.Authenticator())
     1008        xs = xmlstream.XmlStream(xmlstream.Authenticator)
    10171009        sm._connected(xs)
    10181010        self.assertNotIdentical(None, xs.rawDataInFn)
    10191011        self.assertNotIdentical(None, xs.rawDataOutFn)
     
    10271019        sm = self.streamManager
    10281020        handler = DummyXMPPHandler()
    10291021        handler.setHandlerParent(sm)
    1030         xs = xmlstream.XmlStream(xmlstream.Authenticator())
     1022        xs = xmlstream.XmlStream(xmlstream.Authenticator)
    10311023        sm._authd(xs)
    10321024        self.assertEquals(0, handler.doneMade)
    10331025        self.assertEquals(1, handler.doneInitialized)
     
    10421034        sm = self.streamManager
    10431035        handler = DummyXMPPHandler()
    10441036        handler.setHandlerParent(sm)
    1045         xs = xmlstream.XmlStream(xmlstream.Authenticator())
     1037        xs = xmlstream.XmlStream(xmlstream.Authenticator)
    10461038        sm._disconnected(xs)
    10471039        self.assertEquals(0, handler.doneMade)
    10481040        self.assertEquals(0, handler.doneInitialized)
     
    10721064        called.
    10731065        """
    10741066        sm = self.streamManager
    1075         xs = xmlstream.XmlStream(xmlstream.Authenticator())
     1067        xs = xmlstream.XmlStream(xmlstream.Authenticator)
    10761068        sm._connected(xs)
    10771069        sm._authd(xs)
    10781070        handler = DummyXMPPHandler()
     
    10891081
    10901082        The data should be sent directly over the XML stream.
    10911083        """
    1092         factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
     1084        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator)
    10931085        sm = xmlstream.StreamManager(factory)
    10941086        xs = factory.buildProtocol(None)
    10951087        xs.transport = proto_helpers.StringTransport()
     
    11091101        The data should be cached until an XML stream has been established and
    11101102        initialized.
    11111103        """
    1112         factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
     1104        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator)
    11131105        sm = xmlstream.StreamManager(factory)
    11141106        handler = DummyXMPPHandler()
    11151107        sm.addHandler(handler)
     
    11391131
    11401132        The data should be cached until the XML stream has been initialized.
    11411133        """
    1142         factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
     1134        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator)
    11431135        sm = xmlstream.StreamManager(factory)
    11441136        xs = factory.buildProtocol(None)
    11451137        xs.transport = proto_helpers.StringTransport()
     
    11591151        The data should be cached until a new XML stream has been established
    11601152        and initialized.
    11611153        """
    1162         factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
     1154        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator)
    11631155        sm = xmlstream.StreamManager(factory)
    11641156        handler = DummyXMPPHandler()
    11651157        sm.addHandler(handler)
  • twisted/words/test/test_xmlstream.py

    diff -r 32bd907eefae -r e255d835eef1 twisted/words/test/test_xmlstream.py
    a b  
    1 # Copyright (c) 2001-2007 Twisted Matrix Laboratories.
     1# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
    22# See LICENSE for details.
    33
    44"""
     
    77
    88from twisted.internet import defer, protocol
    99from twisted.trial import unittest
    10 from twisted.words.xish import utility, xmlstream
     10from twisted.words.xish import domish, utility, xmlstream
    1111
    1212class XmlStreamTest(unittest.TestCase):
    1313    def setUp(self):
    14         self.errorOccurred = False
    15         self.streamStarted = False
    16         self.streamEnded = False
    1714        self.outlist = []
    1815        self.xmlstream = xmlstream.XmlStream()
    1916        self.xmlstream.transport = self
    2017        self.xmlstream.transport.write = self.outlist.append
    2118
    22     # Auxilary methods
     19
    2320    def loseConnection(self):
     21        """
     22        Stub loseConnection because we are a transport.
     23        """
    2424        self.xmlstream.connectionLost("no reason")
    2525
    26     def streamStartEvent(self, rootelem):
    27         self.streamStarted = True
    2826
    29     def streamErrorEvent(self, errelem):
    30         self.errorOccurred = True
    31 
    32     def streamEndEvent(self, _):
    33         self.streamEnded = True
    34 
    35     def testBasicOp(self):
    36         xs = self.xmlstream
    37         xs.addObserver(xmlstream.STREAM_START_EVENT,
    38                        self.streamStartEvent)
    39         xs.addObserver(xmlstream.STREAM_ERROR_EVENT,
    40                        self.streamErrorEvent)
    41         xs.addObserver(xmlstream.STREAM_END_EVENT,
    42                        self.streamEndEvent)
    43 
    44         # Go...
    45         xs.connectionMade()
    46         xs.send("<root>")
     27    def test_send(self):
     28        """
     29        Sending data should result into it being written to the transport.
     30        """
     31        self.xmlstream.connectionMade()
     32        self.xmlstream.send("<root>")
    4733        self.assertEquals(self.outlist[0], "<root>")
    4834
    49         xs.dataReceived("<root>")
    50         self.assertEquals(self.streamStarted, True)
    5135
    52         self.assertEquals(self.errorOccurred, False)
    53         self.assertEquals(self.streamEnded, False)
    54         xs.dataReceived("<child><unclosed></child>")
    55         self.assertEquals(self.errorOccurred, True)
    56         self.assertEquals(self.streamEnded, True)
     36    def test_receiveRoot(self):
     37        """
     38        Receiving the starttag of the root element results in stream start.
     39        """
     40        streamStarted = []
     41
     42        def streamStartEvent(rootelem):
     43            streamStarted.append(None)
     44
     45        self.xmlstream.addObserver(xmlstream.STREAM_START_EVENT,
     46                                   streamStartEvent)
     47        self.xmlstream.connectionMade()
     48        self.xmlstream.dataReceived("<root>")
     49        self.assertEquals(1, len(streamStarted))
     50
     51
     52    def test_receiveBadXML(self):
     53        """
     54        Receiving malformed XML should result in in error.
     55        """
     56        streamError = []
     57        streamEnd = []
     58
     59        def streamErrorEvent(reason):
     60            streamError.append(reason)
     61
     62        def streamEndEvent(_):
     63            streamEnd.append(None)
     64
     65        self.xmlstream.addObserver(xmlstream.STREAM_ERROR_EVENT,
     66                                   streamErrorEvent)
     67        self.xmlstream.addObserver(xmlstream.STREAM_END_EVENT,
     68                                   streamEndEvent)
     69        self.xmlstream.connectionMade()
     70
     71        self.xmlstream.dataReceived("<root>")
     72        self.assertEquals(0, len(streamError))
     73        self.assertEquals(0, len(streamEnd))
     74
     75        self.xmlstream.dataReceived("<child><unclosed></child>")
     76        self.assertEquals(1, len(streamError))
     77        self.assertTrue(streamError[0].check(domish.ParserError))
     78        self.assertEquals(1, len(streamEnd))
     79
    5780
    5881
    5982class DummyProtocol(protocol.Protocol, utility.EventDispatcher):
  • twisted/words/xish/xmlstream.py

    diff -r 32bd907eefae -r e255d835eef1 twisted/words/xish/xmlstream.py
    a b  
    11# -*- test-case-name: twisted.words.test.test_xmlstream -*-
    22#
    3 # Copyright (c) 2001-2007 Twisted Matrix Laboratories.
     3# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
    44# See LICENSE for details.
    55
    66"""
     
    1616Maintainer: Ralph Meijer
    1717"""
    1818
     19from twisted.python import failure
    1920from twisted.internet import protocol
    2021from twisted.words.xish import domish, utility
    2122
     
    7374                self.rawDataInFn(data)
    7475            self.stream.parse(data)
    7576        except domish.ParserError:
    76             self.dispatch(self, STREAM_ERROR_EVENT)
     77            self.dispatch(failure.Failure(), STREAM_ERROR_EVENT)
    7778            self.transport.loseConnection()
    7879
    7980    def connectionLost(self, reason):