Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / web / soap.py
1 # -*- test-case-name: twisted.web.test.test_soap -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5
6 """
7 SOAP support for twisted.web.
8
9 Requires SOAPpy 0.10.1 or later.
10
11 Maintainer: Itamar Shtull-Trauring
12
13 Future plans:
14 SOAPContext support of some kind.
15 Pluggable method lookup policies.
16 """
17
18 # SOAPpy
19 import SOAPpy
20
21 # twisted imports
22 from twisted.web import server, resource, client
23 from twisted.internet import defer
24
25
26 class SOAPPublisher(resource.Resource):
27     """Publish SOAP methods.
28
29     By default, publish methods beginning with 'soap_'. If the method
30     has an attribute 'useKeywords', it well get the arguments passed
31     as keyword args.
32     """
33
34     isLeaf = 1
35
36     # override to change the encoding used for responses
37     encoding = "UTF-8"
38
39     def lookupFunction(self, functionName):
40         """Lookup published SOAP function.
41
42         Override in subclasses. Default behaviour - publish methods
43         starting with soap_.
44
45         @return: callable or None if not found.
46         """
47         return getattr(self, "soap_%s" % functionName, None)
48
49     def render(self, request):
50         """Handle a SOAP command."""
51         data = request.content.read()
52
53         p, header, body, attrs = SOAPpy.parseSOAPRPC(data, 1, 1, 1)
54
55         methodName, args, kwargs, ns = p._name, p._aslist, p._asdict, p._ns
56
57         # deal with changes in SOAPpy 0.11
58         if callable(args):
59             args = args()
60         if callable(kwargs):
61             kwargs = kwargs()
62
63         function = self.lookupFunction(methodName)
64
65         if not function:
66             self._methodNotFound(request, methodName)
67             return server.NOT_DONE_YET
68         else:
69             if hasattr(function, "useKeywords"):
70                 keywords = {}
71                 for k, v in kwargs.items():
72                     keywords[str(k)] = v
73                 d = defer.maybeDeferred(function, **keywords)
74             else:
75                 d = defer.maybeDeferred(function, *args)
76
77         d.addCallback(self._gotResult, request, methodName)
78         d.addErrback(self._gotError, request, methodName)
79         return server.NOT_DONE_YET
80
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)
86
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)
93
94     def _gotError(self, failure, request, methodName):
95         e = failure.value
96         if isinstance(e, SOAPpy.faultType):
97             fault = e
98         else:
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)
103
104     def _sendResponse(self, request, response, status=200):
105         request.setResponseCode(status)
106
107         if self.encoding is not None:
108             mimeType = 'text/xml; charset="%s"' % self.encoding
109         else:
110             mimeType = "text/xml"
111         request.setHeader("Content-type", mimeType)
112         request.setHeader("Content-length", str(len(response)))
113         request.write(response)
114         request.finish()
115
116
117 class Proxy:
118     """A Proxy for making remote SOAP calls.
119
120     Pass the URL of the remote SOAP server to the constructor.
121
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'.
125     """
126
127     # at some point this should have encoding etc. kwargs
128     def __init__(self, url, namespace=None, header=None):
129         self.url = url
130         self.namespace = namespace
131         self.header = header
132
133     def _cbGotResult(self, result):
134         result = SOAPpy.parseSOAPRPC(result)
135         if hasattr(result, 'Result'):
136             return 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.
143             return result[0]
144         else:
145             return result
146
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)
154