source: TI12-security/trunk/ndg_saml/ndg/soap/server/wsgi/zsi.py @ 7560

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/ndg_saml/ndg/soap/server/wsgi/zsi.py@7560
Revision 7560, 25.3 KB checked in by pjkersha, 9 years ago (diff)

Merged ndg.soap back into ndg_saml package - simpler to keep it together.

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