~landscape/zope3/newer-from-ztk

« back to all changes in this revision

Viewing changes to src/twisted/web/soap.py

  • Committer: Thomas Hervé
  • Date: 2009-07-08 13:52:04 UTC
  • Revision ID: thomas@canonical.com-20090708135204-df5eesrthifpylf8
Remove twisted copy

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