1
# -*- test-case-name: twisted.words.test.test_xmlstream -*-
3
# Copyright (c) 2001-2005 Twisted Matrix Laboratories.
4
# See LICENSE for details.
6
""" XML Stream processing.
8
An XML Stream is defined as a connection over which two XML documents are
9
exchanged during the lifetime of the connection, one for each direction. The
10
unit of interaction is a direct child element of the root element (stanza).
12
The most prominent use of XML Streams is Jabber, but this module is generically
13
usable. See Twisted Words for Jabber specific protocol support.
15
Maintainer: U{Ralph Meijer<mailto:twisted@ralphm.ik.nu>}
18
from twisted.internet import protocol
19
from twisted.words.xish import domish, utility
21
STREAM_CONNECTED_EVENT = intern("//event/stream/connected")
22
STREAM_START_EVENT = intern("//event/stream/start")
23
STREAM_END_EVENT = intern("//event/stream/end")
24
STREAM_ERROR_EVENT = intern("//event/stream/error")
26
class XmlStream(protocol.Protocol, utility.EventDispatcher):
27
""" Generic Streaming XML protocol handler.
29
This protocol handler will parse incoming data as XML and dispatch events
30
accordingly. Incoming stanzas can be handled by registering observers using
31
XPath-like expressions that are matched against each stanza. See
32
L{utility.EventDispatcher} for details.
35
utility.EventDispatcher.__init__(self)
37
self.rawDataOutFn = None
38
self.rawDataInFn = None
40
def _initializeStream(self):
41
""" Sets up XML Parser. """
42
self.stream = domish.elementStream()
43
self.stream.DocumentStartEvent = self.onDocumentStart
44
self.stream.ElementEvent = self.onElement
45
self.stream.DocumentEndEvent = self.onDocumentEnd
47
### --------------------------------------------------------------
51
### --------------------------------------------------------------
53
def connectionMade(self):
54
""" Called when a connection is made.
56
Sets up the XML parser and dispatches the L{STREAM_CONNECTED_EVENT}
57
event indicating the connection has been established.
59
self._initializeStream()
60
self.dispatch(self, STREAM_CONNECTED_EVENT)
62
def dataReceived(self, data):
63
""" Called whenever data is received.
65
Passes the data to the XML parser. This can result in calls to the
66
DOM handlers. If a parse error occurs, the L{STREAM_ERROR_EVENT} event
67
is called to allow for cleanup actions, followed by dropping the
71
if self.rawDataInFn: self.rawDataInFn(data)
72
self.stream.parse(data)
73
except domish.ParserError:
74
self.dispatch(self, STREAM_ERROR_EVENT)
75
self.transport.loseConnection()
77
def connectionLost(self, reason):
78
""" Called when the connection is shut down.
80
Dispatches the L{STREAM_END_EVENT}.
82
self.dispatch(self, STREAM_END_EVENT)
85
### --------------------------------------------------------------
89
### --------------------------------------------------------------
91
def onDocumentStart(self, rootelem):
92
""" Called whenever the start tag of a root element has been received.
94
Dispatches the L{STREAM_START_EVENT}.
96
self.dispatch(self, STREAM_START_EVENT)
98
def onElement(self, element):
99
""" Called whenever a direct child element of the root element has
102
Dispatches the received element.
104
self.dispatch(element)
106
def onDocumentEnd(self):
107
""" Called whenever the end tag of the root element has been received.
109
Closes the connection. This causes C{connectionLost} being called.
111
self.transport.loseConnection()
113
def setDispatchFn(self, fn):
114
""" Set another function to handle elements. """
115
self.stream.ElementEvent = fn
117
def resetDispatchFn(self):
118
""" Set the default function (C{onElement}) to handle elements. """
119
self.stream.ElementEvent = self.onElement
122
""" Send data over the stream.
124
Sends the given C{obj} over the connection. C{obj} may be instances of
125
L{domish.Element}, L{unicode} and L{str}. The first two will be
126
properly serialized and/or encoded. L{str} objects must be in UTF-8
129
Note: because it is easy to make mistakes in maintaining a properly
130
encoded L{str} object, it is advised to use L{unicode} objects
131
everywhere when dealing with XML Streams.
133
@param obj: Object to be sent over the stream.
134
@type obj: L{domish.Element}, L{domish} or L{str}
137
if domish.IElement.providedBy(obj):
140
if isinstance(obj, unicode):
141
obj = obj.encode('utf-8')
143
if self.rawDataOutFn:
144
self.rawDataOutFn(obj)
146
self.transport.write(obj)
149
class XmlStreamFactory(protocol.ReconnectingClientFactory):
150
""" Factory for XmlStream protocol objects as a reconnection client.
152
This factory generates XmlStream objects when a connection has been
153
established. To make sure certain event observers are set up before
154
incoming data is processed, you can set up bootstrap event observers using
161
def buildProtocol(self, addr):
162
""" Create an instance of XmlStream.
164
The returned instance will have bootstrap event observers registered
165
and will proceed to handle input on an incoming connection.
170
for event, fn in self.bootstraps: xs.addObserver(event, fn)
173
def addBootstrap(self, event, fn):
174
""" Add a bootstrap event handler. """
175
self.bootstraps.append((event, fn))
177
def removeBootstrap(self, event, fn):
178
""" Remove a bootstrap event handler. """
179
self.bootstraps.remove((event, fn))