source: TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/zsi.py @ 6586

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg-security/TI12-security/trunk/NDGSecurity/python/ndg_security_server/ndg/security/server/wsgi/zsi.py@6586
Revision 6586, 25.2 KB checked in by pjkersha, 11 years ago (diff)

Started ESG Authorisation Service implementation ndg.security.server.wsgi.authorizationservice - SAML SOAP based interface to a Policy Decision Point enabling centralised policy for a range of services.

Line 
1"""WSGI SOAP Middleware utilising the ZSI Python SOAP Package
2
3NERC DataGrid Project
4
5"""
6__author__ = "P J Kershaw"
7__date__ = "19/08/09"
8__copyright__ = "(C) 2009 Science and Technology Facilities Council"
9__contact__ = "Philip.Kershaw@stfc.ac.uk"
10__license__ = "BSD - see LICENSE file in top-level directory"
11__revision__ = "$Id: $"
12import logging
13log = logging.getLogger(__name__)
14
15import sys
16
17from ZSI.parse import ParsedSoap
18from ZSI.writer import SoapWriter
19from ZSI import fault
20from ZSI.ServiceContainer import ServiceSOAPBinding
21
22from ndg.security.server.wsgi.soap import SOAPMiddleware, SOAPMiddlewareError,\
23    SOAPMiddlewareConfigError, SOAPMiddlewareReadError
24from ndg.security.common.wssecurity.utils import DomletteReader, \
25    DomletteElementProxy
26from ndg.security.common.utils.classfactory import instantiateClass, \
27    importClass
28
29     
30class ZSIMiddlewareError(SOAPMiddlewareError):
31    """Base class for ZSI Middleware type exceptions"""
32
33   
34class ZSIMiddlewareReadError(SOAPMiddlewareReadError):
35    """ZSI Middleware read error"""
36
37
38class ZSIMiddlewareConfigError(SOAPMiddlewareConfigError):
39    """ZSI middleware configuration error"""
40
41     
42class ZSIMiddleware(SOAPMiddleware):
43    '''Middleware configurable to a given ZSI SOAP binding
44
45     @type SOAP_WRITER_KEYNAME: basestring
46     @cvar SOAP_WRITER_KEYNAME: environ key for ZSI SoapWriter instance
47     @type PARSED_SOAP_KEYNAME: basestring
48     @cvar PARSED_SOAP_KEYNAME: environ key for ZSI ParsedSoap instance
49     @type CHARSET_OPTNAME: basestring
50     @cvar CHARSET_OPTNAME: option name to for character set for output
51     @type DEFAULT_CHARSET: basestring
52     @cvar DEFAULT_CHARSET: default character setting is utf-8
53     @type PATH_OPTNAME: basestring
54     @cvar PATH_OPTNAME: option to set path for this endpoint (not including
55     domain name)
56     @type WRITE_RESPONSE_OPTNAME: basestring
57     @cvar WRITE_RESPONSE_OPTNAME: option name for flag to middleware to
58     serialise and output the SoapWriter instance
59     @type REFERENCED_FILTERS_OPTNAME: basestring
60     @cvar REFERENCED_FILTERS_OPTNAME: name for option to enable dereferencing
61     of other middleware via these environ keys
62     @type FILTER_ID_OPTNAME: basestring
63     @cvar FILTER_ID_OPTNAME: option name for environ key to enable other
64     middleware to reference this Filter
65     @type PUBLISHED_URI_OPTNAME: basestring
66     @cvar PUBLISHED_URI_OPTNAME: option name to define path for this endpoint
67     including domain name
68     @type READER_CLASS_OPTNAME: basestring
69     @cvar READER_CLASS_OPTNAME: option name for SOAP reader class
70     @type WRITERCLASS_OPTNAME: basestring
71     @cvar WRITERCLASS_OPTNAME: option name for SOAP writer class
72     ''' 
73   
74    SOAP_WRITER_KEYNAME = 'ZSI.writer.SoapWriter'
75    PARSED_SOAP_KEYNAME = 'ZSI.parse.ParsedSoap'
76   
77    CHARSET_OPTNAME = 'charset'
78    DEFAULT_CHARSET = '; charset=utf-8'
79    PATH_OPTNAME = 'path'
80    WRITE_RESPONSE_OPTNAME = 'writeResponse'
81    REFERENCED_FILTERS_OPTNAME = 'referencedFilters'
82    FILTER_ID_OPTNAME = 'filterID'
83    PUBLISHED_URI_OPTNAME = 'publishedURI'
84    READER_CLASS_OPTNAME = 'readerclass'
85    WRITERCLASS_OPTNAME = 'writerclass'
86   
87    def __init__(self, app):
88        log.debug("ZSIMiddleware.__init__ ...")
89        super(ZSIMiddleware, self).__init__()
90       
91        self._app = app
92        self.__charset = ZSIMiddleware.DEFAULT_CHARSET
93        self.__path = '/'
94        self.__referencedFilterKeys = None
95        self.__publishedURI = None
96        self.__readerClass = None
97        self.__writerClass = None
98        self.__writeResponseSet = None
99        self.__filterID = None
100
101    def _getCharset(self):
102        return self.__charset
103
104    def _setCharset(self, value):
105        if not isinstance(value, basestring):
106            raise TypeError('Expecting string type for "charset" got %r' %
107                            type(value))
108        self.__charset = value
109
110    def _getPath(self):
111        return self.__path
112
113    def _setPath(self, value):
114        if not isinstance(value, basestring):
115            raise TypeError('Expecting string type for "path" got %r' %
116                            type(value))
117        self.__path = value
118
119    def _getPublishedURI(self):
120        return self.__publishedURI
121
122    def _setPublishedURI(self, value):
123        if not isinstance(value, (basestring, type(None))):
124            raise TypeError('Expecting string or None type for "publishedURI" '
125                            'got %r' % type(value))
126        self.__publishedURI = value
127
128    def _getReaderClass(self):
129        return self.__readerClass
130
131    def _setReaderClass(self, value):
132        self.__readerClass = value
133
134    def _getWriterClass(self):
135        return self.__writerClass
136
137    def _setWriterClass(self, value):
138        self.__writerClass = value
139
140    def _getWriteResponseSet(self):
141        return self.__writeResponseSet
142
143    def _setWriteResponseSet(self, value):
144        if not isinstance(value, bool):
145            raise TypeError('Expecting %r for "writeResponseSet" type got %r' %
146                            (bool, type(value)))
147       
148        self.__writeResponseSet = value
149
150    def _getFilterID(self):
151        return self.__filterID
152
153    def _setFilterID(self, value):
154        if not isinstance(value, (basestring, type(None))):
155            raise TypeError('Expecting string or None type for "filterID" got '
156                            '%r' % type(value))
157        self.__filterID = value
158
159    charset = property(_getCharset, _setCharset, 
160                       doc="character set for response")
161
162    path = property(_getPath, _setPath, doc="Path for endpoint")
163
164    publishedURI = property(_getPublishedURI, _setPublishedURI, 
165                            doc="fully qualified path for endpoint")
166
167    readerClass = property(_getReaderClass, _setReaderClass, 
168                           doc="ZSI Reader class")
169
170    writeResponseSet = property(_getWriteResponseSet, _setWriteResponseSet, 
171                                doc="boolean set to True to write out a "
172                                    "response from this middleware")
173
174    writerClass = property(_getWriterClass, _setWriterClass, 
175                           doc="ZSI Writer Class")
176
177    filterID = property(_getFilterID, _setFilterID, 
178                        doc="enable the instance of this middleware to be "
179                            "referenced in environ by this identifier")
180
181    def initialise(self, global_conf, prefix='', **app_conf):
182        """Set-up ZSI middleware interface attributes.  Overloaded base class
183        method to enable custom settings from app_conf
184       
185        @type global_conf: dict       
186        @param global_conf: PasteDeploy global configuration dictionary
187        @type prefix: basestring
188        @param prefix: prefix for configuration items
189        @type app_conf: dict       
190        @param app_conf: PasteDeploy application specific configuration
191        dictionary
192        """
193        charsetOptName = prefix + ZSIMiddleware.CHARSET_OPTNAME
194        if charsetOptName in app_conf:
195            self.charset = '; charset=' + app_conf[charsetOptName]
196        else:
197            self.charset = '; charset=utf-8'
198           
199        pathOptName = prefix + ZSIMiddleware.PATH_OPTNAME
200        if pathOptName in app_conf:
201            if not app_conf[pathOptName].endswith('/'):
202                self.path = app_conf[pathOptName] + '/'
203            else:
204                self.path = app_conf[pathOptName]
205
206        # This flag if set to True causes this handler to call the
207        # start_response method and output the SOAP response
208        writeResponseOptName = prefix + ZSIMiddleware.WRITE_RESPONSE_OPTNAME
209        self.writeResponseSet = ZSIMiddleware.str2Bool(app_conf.get(
210                                                    writeResponseOptName, ''))
211
212        # Check for a list of other filters to be referenced by this one
213        referencedFiltersOptName = prefix + \
214                                    ZSIMiddleware.REFERENCED_FILTERS_OPTNAME
215        if referencedFiltersOptName in app_conf:
216            # __call__  may reference any filters in environ keyed by these
217            # keywords
218            self.referencedFilterKeys = app_conf.pop(
219                                            referencedFiltersOptName).split()
220
221       
222        filterIdOptName = prefix + ZSIMiddleware.FILTER_ID_OPTNAME
223        self.filterID = app_conf.pop(filterIdOptName, None)
224       
225        # The endpoint that this services will be referenced from externally.
226        # e.g. the Session Manager client running locally can check the
227        # input URI and compare with this value to see if the request is
228        # actually to the local Session Manager instance
229        publishedUriOptName = prefix + ZSIMiddleware.PUBLISHED_URI_OPTNAME
230        self.publishedURI = app_conf.pop(publishedUriOptName, None)
231       
232        readerClassOptName = prefix + ZSIMiddleware.READER_CLASS_OPTNAME
233        if readerClassOptName in app_conf:
234            readerClassName = app_conf.pop(readerClassOptName)
235            self.readerClass = importClass(readerClassName)
236        else:
237            self.readerClass = DomletteReader
238           
239        writerClassOptName = prefix + ZSIMiddleware.WRITERCLASS_OPTNAME
240        if writerClassOptName in app_conf:
241            writerClassName = app_conf.pop(writerClassOptName)
242            self.writerClass = importClass(writerClassName)
243        else:
244            self.writerClass = DomletteElementProxy
245
246    def __call__(self, environ, start_response):
247        log.debug("ZSIMiddleware.__call__")
248                       
249        # Derived class must implement SOAP Response via overloaded version of
250        # this method.  ParsedSoap object is available as a key in environ via
251        # the parseRequest method
252       
253        return self.writeResponse(environ, start_response)
254
255   
256    def _initCall(self, environ, start_response):
257        '''Sub-divided out from __call__ to enable derived classes to easily
258        include this functionality:
259         - Set a reference to this WSGI filter in environ if filterID was
260        set in the config and
261         - check the request to see if this filter should handle it
262        '''
263       
264        # Add any filter references for this WSGI component regardless of the
265        # current request ID.  This ensures that other WSGI components called
266        # may reference it if they need to.
267        self.addFilter2Environ(environ)
268       
269        # Apply filter for calls
270        if not self.__class__.isSOAPMessage(environ):
271            log.debug("ZSIMiddleware.__call__: skipping non-SOAP call")
272            return self._app(environ, start_response)
273       
274        elif not self.pathMatch(environ):
275            log.debug("ZSIMiddleware.__call__: path doesn't match SOAP "
276                      "service endpoint")
277            return self._app(environ, start_response)
278       
279        elif self.__class__.isSOAPFaultSet(environ):
280            # This MUST be checked in a overloaded version especially in
281            # consideration of security: e.g. an upstream signature
282            # verification may have found an error in a signature
283            log.debug("ZSIMiddleware.__call__: SOAP fault set by previous "
284                      "SOAP middleware filter")
285            return self._app(environ, start_response)
286
287        # Parse input into a ZSI ParsedSoap object set as a key in environ
288        try:
289            self.parseRequest(environ)
290        except Exception, e:
291            sw = self.exception2SOAPFault(environ, e)
292            self.setSOAPWriter(environ, sw)
293            return self.writeResponse(environ, start_response)
294       
295        # Return None to __call__ to indicate that it can proceed with
296        # processing the input
297        return None
298
299    def exception2SOAPFault(self, environ, exception):
300        '''Convert an exception into a SOAP fault message'''
301        soapFault = fault.FaultFromException(exception, 
302                                             None,
303                                             tb=sys.exc_info()[2])
304        sw = SoapWriter(outputclass=self.writerClass)
305        soapFault.serialize(sw)
306        environ[ZSIMiddleware.SOAP_FAULT_SET_KEYNAME] = True
307        return sw
308   
309    pathMatch = lambda self, environ: environ['PATH_INFO'] == self.path
310   
311    def parseRequest(self, environ):
312        '''Parse SOAP message from environ['wsgi.input']
313       
314        Reading from environ['wsgi.input'] may be a destructive process so the
315        content is saved in a ZSI.parse.ParsedSoap object for use by SOAP
316        handlers which follow in the chain
317       
318        environ['ZSI.parse.ParsedSoap'] may be set to a ParsedSoap object
319        parsed by a SOAP handler ahead of the current one in the chain.  In
320        this case, don't re-parse.  If NOT parsed, parse and set
321        'ZSI.parse.ParsedSoap' environ key'''
322       
323        # Check for ParsedSoap object set in environment, if not present,
324        # make one
325        ps = environ.get(ZSIMiddleware.PARSED_SOAP_KEYNAME)
326        if ps is None:
327            # TODO: allow for chunked data
328            contentLength = int(environ['CONTENT_LENGTH'])
329            soapIn = environ['wsgi.input'].read(contentLength)
330            if len(soapIn) < contentLength:
331                raise ZSIMiddlewareReadError("Expecting %d content length; "
332                                             "received %d instead." % 
333                                             (contentLength, len(soapIn)))
334           
335            log.debug("SOAP Request for handler %r" % ZSIMiddleware)
336            log.debug("_" * 80)
337            log.debug(soapIn)
338            log.debug("_" * 80)
339           
340            ps = ParsedSoap(soapIn, readerclass=self.readerClass)
341            environ[ZSIMiddleware.PARSED_SOAP_KEYNAME] = ps
342           
343        return environ[ZSIMiddleware.PARSED_SOAP_KEYNAME]
344
345
346    def writeResponse(self, environ, start_response, errorCode=None):
347        '''This method serializes the SOAP output and sets the response header.
348        It's the final step and should be called in the last SOAP handler in
349        a chain of handlers or else specify it in the ini file as the last
350        SOAP handler'''
351       
352        # This flag must be set to True to write out the final response from
353        # this handler
354        if self.writeResponseSet == False:
355            return self._app(environ, start_response)
356       
357        sw = self.getSOAPWriter(environ)
358        soapOut = str(sw)
359       
360        if errorCode is None:
361            if self.__class__.isSOAPFaultSet(environ):
362                errorCode = "500 Internal Server Error"
363            else:
364                errorCode = "200 OK"
365               
366        log.debug("SOAP Response for handler %r" % self.__class__)
367        log.debug("_" * 80)
368        log.debug(soapOut)
369        log.debug("_" * 80)
370        start_response(errorCode,
371                       [('content-type', 'text/xml' + self.charset),
372                        ('content-length', str(len(soapOut)))])
373        return soapOut
374
375    @classmethod
376    def getSOAPWriter(cls, environ):
377        '''Access SoapWriter object set in environment by this classes' call
378        method'''
379       
380        sw = environ.get(ZSIMiddleware.SOAP_WRITER_KEYNAME)
381        if sw is None:
382            raise KeyError("Expecting '%s' key in environ: missing call to "
383                           "ZSIMiddleware?" % ZSIMiddleware.SOAP_WRITER_KEYNAME)
384        return sw
385   
386    @classmethod
387    def setSOAPWriter(cls, environ, sw):
388        '''Set SoapWriter object in environment'''   
389        environ[ZSIMiddleware.SOAP_WRITER_KEYNAME] = sw
390
391    def addFilter2Environ(self, environ):
392        '''Add a key to the current application in the environment so that
393        other middleware can reference it.  This is dependent on filterID set
394        in app_conf'''
395        if self.filterID is not None:           
396            if self.filterID in environ:
397                raise ZSIMiddlewareConfigError("An filterID key '%s' is "
398                                                "already set in environ" % 
399                                                self.filterID)
400            environ[self.filterID] = self
401
402
403class SOAPBindingMiddleware(ZSIMiddleware): 
404    '''Interface to apply a ZSI ServiceSOAPBinding type SOAP service'''
405   
406    SERVICE_SOAP_BINDING_CLASSNAME_OPTNAME = 'serviceSOAPBindingClass'
407    SERVICE_SOAP_BINDING_PROPPREFIX_OPTNAME = 'serviceSOAPBindingPropPrefix'
408    DEFAULT_SERVICE_SOAP_BINDING_PROPPREFIX_OPTNAME = \
409                            'ndg.security.server.wsgi.zsi.serviceSOAPBinding.'
410                           
411    SERVICE_SOAP_BINDING_ENVIRON_KEYNAME_OPTNAME = \
412                            'serviceSOAPBindingEnvironKeyName' 
413    DEFAULT_SERVICE_SOAP_BINDING_ENVIRON_KEYNAME = \
414                            'ndg.security.servier.wsgi.zsi.serviceSOAPBinding'
415    ENABLE_WSDL_QUERY_OPTNAME = 'enableWSDLQuery' 
416    DEFAULT_ENABLE_WSDL_QUERY_VALUE = False
417    SOAP_METHOD_STRING = 'soap_%s'
418   
419    def __init__(self, app):
420        super(SOAPBindingMiddleware, self).__init__(app)
421        self.__serviceSOAPBindingKeyName = None
422        self.__serviceSOAPBinding = None
423        self.__enableWSDLQuery = False
424       
425    def _getServiceSOAPBinding(self):
426        return self.__serviceSOAPBinding
427
428    def _setServiceSOAPBinding(self, value):
429        """Instance must be ZSI ServiceSOAPBinding derived type"""
430        if not isinstance(value, ServiceSOAPBinding):
431            raise TypeError('Expecting %r type for "serviceSOAPBinding"; got '
432                            '%r' % (ServiceSOAPBinding, type(value)))
433        self.__serviceSOAPBinding = value
434
435    serviceSOAPBinding = property(fget=_getServiceSOAPBinding, 
436                                  fset=_setServiceSOAPBinding, 
437                                  doc="Instance of ZSI ServiceSOAPBinding "
438                                      "derived type determines the behaviour "
439                                      "of the SOAP callbacks")
440
441    def _getServiceSOAPBindingKeyName(self):
442        return self.__serviceSOAPBindingKeyName
443
444    def _setServiceSOAPBindingKeyName(self, value):
445        """Instance must be ZSI ServiceSOAPBindingKeyName derived type"""
446        if not isinstance(value, basestring):
447            raise TypeError('Expecting bool type for "enableWSDLQuery"; got '
448                            '%r' % type(value))
449        self.__serviceSOAPBindingKeyName = value
450
451    serviceSOAPBindingKeyName = property(fget=_getServiceSOAPBindingKeyName, 
452                                         fset=_setServiceSOAPBindingKeyName, 
453                                         doc="Keyword to key WSGI environ for "
454                                             "SOAP Binding middleware instance")
455
456    def _getEnableWSDLQuery(self):
457        return self.__enableWSDLQuery
458
459    def _setEnableWSDLQuery(self, value):
460        if not isinstance(value, bool):
461            raise TypeError('Expecting bool type for "enableWSDLQuery"; got '
462                            '%r' % type(value))
463        self.__enableWSDLQuery = value
464       
465    enableWSDLQuery = property(fget=_getEnableWSDLQuery, 
466                               fset=_setEnableWSDLQuery, 
467                               doc="Enable service to publish the WSDL via "
468                                   "the ?wsdl query argument appended to the "
469                                   "endpoint")
470
471    def initialise(self, global_conf, prefix='', **app_conf):
472        """Set-up ZSI SOAP Binding middleware interface attributes.
473         Overloaded base class method to enable custom settings from app_conf
474       
475        @type global_conf: dict       
476        @param global_conf: PasteDeploy global configuration dictionary
477        @type prefix: basestring
478        @param prefix: prefix for configuration items
479        @type app_conf: dict       
480        @param app_conf: PasteDeploy application specific configuration
481        dictionary
482        """
483        super(SOAPBindingMiddleware, self).initialise(global_conf, 
484                                                      prefix=prefix,
485                                                      **app_conf)
486       
487        serviceSOAPBindingEnvironKeyNameOptName = prefix + \
488            SOAPBindingMiddleware.SERVICE_SOAP_BINDING_ENVIRON_KEYNAME_OPTNAME
489        serviceSOAPBindingClassNameOptName = prefix + \
490            SOAPBindingMiddleware.SERVICE_SOAP_BINDING_CLASSNAME_OPTNAME
491                           
492        if serviceSOAPBindingEnvironKeyNameOptName in app_conf and \
493           serviceSOAPBindingClassNameOptName in app_conf:
494            raise ZSIMiddlewareConfigError('Only "%s" or "%s" may be set; not '
495                                           ' both' % 
496            (SOAPBindingMiddleware.SERVICE_SOAP_BINDING_ENVIRON_KEYNAME_OPTNAME,
497             SOAPBindingMiddleware.SERVICE_SOAP_BINDING_CLASSNAME_OPTNAME))
498           
499        if serviceSOAPBindingClassNameOptName in app_conf:
500            # Instantiate the binding from the config settings
501            modName, className = app_conf[
502                            serviceSOAPBindingClassNameOptName].rsplit('.', 1)
503               
504            # Filter
505            prefixOptName = prefix + \
506                SOAPBindingMiddleware.SERVICE_SOAP_BINDING_PROPPREFIX_OPTNAME
507            prefix = app_conf.get(prefixOptName,
508                            SOAPBindingMiddleware.
509                            DEFAULT_SERVICE_SOAP_BINDING_PROPPREFIX_OPTNAME)
510           
511            serviceSOAPBindingKw = dict([(k.replace(prefix, ''), v)
512                                         for k, v in app_conf.items()
513                                         if k.startswith(prefix)])
514   
515            self.serviceSOAPBinding = instantiateClass(modName,
516                                       className,
517                                       objectType=ServiceSOAPBinding,
518                                       classProperties=serviceSOAPBindingKw)           
519        else: 
520            # Alternatively, reference another binding instance made available
521            # by upstream middleware via environ
522            self.serviceSOAPBindingKeyName = app_conf.get(
523                            serviceSOAPBindingEnvironKeyNameOptName,
524                            SOAPBindingMiddleware.
525                            DEFAULT_SERVICE_SOAP_BINDING_ENVIRON_KEYNAME)
526       
527       
528        # Flag to enable display of WSDL via wsdl query arg in a GET request
529        enableWSDLQueryOptName = prefix + \
530                                SOAPBindingMiddleware.ENABLE_WSDL_QUERY_OPTNAME
531        self.enableWSDLQuery = SOAPBindingMiddleware.str2Bool(
532                                    app_conf.get(enableWSDLQueryOptName, ''))   
533   
534    def __call__(self, environ, start_response):
535        log.debug("SOAPBindingMiddleware.__call__ ...")
536             
537        # Get a reference to the service SOAP binding from environ or if not,
538        # from the binding instantiated at initialisation
539        serviceSOAPBinding = environ.get(self.serviceSOAPBindingKeyName,
540                                         self.serviceSOAPBinding)
541        if serviceSOAPBinding is None:
542            raise ZSIMiddlewareConfigError('No SOAP service binding set in '
543                                           'environ or configured from start'
544                                           '-up')
545             
546        if self.pathMatch(environ) and self.enableWSDLQuery and \
547           environ.get('REQUEST_METHOD', '') == 'GET' and \
548           environ.get('QUERY_STRING', '') == 'wsdl':
549            wsdl = serviceSOAPBinding._wsdl
550            start_response("200 OK", [('Content-type', 'text/xml'),
551                                      ('Content-length', str(len(wsdl)))])
552            return wsdl
553               
554        # Apply filter for calls
555        response = self._initCall(environ, start_response)
556        if response is not None:
557            return response
558         
559        try:
560            # Other filters in the middleware chain may be passed by setting
561            # a reference to them in the config.  This is useful if the SOAP
562            # binding code needs to access results from upstream middleware
563            # e.g. check output from signature verification filter
564            if hasattr(self, 'referencedFilterKeys'):
565                try:
566                    serviceSOAPBinding.referencedWSGIFilters = \
567                                    dict([(i, environ[i]) 
568                                          for i in self.referencedFilterKeys])
569                except KeyError:
570                    raise ZSIMiddlewareConfigError('No filter ID "%s" found '
571                                                   'in environ' % i)   
572            ps = self.parseRequest(environ)
573               
574            # Map SOAP Action to method in binding class
575            soapActionName = environ[
576                            SOAPBindingMiddleware.SOAP_ACTION_ENVIRON_KEYNAME
577                            ].strip('"')
578            soapMethodName = SOAPBindingMiddleware.SOAP_METHOD_STRING % \
579                            soapActionName
580                   
581           
582            method = getattr(serviceSOAPBinding, soapMethodName)           
583            resp = method(ps)
584        except Exception, e:
585            sw = self.exception2SOAPFault(environ, e)
586        else: 
587            # Serialize output using SOAP writer class
588            sw = SoapWriter(outputclass=self.writerClass)
589            sw.serialize(resp)
590       
591        # Make SoapWriter object available to any SOAP filters that follow
592        self.setSOAPWriter(environ, sw)
593        soapOut = str(sw)
594
595        return self.writeResponse(environ, start_response)
Note: See TracBrowser for help on using the repository browser.