~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/web/soap.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

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