Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / words / xish / xmlstream.py
1 # -*- test-case-name: twisted.words.test.test_xmlstream -*-
2 #
3 # Copyright (c) Twisted Matrix Laboratories.
4 # See LICENSE for details.
5
6 """
7 XML Stream processing.
8
9 An XML Stream is defined as a connection over which two XML documents are
10 exchanged during the lifetime of the connection, one for each direction. The
11 unit of interaction is a direct child element of the root element (stanza).
12
13 The most prominent use of XML Streams is Jabber, but this module is generically
14 usable. See Twisted Words for Jabber specific protocol support.
15
16 Maintainer: Ralph Meijer
17 """
18
19 from twisted.python import failure
20 from twisted.internet import protocol
21 from twisted.words.xish import domish, utility
22
23 STREAM_CONNECTED_EVENT = intern("//event/stream/connected")
24 STREAM_START_EVENT = intern("//event/stream/start")
25 STREAM_END_EVENT = intern("//event/stream/end")
26 STREAM_ERROR_EVENT = intern("//event/stream/error")
27
28 class XmlStream(protocol.Protocol, utility.EventDispatcher):
29     """ Generic Streaming XML protocol handler.
30
31     This protocol handler will parse incoming data as XML and dispatch events
32     accordingly. Incoming stanzas can be handled by registering observers using
33     XPath-like expressions that are matched against each stanza. See
34     L{utility.EventDispatcher} for details.
35     """
36     def __init__(self):
37         utility.EventDispatcher.__init__(self)
38         self.stream = None
39         self.rawDataOutFn = None
40         self.rawDataInFn = None
41
42     def _initializeStream(self):
43         """ Sets up XML Parser. """
44         self.stream = domish.elementStream()
45         self.stream.DocumentStartEvent = self.onDocumentStart
46         self.stream.ElementEvent = self.onElement
47         self.stream.DocumentEndEvent = self.onDocumentEnd
48
49     ### --------------------------------------------------------------
50     ###
51     ### Protocol events
52     ###
53     ### --------------------------------------------------------------
54
55     def connectionMade(self):
56         """ Called when a connection is made.
57
58         Sets up the XML parser and dispatches the L{STREAM_CONNECTED_EVENT}
59         event indicating the connection has been established.
60         """
61         self._initializeStream()
62         self.dispatch(self, STREAM_CONNECTED_EVENT)
63
64     def dataReceived(self, data):
65         """ Called whenever data is received.
66
67         Passes the data to the XML parser. This can result in calls to the
68         DOM handlers. If a parse error occurs, the L{STREAM_ERROR_EVENT} event
69         is called to allow for cleanup actions, followed by dropping the
70         connection.
71         """
72         try:
73             if self.rawDataInFn:
74                 self.rawDataInFn(data)
75             self.stream.parse(data)
76         except domish.ParserError:
77             self.dispatch(failure.Failure(), STREAM_ERROR_EVENT)
78             self.transport.loseConnection()
79
80     def connectionLost(self, reason):
81         """ Called when the connection is shut down.
82
83         Dispatches the L{STREAM_END_EVENT}.
84         """
85         self.dispatch(reason, STREAM_END_EVENT)
86         self.stream = None
87
88     ### --------------------------------------------------------------
89     ###
90     ### DOM events
91     ###
92     ### --------------------------------------------------------------
93
94     def onDocumentStart(self, rootElement):
95         """ Called whenever the start tag of a root element has been received.
96
97         Dispatches the L{STREAM_START_EVENT}.
98         """
99         self.dispatch(self, STREAM_START_EVENT)
100
101     def onElement(self, element):
102         """ Called whenever a direct child element of the root element has
103         been received.
104
105         Dispatches the received element.
106         """
107         self.dispatch(element)
108
109     def onDocumentEnd(self):
110         """ Called whenever the end tag of the root element has been received.
111
112         Closes the connection. This causes C{connectionLost} being called.
113         """
114         self.transport.loseConnection()
115
116     def setDispatchFn(self, fn):
117         """ Set another function to handle elements. """
118         self.stream.ElementEvent = fn
119
120     def resetDispatchFn(self):
121         """ Set the default function (C{onElement}) to handle elements. """
122         self.stream.ElementEvent = self.onElement
123
124     def send(self, obj):
125         """ Send data over the stream.
126
127         Sends the given C{obj} over the connection. C{obj} may be instances of
128         L{domish.Element}, C{unicode} and C{str}. The first two will be
129         properly serialized and/or encoded. C{str} objects must be in UTF-8
130         encoding.
131
132         Note: because it is easy to make mistakes in maintaining a properly
133         encoded C{str} object, it is advised to use C{unicode} objects
134         everywhere when dealing with XML Streams.
135
136         @param obj: Object to be sent over the stream.
137         @type obj: L{domish.Element}, L{domish} or C{str}
138
139         """
140         if domish.IElement.providedBy(obj):
141             obj = obj.toXml()
142
143         if isinstance(obj, unicode):
144             obj = obj.encode('utf-8')
145
146         if self.rawDataOutFn:
147             self.rawDataOutFn(obj)
148
149         self.transport.write(obj)
150
151
152
153 class BootstrapMixin(object):
154     """
155     XmlStream factory mixin to install bootstrap event observers.
156
157     This mixin is for factories providing
158     L{IProtocolFactory<twisted.internet.interfaces.IProtocolFactory>} to make
159     sure bootstrap event observers are set up on protocols, before incoming
160     data is processed. Such protocols typically derive from
161     L{utility.EventDispatcher}, like L{XmlStream}.
162
163     You can set up bootstrap event observers using C{addBootstrap}. The
164     C{event} and C{fn} parameters correspond with the C{event} and
165     C{observerfn} arguments to L{utility.EventDispatcher.addObserver}.
166
167     @since: 8.2.
168     @ivar bootstraps: The list of registered bootstrap event observers.
169     @type bootstrap: C{list}
170     """
171
172     def __init__(self):
173         self.bootstraps = []
174
175
176     def installBootstraps(self, dispatcher):
177         """
178         Install registered bootstrap observers.
179
180         @param dispatcher: Event dispatcher to add the observers to.
181         @type dispatcher: L{utility.EventDispatcher}
182         """
183         for event, fn in self.bootstraps:
184             dispatcher.addObserver(event, fn)
185
186
187     def addBootstrap(self, event, fn):
188         """
189         Add a bootstrap event handler.
190
191         @param event: The event to register an observer for.
192         @type event: C{str} or L{xpath.XPathQuery}
193         @param fn: The observer callable to be registered.
194         """
195         self.bootstraps.append((event, fn))
196
197
198     def removeBootstrap(self, event, fn):
199         """
200         Remove a bootstrap event handler.
201
202         @param event: The event the observer is registered for.
203         @type event: C{str} or L{xpath.XPathQuery}
204         @param fn: The registered observer callable.
205         """
206         self.bootstraps.remove((event, fn))
207
208
209
210 class XmlStreamFactoryMixin(BootstrapMixin):
211     """
212     XmlStream factory mixin that takes care of event handlers.
213
214     All positional and keyword arguments passed to create this factory are
215     passed on as-is to the protocol.
216
217     @ivar args: Positional arguments passed to the protocol upon instantiation.
218     @type args: C{tuple}.
219     @ivar kwargs: Keyword arguments passed to the protocol upon instantiation.
220     @type kwargs: C{dict}.
221     """
222
223     def __init__(self, *args, **kwargs):
224         BootstrapMixin.__init__(self)
225         self.args = args
226         self.kwargs = kwargs
227
228
229     def buildProtocol(self, addr):
230         """
231         Create an instance of XmlStream.
232
233         The returned instance will have bootstrap event observers registered
234         and will proceed to handle input on an incoming connection.
235         """
236         xs = self.protocol(*self.args, **self.kwargs)
237         xs.factory = self
238         self.installBootstraps(xs)
239         return xs
240
241
242
243 class XmlStreamFactory(XmlStreamFactoryMixin,
244                        protocol.ReconnectingClientFactory):
245     """
246     Factory for XmlStream protocol objects as a reconnection client.
247     """
248
249     protocol = XmlStream
250
251     def buildProtocol(self, addr):
252         """
253         Create a protocol instance.
254
255         Overrides L{XmlStreamFactoryMixin.buildProtocol} to work with
256         a L{ReconnectingClientFactory}. As this is called upon having an
257         connection established, we are resetting the delay for reconnection
258         attempts when the connection is lost again.
259         """
260         self.resetDelay()
261         return XmlStreamFactoryMixin.buildProtocol(self, addr)