source: ralphm-patches/c2s_server_factory.patch @ 54:03ec57713c90

Last change on this file since 54:03ec57713c90 was 54:03ec57713c90, checked in by Ralph Meijer <ralphm@…>, 11 years ago

Upstreamed Request patches, split out c2s patches in managable chunks, prepare for release of Wokkel 0.7.0.

File size: 4.1 KB
RevLine 
[54]1# HG changeset patch
2# Parent 52b9931fa5f5c6aeee617a4dad3e09d041ae4361
3Add factory for accepting client connections.
4
5The new XMPPC2SServerFactory is a server factory for accepting client
6connections. It uses `XMPPClientListenAuthenticator` to perform the
7steps for authentication and binding of a resource, and keeps a list
8of all streams.
9
10Upon loss of the connection, the service is called with `unbindResource`.
11Received stanzas cause the service's `onElement` to be called.
12
13The factory has a `deliverStanza` method to deliver stanzas to a particular
14recipient. This is used for stanzas that have different recipient addressing
15than the actual recipient (for presence and messages from a different (or no)
16resource).
17
18TODO:
19
20 * Add docstrings.
21 * Add tests.
22
23diff -r 52b9931fa5f5 wokkel/client.py
24--- a/wokkel/client.py  Wed Oct 05 09:50:55 2011 +0200
25+++ b/wokkel/client.py  Wed Oct 05 09:58:11 2011 +0200
26@@ -16,9 +16,11 @@
27 from twisted.internet import reactor
28 from twisted.names.srvconnect import SRVConnector
29 from twisted.words.protocols.jabber import client, error, sasl, xmlstream
30+from twisted.words.xish import domish, utility
31 
32 from wokkel import generic
33 from wokkel.subprotocols import StreamManager
34+from wokkel.compat import XmlStreamServerFactory
35 
36 NS_CLIENT = 'jabber:client'
37 
38@@ -345,3 +347,98 @@
39 
40 
41 
42+class RecipientUnavailable(Exception):
43+    """
44+    The addressed entity is not, or no longer, available.
45+    """
46+
47+
48+
49+class XMPPC2SServerFactory(XmlStreamServerFactory):
50+
51+    def __init__(self, service):
52+        self.service = service
53+
54+        def authenticatorFactory():
55+            return XMPPClientListenAuthenticator(service)
56+
57+        XmlStreamServerFactory.__init__(self, authenticatorFactory)
58+        self.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT,
59+                          self.onConnectionMade)
60+        self.addBootstrap(xmlstream.STREAM_AUTHD_EVENT,
61+                          self.onAuthenticated)
62+
63+        self.serial = 0
64+        self.streams = {}
65+
66+
67+    def onConnectionMade(self, xs):
68+        """
69+        Called when a client-to-server connection was made.
70+
71+        This enables traffic debugging on incoming streams.
72+        """
73+        xs.serial = self.serial
74+        self.serial += 1
75+
76+        log.msg("Client connection %d made" % xs.serial)
77+
78+        def logDataIn(buf):
79+            log.msg("RECV (%d): %r" % (xs.serial, buf))
80+
81+        def logDataOut(buf):
82+            log.msg("SEND (%d): %r" % (xs.serial, buf))
83+
84+        if self.logTraffic:
85+            xs.rawDataInFn = logDataIn
86+            xs.rawDataOutFn = logDataOut
87+
88+        xs.addObserver(xmlstream.STREAM_ERROR_EVENT, self.onError)
89+
90+
91+    def onAuthenticated(self, xs):
92+        log.msg("Client connection %d authenticated" % xs.serial)
93+
94+        xs.addObserver(xmlstream.STREAM_END_EVENT, self.onConnectionLost,
95+                                                   0, xs)
96+        xs.addObserver('/*', self.onElement, 0, xs)
97+
98+        # Record this stream as bound to the authenticated JID
99+        self.streams[xs.otherEntity] = xs
100+
101+
102+    def onConnectionLost(self, xs, reason):
103+        log.msg("Client connection %d disconnected" % xs.serial)
104+
105+        entity = xs.otherEntity
106+        self.service.unbindResource(entity.user,
107+                                    entity.host,
108+                                    entity.resource,
109+                                    reason)
110+
111+        # If the lost connections had been bound, remove the reference
112+        if xs.otherEntity in self.streams:
113+            del self.streams[xs.otherEntity]
114+
115+
116+    def onError(self, reason):
117+        log.err(reason, "Stream Error")
118+
119+
120+    def onElement(self, xs, stanza):
121+        """
122+        Called when an element was received from one of the connected streams.
123+
124+        """
125+        if stanza.handled:
126+            return
127+        else:
128+            self.service.onElement(stanza, xs.otherEntity)
129+
130+
131+    def deliverStanza(self, element, recipient):
132+        if recipient in self.streams:
133+            self.streams[recipient].send(element)
134+        else:
135+            raise RecipientUnavailable(u"There is no connection for %s" %
136+                                       recipient.full())
Note: See TracBrowser for help on using the repository browser.