~certify-web-dev/twisted/certify-production

« back to all changes in this revision

Viewing changes to twisted/web/soap.py

  • Committer: Marc Tardif
  • Date: 2010-05-20 19:56:06 UTC
  • Revision ID: marc.tardif@canonical.com-20100520195606-xdrf0ztlxhvwmmzb
Added twisted-web.

Show diffs side-by-side

added added

removed removed

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