1 | # -*- test-case-name: wokkel.test.test_ping -*- |
---|
2 | # |
---|
3 | # Copyright (c) Ralph Meijer. |
---|
4 | # See LICENSE for details. |
---|
5 | |
---|
6 | """ |
---|
7 | XMPP Ping. |
---|
8 | |
---|
9 | The XMPP Ping protocol is documented in |
---|
10 | U{XEP-0199<http://xmpp.org/extensions/xep-0199.html>}. |
---|
11 | """ |
---|
12 | |
---|
13 | from zope.interface import implements |
---|
14 | |
---|
15 | from twisted.words.protocols.jabber.error import StanzaError |
---|
16 | from twisted.words.protocols.jabber.xmlstream import IQ, toResponse |
---|
17 | from twisted.words.protocols.jabber.xmlstream import XMPPHandler |
---|
18 | |
---|
19 | from wokkel import disco, iwokkel |
---|
20 | |
---|
21 | NS_PING = 'urn:xmpp:ping' |
---|
22 | PING_REQUEST = "/iq[@type='get']/ping[@xmlns='%s']" % NS_PING |
---|
23 | |
---|
24 | class PingClientProtocol(XMPPHandler): |
---|
25 | """ |
---|
26 | Ping client. |
---|
27 | |
---|
28 | This handler implements the protocol for sending out XMPP Ping requests. |
---|
29 | """ |
---|
30 | |
---|
31 | def ping(self, entity, sender=None): |
---|
32 | """ |
---|
33 | Send out a ping request and wait for a response. |
---|
34 | |
---|
35 | @param entity: Entity to be pinged. |
---|
36 | @type entity: L{JID<twisted.words.protocols.jabber.jid.JID>} |
---|
37 | |
---|
38 | @return: A deferred that fires upon receiving a response. |
---|
39 | @rtype: L{Deferred<twisted.internet.defer.Deferred>} |
---|
40 | |
---|
41 | @param sender: Optional sender address. |
---|
42 | @type sender: L{JID<twisted.words.protocols.jabber.jid.JID>} |
---|
43 | """ |
---|
44 | def cb(response): |
---|
45 | return None |
---|
46 | |
---|
47 | def eb(failure): |
---|
48 | failure.trap(StanzaError) |
---|
49 | exc = failure.value |
---|
50 | if exc.condition == 'service-unavailable': |
---|
51 | return None |
---|
52 | else: |
---|
53 | return failure |
---|
54 | |
---|
55 | request = IQ(self.xmlstream, 'get') |
---|
56 | request.addElement((NS_PING, 'ping')) |
---|
57 | |
---|
58 | if sender is not None: |
---|
59 | request['from'] = unicode(sender) |
---|
60 | |
---|
61 | d = request.send(entity.full()) |
---|
62 | d.addCallbacks(cb, eb) |
---|
63 | return d |
---|
64 | |
---|
65 | |
---|
66 | |
---|
67 | class PingHandler(XMPPHandler): |
---|
68 | """ |
---|
69 | Ping responder. |
---|
70 | |
---|
71 | This handler waits for XMPP Ping requests and sends a response. |
---|
72 | """ |
---|
73 | |
---|
74 | implements(iwokkel.IDisco) |
---|
75 | |
---|
76 | def connectionInitialized(self): |
---|
77 | """ |
---|
78 | Called when the XML stream has been initialized. |
---|
79 | |
---|
80 | This sets up an observer for incoming ping requests. |
---|
81 | """ |
---|
82 | self.xmlstream.addObserver(PING_REQUEST, self.onPing) |
---|
83 | |
---|
84 | |
---|
85 | def onPing(self, iq): |
---|
86 | """ |
---|
87 | Called when a ping request has been received. |
---|
88 | |
---|
89 | This immediately replies with a result response. |
---|
90 | """ |
---|
91 | response = toResponse(iq, 'result') |
---|
92 | self.xmlstream.send(response) |
---|
93 | iq.handled = True |
---|
94 | |
---|
95 | |
---|
96 | def getDiscoInfo(self, requestor, target, nodeIdentifier=''): |
---|
97 | """ |
---|
98 | Get identity and features from this entity, node. |
---|
99 | |
---|
100 | This handler supports XMPP Ping, but only without a nodeIdentifier |
---|
101 | specified. |
---|
102 | """ |
---|
103 | if not nodeIdentifier: |
---|
104 | return [disco.DiscoFeature(NS_PING)] |
---|
105 | else: |
---|
106 | return [] |
---|
107 | |
---|
108 | |
---|
109 | def getDiscoItems(self, requestor, target, nodeIdentifier=''): |
---|
110 | """ |
---|
111 | Get contained items for this entity, node. |
---|
112 | |
---|
113 | This handler does not support items. |
---|
114 | """ |
---|
115 | return [] |
---|