source: ralphm-patches/server-stream-manager.patch @ 74:355afce3af27

Last change on this file since 74:355afce3af27 was 74:355afce3af27, checked in by Ralph Meijer <ralphm@…>, 8 years ago

Refactor Component to match changes to StreamManager?.

File size: 21.2 KB
RevLine 
[68]1# HG changeset patch
[74]2# Parent 71082c22f73200a0b8b4b5c6e5d2773f9f68dd4d
[72]3Generalize StreamManager and add ServerStreamManager.
4
5This generalizes `StreamManager` to `BaseStreamManager` to take the
6common functionality and reuse it for `ServerStreamManager`. Where
7`StreamManager` is used for initiating connections, with a factory as
8a parameter, `ServerStreamManager` works for receiving connections. It
9must be created in a protocol factory when a connection is established,
10and then its `makeConnection` should be called to hook it up to the
11`XmlStream` instance.
[68]12
13diff --git a/wokkel/client.py b/wokkel/client.py
14--- a/wokkel/client.py
15+++ b/wokkel/client.py
16@@ -109,7 +109,7 @@
17         self._connection.disconnect()
18 
19 
20-    def _authd(self, xs):
21+    def connectionInitialized(self, xs):
22         """
23         Called when the stream has been initialized.
24 
25@@ -118,7 +118,7 @@
26         by its constituent initializers.
27         """
28         self.jid = self.factory.authenticator.jid
29-        StreamManager._authd(self, xs)
30+        StreamManager.connectionInitialized(self, xs)
31 
32 
33     def initializationFailed(self, reason):
[74]34diff --git a/wokkel/component.py b/wokkel/component.py
35--- a/wokkel/component.py
36+++ b/wokkel/component.py
37@@ -36,7 +36,7 @@
38         StreamManager.__init__(self, factory)
39 
40 
41-    def _authd(self, xs):
42+    def connectionInitialized(self, xs):
43         """
44         Called when stream initialization has completed.
45 
46@@ -53,7 +53,7 @@
47             old_send(obj)
48 
49         xs.send = send
50-        StreamManager._authd(self, xs)
51+        StreamManager.connectionInitialized(self, xs)
52 
53 
54     def initializationFailed(self, reason):
[68]55diff --git a/wokkel/subprotocols.py b/wokkel/subprotocols.py
56--- a/wokkel/subprotocols.py
57+++ b/wokkel/subprotocols.py
[74]58@@ -120,7 +120,7 @@
[68]59 
60 
61 
62-class StreamManager(XMPPHandlerCollection):
63+class BaseStreamManager(XMPPHandlerCollection):
64     """
65     Business logic representing a managed XMPP connection.
66 
[74]67@@ -131,43 +131,39 @@
[68]68 
69     @ivar xmlstream: currently managed XML stream
70     @type xmlstream: L{XmlStream}
71+
72     @ivar logTraffic: if true, log all traffic.
73     @type logTraffic: C{bool}
74+
75     @ivar _initialized: Whether the stream represented by L{xmlstream} has
76                         been initialized. This is used when caching outgoing
77                         stanzas.
78     @type _initialized: C{bool}
79-    @ivar _packetQueue: internal buffer of unsent data. See L{send} for details.
80-    @type _packetQueue: L{list}
81+
82     @ivar timeout: Default IQ request timeout in seconds.
83     @type timeout: C{int}
84+
85     @ivar _reactor: A provider of L{IReactorTime} to track timeouts.
86     """
87+
88     timeout = None
89     _reactor = None
90 
91     logTraffic = False
92 
93-    def __init__(self, factory, reactor=None):
94+    def __init__(self, reactor=None):
95         """
96         Construct a stream manager.
97 
98-        @param factory: The stream factory to connect with.
99         @param reactor: A provider of L{IReactorTime} to track timeouts.
100             If not provided, the global reactor will be used.
101         """
102         XMPPHandlerCollection.__init__(self)
103+
104         self.xmlstream = None
105         self._packetQueue = []
106         self._initialized = False
107 
108-        factory.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT, self._connected)
109-        factory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self._authd)
110-        factory.addBootstrap(xmlstream.INIT_FAILED_EVENT,
111-                             self.initializationFailed)
112-        factory.addBootstrap(xmlstream.STREAM_END_EVENT, self._disconnected)
113-        self.factory = factory
114-
115         if reactor is None:
116             from twisted.internet import reactor
117         self._reactor = reactor
[74]118@@ -193,7 +189,7 @@
[68]119             handler.connectionInitialized()
120 
121 
122-    def _connected(self, xs):
123+    def makeConnection(self, xs):
124         """
125         Called when the transport connection has been established.
126 
[74]127@@ -211,13 +207,17 @@
[68]128             xs.rawDataInFn = logDataIn
129             xs.rawDataOutFn = logDataOut
130 
131+        xs.addObserver(xmlstream.STREAM_AUTHD_EVENT,
132+                       self.connectionInitialized)
133+        xs.addObserver(xmlstream.STREAM_END_EVENT,
134+                       self.connectionLost)
135         self.xmlstream = xs
136 
137         for e in list(self):
138             e.makeConnection(xs)
139 
140 
141-    def _authd(self, xs):
142+    def connectionInitialized(self, xs):
143         """
144         Called when the stream has been initialized.
145 
[74]146@@ -240,21 +240,8 @@
[68]147             e.connectionInitialized()
148 
149 
150-    def initializationFailed(self, reason):
151-        """
152-        Called when stream initialization has failed.
153 
154-        Stream initialization has halted, with the reason indicated by
155-        C{reason}. It may be retried by calling the authenticator's
156-        C{initializeStream}. See the respective authenticators for details.
157-
158-        @param reason: A failure instance indicating why stream initialization
159-                       failed.
160-        @type reason: L{failure.Failure}
161-        """
162-
163-
164-    def _disconnected(self, reason):
165+    def connectionLost(self, reason):
166         """
167         Called when the stream has been closed.
168 
[74]169@@ -464,6 +451,60 @@
[68]170 
171 
172 
173+class StreamManager(BaseStreamManager):
174+    """
175+    Business logic representing a managed XMPP client connection.
176+
177+    @ivar _packetQueue: internal buffer of unsent data. See L{send} for details.
178+    @type _packetQueue: L{list}
179+    """
180+
181+    def __init__(self, factory, reactor=None):
182+        """
183+        Construct a stream manager.
184+
185+        @param factory: The stream factory to connect with.
186+        @param reactor: A provider of L{IReactorTime} to track timeouts.
187+            If not provided, the global reactor will be used.
188+        """
189+        BaseStreamManager.__init__(self, reactor=reactor)
190+
191+        factory.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT,
192+                             self.makeConnection)
193+        self.factory = factory
194+
195+
196+    def makeConnection(self, xs):
197+        xs.addObserver(xmlstream.INIT_FAILED_EVENT,
198+                       self.initializationFailed)
199+        BaseStreamManager.makeConnection(self, xs)
200+
201+
202+    def initializationFailed(self, reason):
203+        """
204+        Called when stream initialization has failed.
205+
206+        Stream initialization has halted, with the reason indicated by
207+        C{reason}. It may be retried by calling the authenticator's
208+        C{initializeStream}. See the respective authenticators for details.
209+
210+        @param reason: A failure instance indicating why stream initialization
211+                       failed.
212+        @type reason: L{failure.Failure}
213+        """
214+
215+
216+
217+class ServerStreamManager(BaseStreamManager):
218+    """
219+    Business logic representing a managed server-side XMPP connection.
220+    """
221+
222+    def __init__(self, reactor=None):
223+        BaseStreamManager.__init__(self, reactor=reactor)
224+
225+
226+
227 class IQHandlerMixin(object):
228     """
229     XMPP subprotocol mixin for handle incoming IQ stanzas.
230diff --git a/wokkel/test/helpers.py b/wokkel/test/helpers.py
231--- a/wokkel/test/helpers.py
232+++ b/wokkel/test/helpers.py
233@@ -108,5 +108,5 @@
234         factory = DummyFactory()
235         StreamManager.__init__(self, factory, reactor)
236         self.stub = XmlStreamStub()
237-        self._connected(self.stub.xmlstream)
238-        self._authd(self.stub.xmlstream)
239+        self.makeConnection(self.stub.xmlstream)
240+        self.connectionInitialized(self.stub.xmlstream)
241diff --git a/wokkel/test/test_client.py b/wokkel/test/test_client.py
242--- a/wokkel/test/test_client.py
243+++ b/wokkel/test/test_client.py
244@@ -38,7 +38,7 @@
245         """
246         xs = self.client.factory.buildProtocol(None)
247         self.client.factory.authenticator.jid = JID('user@example.org/test')
248-        xs.dispatch(xs, xmlstream.STREAM_AUTHD_EVENT)
249+        self.client.connectionInitialized(xs)
250         self.assertEquals(JID('user@example.org/test'), self.client.jid)
251 
252 
[74]253diff --git a/wokkel/test/test_component.py b/wokkel/test/test_component.py
254--- a/wokkel/test/test_component.py
255+++ b/wokkel/test/test_component.py
256@@ -49,11 +49,18 @@
257     def __init__(self, *args, **kwargs):
258         component.Component.__init__(self, *args, **kwargs)
259         self.factory.clock = Clock()
260+        self.output = []
261 
262 
263     def _getConnection(self):
264         c = FakeConnector(self.factory, None, None)
265         c.connect()
266+        xs = self.factory.buildProtocol(None)
267+        xs.send = self.output.append
268+        xs.connectionMade()
269+        self.makeConnection(xs)
270+        xs.thisEntity = xs.otherEntity
271+        xs.dispatch(xs, xmlstream.STREAM_AUTHD_EVENT)
272         return c
273 
274 
275@@ -104,6 +111,21 @@
276         self.assertEqual(1, connector.connects)
277 
278 
279+    def test_stampFrom(self):
280+        """
281+        Outgoing elements with missing sender address get component JID.
282+        """
283+        comp = TestableComponent('example.org', 5347,
284+                                 'test.example.org', 'secret')
285+        comp.startService()
286+
287+        element = domish.Element((component.NS_COMPONENT_ACCEPT, "message"))
288+        element["to"] = "test@example.org"
289+        comp.xmlstream.send(element)
290+
291+        self.assertEqual('test.example.org', element.getAttribute("from"))
292+
293+
294 
295 class InternalComponentTest(unittest.TestCase):
296     """
[68]297diff --git a/wokkel/test/test_subprotocols.py b/wokkel/test/test_subprotocols.py
298--- a/wokkel/test/test_subprotocols.py
299+++ b/wokkel/test/test_subprotocols.py
300@@ -189,20 +189,7 @@
301 
302 
303 
304-class StreamManagerTest(unittest.TestCase):
305-    """
306-    Tests for L{subprotocols.StreamManager}.
307-    """
308-
309-    def setUp(self):
310-        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
311-        self.clock = task.Clock()
312-        self.streamManager = subprotocols.StreamManager(factory, self.clock)
313-        self.xmlstream = factory.buildProtocol(None)
314-        self.transport = proto_helpers.StringTransport()
315-        self.xmlstream.transport = self.transport
316-
317-        self.request = IQGetStanza()
318+class BaseStreamManagerTestsMixin(object):
319 
320     def _streamStarted(self):
321         """
322@@ -216,25 +203,14 @@
323         self.xmlstream.dispatch(self.xmlstream, "//event/stream/authd")
324 
325 
326-    def test_basic(self):
327+    def test_interface(self):
328         """
329-        Test correct initialization and setup of factory observers.
330+        ServerStreamManager implements IXMPPHandlerCollection.
331         """
332-        factory = DummyFactory()
333-        sm = subprotocols.StreamManager(factory)
334-        self.assertIdentical(None, sm.xmlstream)
335-        self.assertEquals([], sm.handlers)
336-        self.assertEquals(sm._connected,
337-                          sm.factory.callbacks['//event/stream/connected'])
338-        self.assertEquals(sm._authd,
339-                          sm.factory.callbacks['//event/stream/authd'])
340-        self.assertEquals(sm._disconnected,
341-                          sm.factory.callbacks['//event/stream/end'])
342-        self.assertEquals(sm.initializationFailed,
343-                          sm.factory.callbacks['//event/xmpp/initfailed'])
344+        verifyObject(ijabber.IXMPPHandlerCollection, self.streamManager)
345 
346 
347-    def test_connected(self):
348+    def test_makeConnection(self):
349         """
350         Test that protocol handlers have their connectionMade method called
351         when the XML stream is connected.
352@@ -242,27 +218,27 @@
353         sm = self.streamManager
354         handler = DummyXMPPHandler()
355         handler.setHandlerParent(sm)
356-        xs = xmlstream.XmlStream(xmlstream.Authenticator())
357-        sm._connected(xs)
358+        xs = self.xmlstream
359+        sm.makeConnection(xs)
360         self.assertEquals(1, handler.doneMade)
361         self.assertEquals(0, handler.doneInitialized)
362         self.assertEquals(0, handler.doneLost)
363 
364 
365-    def test_connectedLogTrafficFalse(self):
366+    def test_makeConnectionLogTrafficFalse(self):
367         """
368         Test raw data functions unset when logTraffic is set to False.
369         """
370         sm = self.streamManager
371         handler = DummyXMPPHandler()
372         handler.setHandlerParent(sm)
373-        xs = xmlstream.XmlStream(xmlstream.Authenticator())
374-        sm._connected(xs)
375+        xs = self.xmlstream
376+        sm.makeConnection(xs)
377         self.assertIdentical(None, xs.rawDataInFn)
378         self.assertIdentical(None, xs.rawDataOutFn)
379 
380 
381-    def test_connectedLogTrafficTrue(self):
382+    def test_makeConnectionLogTrafficTrue(self):
383         """
384         Test raw data functions set when logTraffic is set to True.
385         """
386@@ -270,13 +246,13 @@
387         sm.logTraffic = True
388         handler = DummyXMPPHandler()
389         handler.setHandlerParent(sm)
390-        xs = xmlstream.XmlStream(xmlstream.Authenticator())
391-        sm._connected(xs)
392+        xs = self.xmlstream
393+        sm.makeConnection(xs)
394         self.assertNotIdentical(None, xs.rawDataInFn)
395         self.assertNotIdentical(None, xs.rawDataOutFn)
396 
397 
398-    def test_authd(self):
399+    def test_connectionInitialized(self):
400         """
401         Test that protocol handlers have their connectionInitialized method
402         called when the XML stream is initialized.
403@@ -284,27 +260,27 @@
404         sm = self.streamManager
405         handler = DummyXMPPHandler()
406         handler.setHandlerParent(sm)
407-        xs = xmlstream.XmlStream(xmlstream.Authenticator())
408-        sm._authd(xs)
409+        xs = self.xmlstream
410+        sm.connectionInitialized(xs)
411         self.assertEquals(0, handler.doneMade)
412         self.assertEquals(1, handler.doneInitialized)
413         self.assertEquals(0, handler.doneLost)
414 
415 
416-    def test_disconnected(self):
417+    def test_connectionLost(self):
418         """
419         Protocol handlers have connectionLost called on stream disconnect.
420         """
421         sm = self.streamManager
422         handler = DummyXMPPHandler()
423         handler.setHandlerParent(sm)
424-        sm._disconnected(None)
425+        sm.connectionLost(None)
426         self.assertEquals(0, handler.doneMade)
427         self.assertEquals(0, handler.doneInitialized)
428         self.assertEquals(1, handler.doneLost)
429 
430 
431-    def test_disconnectedReason(self):
432+    def test_connectionLostReason(self):
433         """
434         A L{STREAM_END_EVENT} results in L{StreamManager} firing the handlers
435         L{connectionLost} methods, passing a L{failure.Failure} reason.
436@@ -313,7 +289,7 @@
437         handler = FailureReasonXMPPHandler()
438         handler.setHandlerParent(sm)
439         xmlstream.XmlStream(xmlstream.Authenticator())
440-        sm._disconnected(failure.Failure(Exception("no reason")))
441+        sm.connectionLost(failure.Failure(Exception("no reason")))
442         self.assertEquals(True, handler.gotFailureReason)
443 
444 
445@@ -335,8 +311,8 @@
446         Adding a handler when connected doesn't call connectionInitialized.
447         """
448         sm = self.streamManager
449-        xs = xmlstream.XmlStream(xmlstream.Authenticator())
450-        sm._connected(xs)
451+        xs = self.xmlstream
452+        sm.makeConnection(xs)
453         handler = DummyXMPPHandler()
454         handler.setHandlerParent(sm)
455 
456@@ -358,10 +334,10 @@
457                 self.nestedHandler.setHandlerParent(self.parent)
458 
459         sm = self.streamManager
460-        xs = xmlstream.XmlStream(xmlstream.Authenticator())
461+        xs = self.xmlstream
462         handler = NestingHandler()
463         handler.setHandlerParent(sm)
464-        sm._connected(xs)
465+        sm.makeConnection(xs)
466 
467         self.assertEquals(1, handler.doneMade)
468         self.assertEquals(0, handler.doneInitialized)
469@@ -383,9 +359,9 @@
470         called.
471         """
472         sm = self.streamManager
473-        xs = xmlstream.XmlStream(xmlstream.Authenticator())
474-        sm._connected(xs)
475-        sm._authd(xs)
476+        xs = self.xmlstream
477+        sm.makeConnection(xs)
478+        sm.connectionInitialized(xs)
479         handler = DummyXMPPHandler()
480         handler.setHandlerParent(sm)
481 
482@@ -407,11 +383,11 @@
483                 self.nestedHandler.setHandlerParent(self.parent)
484 
485         sm = self.streamManager
486-        xs = xmlstream.XmlStream(xmlstream.Authenticator())
487+        xs = self.xmlstream
488         handler = NestingHandler()
489         handler.setHandlerParent(sm)
490-        sm._connected(xs)
491-        sm._authd(xs)
492+        sm.makeConnection(xs)
493+        sm.connectionInitialized(xs)
494 
495         self.assertEquals(1, handler.doneMade)
496         self.assertEquals(1, handler.doneInitialized)
497@@ -435,12 +411,12 @@
498                 self.nestedHandler.setHandlerParent(self.parent)
499 
500         sm = self.streamManager
501-        xs = xmlstream.XmlStream(xmlstream.Authenticator())
502+        xs = self.xmlstream
503         handler = NestingHandler()
504         handler.setHandlerParent(sm)
505-        sm._connected(xs)
506-        sm._authd(xs)
507-        sm._disconnected(xs)
508+        sm.makeConnection(xs)
509+        sm.connectionInitialized(xs)
510+        sm.connectionLost(xs)
511 
512         self.assertEquals(1, handler.doneMade)
513         self.assertEquals(1, handler.doneInitialized)
514@@ -470,16 +446,13 @@
515 
516         The data should be sent directly over the XML stream.
517         """
518-        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
519-        sm = subprotocols.StreamManager(factory)
520-        xs = factory.buildProtocol(None)
521-        xs.transport = proto_helpers.StringTransport()
522+        xs = self.xmlstream
523         xs.connectionMade()
524         xs.dataReceived("<stream:stream xmlns='jabber:client' "
525                         "xmlns:stream='http://etherx.jabber.org/streams' "
526                         "from='example.com' id='12345'>")
527         xs.dispatch(xs, "//event/stream/authd")
528-        sm.send("<presence/>")
529+        self.streamManager.send("<presence/>")
530         self.assertEquals("<presence/>", xs.transport.value())
531 
532 
533@@ -490,12 +463,11 @@
534         The data should be cached until an XML stream has been established and
535         initialized.
536         """
537-        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
538-        sm = subprotocols.StreamManager(factory)
539+        sm = self.streamManager
540         handler = DummyXMPPHandler()
541         sm.addHandler(handler)
542 
543-        xs = factory.buildProtocol(None)
544+        xs = self.xmlstream
545         xs.transport = proto_helpers.StringTransport()
546         sm.send("<presence/>")
547         self.assertEquals("", xs.transport.value())
548@@ -522,7 +494,7 @@
549         """
550         factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
551         sm = subprotocols.StreamManager(factory)
552-        xs = factory.buildProtocol(None)
553+        xs = self.xmlstream
554         xs.transport = proto_helpers.StringTransport()
555         xs.connectionMade()
556         xs.dataReceived("<stream:stream xmlns='jabber:client' "
557@@ -545,7 +517,7 @@
558         handler = DummyXMPPHandler()
559         sm.addHandler(handler)
560 
561-        xs = factory.buildProtocol(None)
562+        xs = self.xmlstream
563         xs.connectionMade()
564         xs.transport = proto_helpers.StringTransport()
565         xs.connectionLost(None)
566@@ -677,6 +649,7 @@
567         """
568         d = self.streamManager.request(self.request)
569         xs = self.xmlstream
570+        xs.connectionMade()
571         xs.connectionLost(failure.Failure(ConnectionDone()))
572         self.assertFailure(d, ConnectionDone)
573         return d
574@@ -692,6 +665,7 @@
575             d = xmlstream.IQ(self.xmlstream).send()
576             d.addErrback(eb)
577 
578+        self.xmlstream.connectionMade()
579         d = self.streamManager.request(self.request)
580         d.addErrback(eb)
581         self.xmlstream.connectionLost(failure.Failure(ConnectionDone()))
582@@ -736,6 +710,7 @@
583         self.request.timeout = 60
584         d = self.streamManager.request(self.request)
585 
586+        self.xmlstream.connectionMade()
587         self.xmlstream.connectionLost(failure.Failure(ConnectionDone()))
588         self.assertFailure(d, ConnectionDone)
589         self.assertFalse(self.clock.calls)
590@@ -778,6 +753,56 @@
591 
592 
593 
594+class StreamManagerTest(unittest.TestCase, BaseStreamManagerTestsMixin):
595+    """
596+    Tests for L{subprotocols.StreamManager}.
597+    """
598+
599+    def setUp(self):
600+        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
601+        self.clock = task.Clock()
602+        self.streamManager = subprotocols.StreamManager(factory, self.clock)
603+        self.xmlstream = factory.buildProtocol(None)
604+        self.transport = proto_helpers.StringTransport()
605+        self.xmlstream.transport = self.transport
606+
607+        self.request = IQGetStanza()
608+
609+
610+    def test_basic(self):
611+        """
612+        Test correct initialization and setup of factory observers.
613+        """
614+        factory = DummyFactory()
615+        sm = subprotocols.StreamManager(factory)
616+        self.assertIdentical(None, sm.xmlstream)
617+        self.assertEquals([], sm.handlers)
618+        self.assertEquals(sm.makeConnection,
619+                          sm.factory.callbacks['//event/stream/connected'])
620+
621+
622+
623+class ServerStreamManagerTest(unittest.TestCase, BaseStreamManagerTestsMixin):
624+    """
625+    Tests for L{subprotocols.StreamManager}.
626+    """
627+
628+    def setUp(self):
629+        self.clock = task.Clock()
630+        self.streamManager = subprotocols.ServerStreamManager(
631+            reactor=self.clock)
632+
633+        self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator())
634+        self.transport = proto_helpers.StringTransport()
635+        self.xmlstream.transport = self.transport
636+
637+        self.xmlstream.addObserver(xmlstream.STREAM_CONNECTED_EVENT,
638+                                   self.streamManager.makeConnection)
639+
640+        self.request = IQGetStanza()
641+
642+
643+
644 class DummyIQHandler(subprotocols.IQHandlerMixin):
645     iqHandlers = {'/iq[@type="get"]': 'onGet'}
646 
Note: See TracBrowser for help on using the repository browser.