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