1
# -*- test-case-name: twisted.web.test.test_soap -*-
2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
"""SOAP support for twisted.web.
8
Requires SOAPpy 0.10.1 or later.
10
API Stability: unstable
12
Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
15
SOAPContext support of some kind.
16
Pluggable method lookup policies.
23
from twisted.web import server, resource, client
24
from twisted.internet import defer
27
class SOAPPublisher(resource.Resource):
28
"""Publish SOAP methods.
30
By default, publish methods beginning with 'soap_'. If the method
31
has an attribute 'useKeywords', it well get the arguments passed
37
# override to change the encoding used for responses
40
def lookupFunction(self, functionName):
41
"""Lookup published SOAP function.
43
Override in subclasses. Default behaviour - publish methods
46
@return: callable or None if not found.
48
return getattr(self, "soap_%s" % functionName, None)
50
def render(self, request):
51
"""Handle a SOAP command."""
52
data = request.content.read()
54
p, header, body, attrs = SOAPpy.parseSOAPRPC(data, 1, 1, 1)
56
methodName, args, kwargs, ns = p._name, p._aslist, p._asdict, p._ns
58
# deal with changes in SOAPpy 0.11
64
function = self.lookupFunction(methodName)
67
self._methodNotFound(request, methodName)
68
return server.NOT_DONE_YET
70
if hasattr(function, "useKeywords"):
72
for k, v in kwargs.items():
74
d = defer.maybeDeferred(function, **keywords)
76
d = defer.maybeDeferred(function, *args)
78
d.addCallback(self._gotResult, request, methodName)
79
d.addErrback(self._gotError, request, methodName)
80
return server.NOT_DONE_YET
82
def _methodNotFound(self, request, methodName):
83
response = SOAPpy.buildSOAP(SOAPpy.faultType("%s:Client" %
84
SOAPpy.NS.ENV_T, "Method %s not found" % methodName),
85
encoding=self.encoding)
86
self._sendResponse(request, response, status=500)
88
def _gotResult(self, result, request, methodName):
89
if not isinstance(result, SOAPpy.voidType):
90
result = {"Result": result}
91
response = SOAPpy.buildSOAP(kw={'%sResponse' % methodName: result},
92
encoding=self.encoding)
93
self._sendResponse(request, response)
95
def _gotError(self, failure, request, methodName):
97
if isinstance(e, SOAPpy.faultType):
100
fault = SOAPpy.faultType("%s:Server" % SOAPpy.NS.ENV_T,
101
"Method %s failed." % methodName)
102
response = SOAPpy.buildSOAP(fault, encoding=self.encoding)
103
self._sendResponse(request, response, status=500)
105
def _sendResponse(self, request, response, status=200):
106
request.setResponseCode(status)
108
if self.encoding is not None:
109
mimeType = 'text/xml; charset="%s"' % self.encoding
111
mimeType = "text/xml"
112
request.setHeader("Content-type", mimeType)
113
request.setHeader("Content-length", str(len(response)))
114
request.write(response)
119
"""A Proxy for making remote SOAP calls.
121
Pass the URL of the remote SOAP server to the constructor.
123
Use proxy.callRemote('foobar', 1, 2) to call remote method
124
'foobar' with args 1 and 2, proxy.callRemote('foobar', x=1)
125
will call foobar with named argument 'x'.
128
# at some point this should have encoding etc. kwargs
129
def __init__(self, url, namespace=None, header=None):
131
self.namespace = namespace
134
def _cbGotResult(self, result):
135
result = SOAPpy.parseSOAPRPC(result)
136
if hasattr(result, 'Result'):
138
elif len(result) == 1:
139
## SOAPpy 0.11.6 wraps the return results in a containing structure.
140
## This check added to make Proxy behaviour emulate SOAPProxy, which
141
## flattens the structure by default.
142
## This behaviour is OK because even singleton lists are wrapped in
143
## another singleton structType, which is almost always useless.
148
def callRemote(self, method, *args, **kwargs):
149
payload = SOAPpy.buildSOAP(args=args, kw=kwargs, method=method,
150
header=self.header, namespace=self.namespace)
151
return client.getPage(self.url, postdata=payload, method="POST",
152
headers={'content-type': 'text/xml',
153
'SOAPAction': method}
154
).addCallback(self._cbGotResult)