source: ralphm-patches/c2s_server_factory.patch @ 57:0d8b6cf41728

Last change on this file since 57:0d8b6cf41728 was 57:0d8b6cf41728, checked in by Ralph Meijer <ralphm@…>, 10 years ago

Wokkel 0.7.0 release, clean up various patches.

File size: 4.0 KB
RevLine 
[54]1# HG changeset patch
[57]2# Parent d76497171af8f3acf1efd2c8433fbdc3c4a55f92
[54]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
[57]23diff -r d76497171af8 wokkel/client.py
24--- a/wokkel/client.py  Wed Nov 30 09:31:07 2011 +0100
25+++ b/wokkel/client.py  Wed Nov 30 09:32:01 2011 +0100
26@@ -20,6 +20,7 @@
27 from twisted.words.xish import domish
[54]28 
29 from wokkel import generic
[57]30+from wokkel.compat import XmlStreamServerFactory
[54]31 from wokkel.subprotocols import StreamManager
32 
33 NS_CLIENT = 'jabber:client'
[57]34@@ -347,3 +348,98 @@
[54]35 
36 
37 
38+class RecipientUnavailable(Exception):
39+    """
40+    The addressed entity is not, or no longer, available.
41+    """
42+
43+
44+
45+class XMPPC2SServerFactory(XmlStreamServerFactory):
46+
47+    def __init__(self, service):
48+        self.service = service
49+
50+        def authenticatorFactory():
51+            return XMPPClientListenAuthenticator(service)
52+
53+        XmlStreamServerFactory.__init__(self, authenticatorFactory)
54+        self.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT,
55+                          self.onConnectionMade)
56+        self.addBootstrap(xmlstream.STREAM_AUTHD_EVENT,
57+                          self.onAuthenticated)
58+
59+        self.serial = 0
60+        self.streams = {}
61+
62+
63+    def onConnectionMade(self, xs):
64+        """
65+        Called when a client-to-server connection was made.
66+
67+        This enables traffic debugging on incoming streams.
68+        """
69+        xs.serial = self.serial
70+        self.serial += 1
71+
72+        log.msg("Client connection %d made" % xs.serial)
73+
74+        def logDataIn(buf):
75+            log.msg("RECV (%d): %r" % (xs.serial, buf))
76+
77+        def logDataOut(buf):
78+            log.msg("SEND (%d): %r" % (xs.serial, buf))
79+
80+        if self.logTraffic:
81+            xs.rawDataInFn = logDataIn
82+            xs.rawDataOutFn = logDataOut
83+
84+        xs.addObserver(xmlstream.STREAM_ERROR_EVENT, self.onError)
85+
86+
87+    def onAuthenticated(self, xs):
88+        log.msg("Client connection %d authenticated" % xs.serial)
89+
90+        xs.addObserver(xmlstream.STREAM_END_EVENT, self.onConnectionLost,
91+                                                   0, xs)
92+        xs.addObserver('/*', self.onElement, 0, xs)
93+
94+        # Record this stream as bound to the authenticated JID
95+        self.streams[xs.otherEntity] = xs
96+
97+
98+    def onConnectionLost(self, xs, reason):
99+        log.msg("Client connection %d disconnected" % xs.serial)
100+
101+        entity = xs.otherEntity
102+        self.service.unbindResource(entity.user,
103+                                    entity.host,
104+                                    entity.resource,
105+                                    reason)
106+
107+        # If the lost connections had been bound, remove the reference
108+        if xs.otherEntity in self.streams:
109+            del self.streams[xs.otherEntity]
110+
111+
112+    def onError(self, reason):
113+        log.err(reason, "Stream Error")
114+
115+
116+    def onElement(self, xs, stanza):
117+        """
118+        Called when an element was received from one of the connected streams.
119+
120+        """
121+        if stanza.handled:
122+            return
123+        else:
124+            self.service.onElement(stanza, xs.otherEntity)
125+
126+
127+    def deliverStanza(self, element, recipient):
128+        if recipient in self.streams:
129+            self.streams[recipient].send(element)
130+        else:
131+            raise RecipientUnavailable(u"There is no connection for %s" %
132+                                       recipient.full())
Note: See TracBrowser for help on using the repository browser.