1
from twisted.trial import unittest
3
from twisted.internet import defer, task
4
from twisted.internet.error import ConnectionLost
5
from twisted.test import proto_helpers
6
from twisted.words.xish import domish
7
from twisted.words.protocols.jabber import error, xmlstream
9
NS_XMPP_TLS = 'urn:ietf:params:xml:ns:xmpp-tls'
11
class IQTest(unittest.TestCase):
13
Tests both IQ and the associated IIQResponseTracker callback.
17
authenticator = xmlstream.ConnectAuthenticator('otherhost')
18
authenticator.namespace = 'testns'
19
self.xmlstream = xmlstream.XmlStream(authenticator)
20
self.clock = task.Clock()
21
self.xmlstream._callLater = self.clock.callLater
22
self.xmlstream.makeConnection(proto_helpers.StringTransport())
23
self.xmlstream.dataReceived(
24
"<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
25
"xmlns='testns' from='otherhost' version='1.0'>")
26
self.iq = xmlstream.IQ(self.xmlstream, type='get')
29
self.assertEquals(self.iq['type'], 'get')
30
self.assert_(self.iq['id'])
33
self.xmlstream.transport.clear()
35
self.assertEquals("<iq type='get' id='%s'/>" % self.iq['id'],
36
self.xmlstream.transport.value())
38
def testResultResponse(self):
40
self.assertEquals(result['type'], 'result')
46
xs.dataReceived("<iq type='result' id='%s'/>" % self.iq['id'])
49
def testErrorResponse(self):
51
self.assertFailure(d, error.StanzaError)
54
xs.dataReceived("<iq type='error' id='%s'/>" % self.iq['id'])
57
def testNonTrackedResponse(self):
59
Test that untracked iq responses don't trigger any action.
61
Untracked means that the id of the incoming response iq is not
62
in the stream's C{iqDeferreds} dictionary.
65
xmlstream.upgradeWithIQResponseTracker(xs)
67
# Make sure we aren't tracking any iq's.
68
self.failIf(xs.iqDeferreds)
70
# Set up a fallback handler that checks the stanza's handled attribute.
71
# If that is set to True, the iq tracker claims to have handled the
74
self.failIf(getattr(iq, 'handled', False))
76
xs.addObserver("/iq", cb, -1)
78
# Receive an untracked iq response
79
xs.dataReceived("<iq type='result' id='test'/>")
81
def testCleanup(self):
83
Test if the deferred associated with an iq request is removed
84
from the list kept in the L{XmlStream} object after it has
90
xs.dataReceived("<iq type='result' id='%s'/>" % self.iq['id'])
91
self.assertNotIn(self.iq['id'], xs.iqDeferreds)
94
def testDisconnectCleanup(self):
96
Test if deferreds for iq's that haven't yet received a response
97
have their errback called on stream disconnect.
102
xs.connectionLost("Closed by peer")
103
self.assertFailure(d, ConnectionLost)
106
def testNoModifyingDict(self):
108
Test to make sure the errbacks cannot cause the iteration of the
109
iqDeferreds to blow up in our face.
113
d = xmlstream.IQ(self.xmlstream).send()
118
self.xmlstream.connectionLost("Closed by peer")
121
def testRequestTimingOut(self):
123
Test that an iq request with a defined timeout times out.
127
self.assertFailure(d, xmlstream.TimeoutError)
129
self.clock.pump([1, 60])
130
self.failIf(self.clock.calls)
131
self.failIf(self.xmlstream.iqDeferreds)
134
def testRequestNotTimingOut(self):
136
Test that an iq request with a defined timeout does not time out
137
when a response was received before the timeout period elapsed.
141
self.clock.callLater(1, self.xmlstream.dataReceived,
142
"<iq type='result' id='%s'/>" % self.iq['id'])
143
self.clock.pump([1, 1])
144
self.failIf(self.clock.calls)
147
def testDisconnectTimeoutCancellation(self):
149
Test if timeouts for iq's that haven't yet received a response
150
are cancelled on stream disconnect.
157
xs.connectionLost("Closed by peer")
158
self.assertFailure(d, ConnectionLost)
159
self.failIf(self.clock.calls)
162
class XmlStreamTest(unittest.TestCase):
164
def onStreamStart(self, obj):
165
self.gotStreamStart = True
168
def onStreamEnd(self, obj):
169
self.gotStreamEnd = True
172
def onStreamError(self, obj):
173
self.gotStreamError = True
178
Set up XmlStream and several observers.
180
self.gotStreamStart = False
181
self.gotStreamEnd = False
182
self.gotStreamError = False
183
xs = xmlstream.XmlStream(xmlstream.Authenticator())
184
xs.addObserver('//event/stream/start', self.onStreamStart)
185
xs.addObserver('//event/stream/end', self.onStreamEnd)
186
xs.addObserver('//event/stream/error', self.onStreamError)
187
xs.makeConnection(proto_helpers.StringTransportWithDisconnection())
188
xs.transport.protocol = xs
189
xs.namespace = 'testns'
194
def testSendHeaderBasic(self):
196
Basic test on the header sent by sendHeader.
200
splitHeader = self.xmlstream.transport.value()[0:-1].split(' ')
201
self.assertIn("<stream:stream", splitHeader)
202
self.assertIn("xmlns:stream='http://etherx.jabber.org/streams'",
204
self.assertIn("xmlns='testns'", splitHeader)
205
self.assertIn("version='1.0'", splitHeader)
206
self.assertEquals(True, xs._headerSent)
209
def testSendHeaderInitiating(self):
211
Test addressing when initiating a stream.
214
xs.thisHost = 'thisHost'
215
xs.otherHost = 'otherHost'
218
splitHeader = xs.transport.value()[0:-1].split(' ')
219
self.assertIn("to='otherHost'", splitHeader)
220
self.assertNotIn("from='thisHost'", splitHeader)
223
def testSendHeaderReceiving(self):
225
Test addressing when receiving a stream.
228
xs.thisHost = 'thisHost'
229
xs.otherHost = 'otherHost'
230
xs.initiating = False
233
splitHeader = xs.transport.value()[0:-1].split(' ')
234
self.assertNotIn("to='otherHost'", splitHeader)
235
self.assertIn("from='thisHost'", splitHeader)
236
self.assertIn("id='session01'", splitHeader)
239
def testReceiveStreamError(self):
241
Test events when a stream error is received.
244
xs.dataReceived("<stream:stream xmlns='jabber:client' "
245
"xmlns:stream='http://etherx.jabber.org/streams' "
246
"from='example.com' id='12345' version='1.0'>")
247
xs.dataReceived("<stream:error/>")
248
self.assert_(self.gotStreamError)
249
self.assert_(self.gotStreamEnd)
252
def testSendStreamErrorInitiating(self):
254
Test sendStreamError on an initiating xmlstream with a header sent.
256
An error should be sent out and the connection lost.
262
xs.sendStreamError(error.StreamError('version-unsupported'))
263
self.assertNotEqual('', xs.transport.value())
264
self.assert_(self.gotStreamEnd)
267
def testSendStreamErrorInitiatingNoHeader(self):
269
Test sendStreamError on an initiating xmlstream without having sent a
272
In this case, no header should be generated. Also, the error should
273
not be sent out on the stream. Just closing the connection.
278
xs.sendStreamError(error.StreamError('version-unsupported'))
279
self.assertNot(xs._headerSent)
280
self.assertEqual('', xs.transport.value())
281
self.assert_(self.gotStreamEnd)
284
def testSendStreamErrorReceiving(self):
286
Test sendStreamError on a receiving xmlstream with a header sent.
288
An error should be sent out and the connection lost.
291
xs.initiating = False
294
xs.sendStreamError(error.StreamError('version-unsupported'))
295
self.assertNotEqual('', xs.transport.value())
296
self.assert_(self.gotStreamEnd)
299
def testSendStreamErrorReceivingNoHeader(self):
301
Test sendStreamError on a receiving xmlstream without having sent a
304
In this case, a header should be generated. Then, the error should
305
be sent out on the stream followed by closing the connection.
308
xs.initiating = False
310
xs.sendStreamError(error.StreamError('version-unsupported'))
311
self.assert_(xs._headerSent)
312
self.assertNotEqual('', xs.transport.value())
313
self.assert_(self.gotStreamEnd)
316
def testOnDocumentStart(self):
318
Test onDocumentStart to fill the appropriate attributes from the
319
stream header and stream start event.
323
xs.dataReceived("<stream:stream xmlns='jabber:client' "
324
"xmlns:stream='http://etherx.jabber.org/streams' "
325
"from='example.com' id='12345' version='1.0'>")
326
self.assert_(self.gotStreamStart)
327
self.assertEqual((1, 0), xs.version)
328
self.assertEqual('12345', xs.sid)
329
xs.dataReceived("<stream:features>"
330
"<test xmlns='testns'/>"
331
"</stream:features>")
332
self.assertIn(('testns', 'test'), xs.features)
335
def testOnDocumentStartLegacy(self):
337
Test onDocumentStart to fill the appropriate attributes from the
338
stream header and stream start event for a pre-XMPP-1.0 header.
341
xs.dataReceived("<stream:stream xmlns='jabber:client' "
342
"xmlns:stream='http://etherx.jabber.org/streams' "
343
"from='example.com' id='12345'>")
344
self.assert_(self.gotStreamStart)
345
self.assertEqual((0, 0), xs.version)
350
Test resetting the XML stream to start a new layer.
356
self.assertNotEqual(stream, xs.stream)
357
self.assertNot(xs._headerSent)
362
Test send with various types of objects.
365
xs.send('<presence/>')
366
self.assertEqual(xs.transport.value(), '<presence/>')
369
el = domish.Element(('testns', 'presence'))
371
self.assertEqual(xs.transport.value(), '<presence/>')
374
el = domish.Element(('http://etherx.jabber.org/streams', 'features'))
376
self.assertEqual(xs.transport.value(), '<stream:features/>')
379
def testAuthenticator(self):
381
Test that the associated authenticator is correctly called.
385
associateWithStream = []
387
class TestAuthenticator:
388
def connectionMade(self):
389
connectionMade.append(None)
391
def streamStarted(self):
392
streamStarted.append(None)
394
def associateWithStream(self, xs):
395
associateWithStream.append(xs)
397
a = TestAuthenticator()
398
xs = xmlstream.XmlStream(a)
399
self.assertEqual([xs], associateWithStream)
401
self.assertEqual([None], connectionMade)
402
xs.dataReceived("<stream:stream xmlns='jabber:client' "
403
"xmlns:stream='http://etherx.jabber.org/streams' "
404
"from='example.com' id='12345'>")
405
self.assertEqual([None], streamStarted)
407
self.assertEqual([None], connectionMade)
411
class TestError(Exception):
416
class ConnectAuthenticatorTest(unittest.TestCase):
419
self.gotAuthenticated = False
420
self.initFailure = None
421
self.authenticator = xmlstream.ConnectAuthenticator('otherHost')
422
self.xmlstream = xmlstream.XmlStream(self.authenticator)
423
self.xmlstream.addObserver('//event/stream/authd', self.onAuthenticated)
424
self.xmlstream.addObserver('//event/xmpp/initfailed', self.onInitFailed)
427
def onAuthenticated(self, obj):
428
self.gotAuthenticated = True
431
def onInitFailed(self, failure):
432
self.initFailure = failure
435
def testSucces(self):
437
Test successful completion of an initialization step.
440
def initialize(self):
444
self.xmlstream.initializers = [init]
446
self.authenticator.initializeStream()
447
self.assertEqual([], self.xmlstream.initializers)
448
self.assert_(self.gotAuthenticated)
451
def testFailure(self):
453
Test failure of an initialization step.
456
def initialize(self):
460
self.xmlstream.initializers = [init]
462
self.authenticator.initializeStream()
463
self.assertEqual([init], self.xmlstream.initializers)
464
self.assertFalse(self.gotAuthenticated)
465
self.assertNotIdentical(None, self.initFailure)
466
self.assert_(self.initFailure.check(TestError))
470
class TLSInitiatingInitializerTest(unittest.TestCase):
475
self.savedSSL = xmlstream.ssl
477
self.authenticator = xmlstream.Authenticator()
478
self.xmlstream = xmlstream.XmlStream(self.authenticator)
479
self.xmlstream.send = self.output.append
480
self.xmlstream.connectionMade()
481
self.xmlstream.dataReceived("<stream:stream xmlns='jabber:client' "
482
"xmlns:stream='http://etherx.jabber.org/streams' "
483
"from='example.com' id='12345' version='1.0'>")
484
self.init = xmlstream.TLSInitiatingInitializer(self.xmlstream)
488
xmlstream.ssl = self.savedSSL
491
def testWantedSupported(self):
493
Test start when TLS is wanted and the SSL library available.
495
self.xmlstream.transport = proto_helpers.StringTransport()
496
self.xmlstream.transport.startTLS = lambda ctx: self.done.append('TLS')
497
self.xmlstream.reset = lambda: self.done.append('reset')
498
self.xmlstream.sendHeader = lambda: self.done.append('header')
500
d = self.init.start()
501
d.addCallback(self.assertEquals, xmlstream.Reset)
502
starttls = self.output[0]
503
self.assertEquals('starttls', starttls.name)
504
self.assertEquals(NS_XMPP_TLS, starttls.uri)
505
self.xmlstream.dataReceived("<proceed xmlns='%s'/>" % NS_XMPP_TLS)
506
self.assertEquals(['TLS', 'reset', 'header'], self.done)
510
if not xmlstream.ssl:
511
testWantedSupported.skip = "SSL not available"
513
def testWantedNotSupportedNotRequired(self):
515
Test start when TLS is wanted and the SSL library available.
519
d = self.init.start()
520
d.addCallback(self.assertEquals, None)
521
self.assertEquals([], self.output)
526
def testWantedNotSupportedRequired(self):
528
Test start when TLS is wanted and the SSL library available.
531
self.init.required = True
533
d = self.init.start()
534
self.assertFailure(d, xmlstream.TLSNotSupported)
535
self.assertEquals([], self.output)
540
def testNotWantedRequired(self):
542
Test start when TLS is not wanted, but required by the server.
544
tls = domish.Element(('urn:ietf:params:xml:ns:xmpp-tls', 'starttls'))
545
tls.addElement('required')
546
self.xmlstream.features = {(tls.uri, tls.name): tls}
547
self.init.wanted = False
549
d = self.init.start()
550
self.assertEquals([], self.output)
551
self.assertFailure(d, xmlstream.TLSRequired)
556
def testNotWantedNotRequired(self):
558
Test start when TLS is not wanted, but required by the server.
560
tls = domish.Element(('urn:ietf:params:xml:ns:xmpp-tls', 'starttls'))
561
self.xmlstream.features = {(tls.uri, tls.name): tls}
562
self.init.wanted = False
564
d = self.init.start()
565
d.addCallback(self.assertEqual, None)
566
self.assertEquals([], self.output)
570
def testFailed(self):
572
Test failed TLS negotiation.
574
# Pretend that ssl is supported, it isn't actually used when the
575
# server starts out with a failure in response to our initial
576
# C{starttls} stanza.
579
d = self.init.start()
580
self.assertFailure(d, xmlstream.TLSFailed)
581
self.xmlstream.dataReceived("<failure xmlns='%s'/>" % NS_XMPP_TLS)
586
class TestFeatureInitializer(xmlstream.BaseFeatureInitiatingInitializer):
587
feature = ('testns', 'test')
590
return defer.succeed(None)
594
class BaseFeatureInitiatingInitializerTest(unittest.TestCase):
597
self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator())
598
self.init = TestFeatureInitializer(self.xmlstream)
601
def testAdvertized(self):
603
Test that an advertized feature results in successful initialization.
605
self.xmlstream.features = {self.init.feature:
606
domish.Element(self.init.feature)}
607
return self.init.initialize()
610
def testNotAdvertizedRequired(self):
612
Test that when the feature is not advertized, but required by the
613
initializer, an exception is raised.
615
self.init.required = True
616
self.assertRaises(xmlstream.FeatureNotAdvertized, self.init.initialize)
619
def testNotAdvertizedNotRequired(self):
621
Test that when the feature is not advertized, and not required by the
622
initializer, the initializer silently succeeds.
624
self.init.required = False
625
self.assertIdentical(None, self.init.initialize())