1 # -*- test-case-name: twisted.web.test.test_soap -*-
2 # Copyright (c) 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)