1
# -*- test-case-name: twisted.test.test_soap -*-
2
# Twisted, the Framework of Your Internet
3
# Copyright (C) 2001 Matthew W. Lefkowitz
5
# This library is free software; you can redistribute it and/or
6
# modify it under the terms of version 2.1 of the GNU Lesser General Public
7
# License as published by the Free Software Foundation.
9
# This library is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
# Lesser General Public License for more details.
14
# You should have received a copy of the GNU Lesser General Public
15
# License along with this library; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
"""SOAP support for twisted.web.
20
Requires SOAPpy 0.10.1 or later.
22
API Stability: unstable
24
Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org}
27
SOAPContext support of some kind.
28
Pluggable method lookup policies.
35
from twisted.web import server, resource, client
36
from twisted.internet import defer
37
from twisted.python import log, failure
40
class SOAPPublisher(resource.Resource):
41
"""Publish SOAP methods.
43
By default, publish methods beginning with 'soap_'. If the method
44
has an attribute 'useKeywords', it well get the arguments passed
50
# override to change the encoding used for responses
53
def lookupFunction(self, functionName):
54
"""Lookup published SOAP function.
56
Override in subclasses. Default behaviour - publish methods
57
starting with soap_, if they have true attribute useKeywords
58
they are expected to accept keywords.
60
@return: tuple (callable, useKeywords), or (None, None) if not found.
62
function = getattr(self, "soap_%s" % functionName, None)
64
return function, getattr(function, "useKeywords", False)
68
def render(self, request):
69
"""Handle a SOAP command."""
70
data = request.content.read()
72
p, header, body, attrs = SOAPpy.parseSOAPRPC(data, 1, 1, 1)
74
methodName, args, kwargs, ns = p._name, p._aslist, p._asdict, p._ns
76
# deal with changes in SOAPpy 0.11
82
function, useKeywords = self.lookupFunction(methodName)
85
self._methodNotFound(request, methodName)
86
return server.NOT_DONE_YET
88
if hasattr(function, "useKeywords"):
90
for k, v in kwargs.items():
92
d = defer.maybeDeferred(function, **keywords)
94
d = defer.maybeDeferred(function, *args)
96
d.addCallback(self._gotResult, request, methodName)
97
d.addErrback(self._gotError, request, methodName)
98
return server.NOT_DONE_YET
100
def _methodNotFound(self, request, methodName):
101
response = SOAPpy.buildSOAP(SOAPpy.faultType("%s:Client" % SOAPpy.NS.ENV_T,
102
"Method %s not found" % methodName),
103
encoding=self.encoding)
104
self._sendResponse(request, response, status=500)
106
def _gotResult(self, result, request, methodName):
107
if not isinstance(result, SOAPpy.voidType):
108
result = {"Result": result}
109
response = SOAPpy.buildSOAP(kw={'%sResponse' % methodName: result},
110
encoding=self.encoding)
111
self._sendResponse(request, response)
113
def _gotError(self, failure, request, methodName):
115
if isinstance(e, SOAPpy.faultType):
118
fault = SOAPpy.faultType("%s:Server" % SOAPpy.NS.ENV_T, "Method %s failed." % methodName)
119
response = SOAPpy.buildSOAP(fault, encoding=self.encoding)
120
self._sendResponse(request, response, status=500)
122
def _sendResponse(self, request, response, status=200):
123
request.setResponseCode(status)
125
if self.encoding is not None:
126
mimeType = 'text/xml; charset="%s"' % self.encoding
128
mimeType = "text/xml"
129
request.setHeader("Content-type", mimeType)
130
request.setHeader("Content-length", str(len(response)))
131
request.write(response)
136
"""A Proxy for making remote SOAP calls.
138
Pass the URL of the remote SOAP server to the constructor.
140
Use proxy.callRemote('foobar', 1, 2) to call remote method
141
'foobar' with args 1 and 2, proxy.callRemote('foobar', x=1)
142
will call foobar with named argument 'x'.
145
# at some point this should have encoding etc. kwargs
146
def __init__(self, url, namespace=None, header=None):
148
self.namespace = namespace
151
def _cbGotResult(self, result):
152
return SOAPpy.parseSOAPRPC(result).Result
154
def callRemote(self, method, *args, **kwargs):
155
payload = SOAPpy.buildSOAP(args=args, kw=kwargs, method=method,
156
header=self.header, namespace=self.namespace)
157
return client.getPage(self.url, postdata=payload, method="POST",
158
headers={'content-type': 'text/xml'}
159
).addCallback(self._cbGotResult)