1
# -*- test-case-name: twisted.web.test.test_soap -*-
2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
# See LICENSE for details.
7
SOAP support for twisted.web.
9
Requires SOAPpy 0.10.1 or later.
11
Maintainer: Itamar Shtull-Trauring
14
SOAPContext support of some kind.
15
Pluggable method lookup policies.
22
from twisted.web import server, resource, client
23
from twisted.internet import defer
26
class SOAPPublisher(resource.Resource):
27
"""Publish SOAP methods.
29
By default, publish methods beginning with 'soap_'. If the method
30
has an attribute 'useKeywords', it well get the arguments passed
36
# override to change the encoding used for responses
39
def lookupFunction(self, functionName):
40
"""Lookup published SOAP function.
42
Override in subclasses. Default behaviour - publish methods
45
@return: callable or None if not found.
47
return getattr(self, "soap_%s" % functionName, None)
49
def render(self, request):
50
"""Handle a SOAP command."""
51
data = request.content.read()
53
p, header, body, attrs = SOAPpy.parseSOAPRPC(data, 1, 1, 1)
55
methodName, args, kwargs, ns = p._name, p._aslist, p._asdict, p._ns
57
# deal with changes in SOAPpy 0.11
63
function = self.lookupFunction(methodName)
66
self._methodNotFound(request, methodName)
67
return server.NOT_DONE_YET
69
if hasattr(function, "useKeywords"):
71
for k, v in kwargs.items():
73
d = defer.maybeDeferred(function, **keywords)
75
d = defer.maybeDeferred(function, *args)
77
d.addCallback(self._gotResult, request, methodName)
78
d.addErrback(self._gotError, request, methodName)
79
return server.NOT_DONE_YET
81
def _methodNotFound(self, request, methodName):
82
response = SOAPpy.buildSOAP(SOAPpy.faultType("%s:Client" %
83
SOAPpy.NS.ENV_T, "Method %s not found" % methodName),
84
encoding=self.encoding)
85
self._sendResponse(request, response, status=500)
87
def _gotResult(self, result, request, methodName):
88
if not isinstance(result, SOAPpy.voidType):
89
result = {"Result": result}
90
response = SOAPpy.buildSOAP(kw={'%sResponse' % methodName: result},
91
encoding=self.encoding)
92
self._sendResponse(request, response)
94
def _gotError(self, failure, request, methodName):
96
if isinstance(e, SOAPpy.faultType):
99
fault = SOAPpy.faultType("%s:Server" % SOAPpy.NS.ENV_T,
100
"Method %s failed." % methodName)
101
response = SOAPpy.buildSOAP(fault, encoding=self.encoding)
102
self._sendResponse(request, response, status=500)
104
def _sendResponse(self, request, response, status=200):
105
request.setResponseCode(status)
107
if self.encoding is not None:
108
mimeType = 'text/xml; charset="%s"' % self.encoding
110
mimeType = "text/xml"
111
request.setHeader("Content-type", mimeType)
112
request.setHeader("Content-length", str(len(response)))
113
request.write(response)
118
"""A Proxy for making remote SOAP calls.
120
Pass the URL of the remote SOAP server to the constructor.
122
Use proxy.callRemote('foobar', 1, 2) to call remote method
123
'foobar' with args 1 and 2, proxy.callRemote('foobar', x=1)
124
will call foobar with named argument 'x'.
127
# at some point this should have encoding etc. kwargs
128
def __init__(self, url, namespace=None, header=None):
130
self.namespace = namespace
133
def _cbGotResult(self, result):
134
result = SOAPpy.parseSOAPRPC(result)
135
if hasattr(result, 'Result'):
137
elif len(result) == 1:
138
## SOAPpy 0.11.6 wraps the return results in a containing structure.
139
## This check added to make Proxy behaviour emulate SOAPProxy, which
140
## flattens the structure by default.
141
## This behaviour is OK because even singleton lists are wrapped in
142
## another singleton structType, which is almost always useless.
147
def callRemote(self, method, *args, **kwargs):
148
payload = SOAPpy.buildSOAP(args=args, kw=kwargs, method=method,
149
header=self.header, namespace=self.namespace)
150
return client.getPage(self.url, postdata=payload, method="POST",
151
headers={'content-type': 'text/xml',
152
'SOAPAction': method}
153
).addCallback(self._cbGotResult)