source: TI12-security/trunk/python/ndg_security_common/ndg/security/common/soap/client.py @ 5741

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/trunk/python/ndg_security_common/ndg/security/common/soap/client.py@5741
Revision 5741, 8.7 KB checked in by pjkersha, 10 years ago (diff)

Added debug logging of SOAP request for ndg.security.common.soap.client.UrlLib2SOAPClient

Line 
1"""SOAP client module for NDG Security - initially for use with SAML SOAP
2binding based Attribute Authority interface
3
4NERC DataGrid Project
5"""
6__author__ = "P J Kershaw"
7__date__ = "27/07/09"
8__copyright__ = ""
9__license__ = "BSD - see LICENSE file in top-level directory"
10__contact__ = "Philip.Kershaw@stfc.ac.uk"
11__revision__ = '$Id:$'
12import logging
13log = logging.getLogger(__name__)
14
15from ndg.security.common.soap import SOAPEnvelopeBase
16
17
18class SOAPClientError(Exception):
19    """Base class for SOAP Client exceptions"""
20
21class SOAPParseError(SOAPClientError):
22    """Error parsing SOAP response"""
23   
24           
25class SOAPClientBase(object):
26    """Handle client request to a SOAP Service
27    @cvar RESPONSE_CONTENT_TYPES: expected content type to be returned in a response
28    from a service
29    @type RESPONSE_CONTENT_TYPES: string
30    """
31    RESPONSE_CONTENT_TYPES = ('text/xml', )
32   
33    def __init__(self):
34        self.__responseEnvelopeClass = None
35
36    def _getResponseEnvelopeClass(self):
37        return self.__responseEnvelopeClass
38
39    def _setResponseEnvelopeClass(self, value):
40        if not issubclass(value, SOAPEnvelopeBase):
41            raise TypeError("Setting SOAP envelope class: expecting %r, got "
42                            "%r" % (SOAPEnvelopeBase, type(value)))
43        self.__responseEnvelopeClass = value
44
45    responseEnvelopeClass = property(fget=_getResponseEnvelopeClass, 
46                                     fset=_setResponseEnvelopeClass, 
47                                     doc="Set the class for handling "
48                                         "the SOAP envelope responses")
49     
50    def send(self, soapRequest):
51        raise NotImplementedError()
52
53
54class _SoapIOBase(object):
55    """Base class for request and response classes"""
56   
57    def __init__(self):
58        self.__envelope = None
59
60    def _getEnvelope(self):
61        return self.__envelope
62
63    def _setEnvelope(self, value):
64        if not isinstance(value, SOAPEnvelopeBase):
65            raise TypeError('Setting SOAP envelope object: expecting %r; got '
66                            '%r' % (SOAPEnvelopeBase, type(value)))
67                           
68        self.__envelope = value
69
70    envelope = property(fget=_getEnvelope, 
71                        fset=_setEnvelope, 
72                        doc="SOAP Envelope object used in request/response")
73
74       
75class SOAPRequestBase(object):
76    """Interface for SOAP requests"""
77    def __init__(self):
78        self.__url = None
79        self.__envelope = None
80
81    def _getUrl(self):
82        return self.__url
83
84    def _setUrl(self, value):
85        if not isinstance(value, basestring):
86            raise TypeError('Setting request URL: expecting %r; got '
87                            '%r' % (basestring, type(value)))
88        self.__url = value
89
90    url = property(fget=_getUrl, fset=_setUrl, doc="URL of SOAP endpoint")
91
92   
93class SOAPResponseBase(_SoapIOBase):
94    """Interface for SOAP responses"""
95
96import httplib
97import urllib2
98from urllib import addinfourl
99 
100class UrlLib2SOAPClientError(SOAPClientError):
101    """Specialisation to enable the urllib2 response to be included in the
102    exception instance as context information for the caller
103    """
104    URLLIB2RESPONSE_TYPE = addinfourl
105   
106    def __init__(self, *arg, **kw):
107        Exception.__init__(self, *arg, **kw)
108        self.__urllib2Response = None
109
110    def _getUrllib2Response(self):
111        return self.__urllib2Response
112
113    def _setUrllib2Response(self, value):
114        if not isinstance(value, UrlLib2SOAPClientError.URLLIB2RESPONSE_TYPE):
115            raise TypeError('Expecting %r type for "urllib2Response"; got %r' %
116                            (UrlLib2SOAPClientError.URLLIB2RESPONSE_TYPE, 
117                             type(value)))
118        self.__urllib2Response = value
119
120    urllib2Response = property(_getUrllib2Response, 
121                               _setUrllib2Response, 
122                               doc="Urllib2Response")
123
124
125class SOAPResponseError(UrlLib2SOAPClientError):
126    """Raise for invalid SOAP response from server"""
127       
128class HTTPException(UrlLib2SOAPClientError):
129    """Server returned HTTP code error code"""
130
131class UrlLib2SOAPRequest(SOAPRequestBase): 
132    """Interface for UrlLib2 based SOAP Requests"""
133   
134   
135class UrlLib2SOAPResponse(SOAPResponseBase):
136    """Interface for UrlLib2 based SOAP Responses"""
137    def __init__(self):
138        self.__fileobject = None
139
140    def _getFileobject(self):
141        return self.__fileobject
142
143    fileobject = property(fget=_getFileobject,
144                          doc="urllib2 file object returned from request")
145
146   
147class UrlLib2SOAPClient(SOAPClientBase):
148    """urllib2 based SOAP Client"""
149   
150    def __init__(self):
151        super(UrlLib2SOAPClient, self).__init__()
152        self.__openerDirector = urllib2.OpenerDirector()
153        self.__openerDirector.add_handler(urllib2.UnknownHandler())
154        self.__openerDirector.add_handler(urllib2.HTTPHandler())
155        self.__timeout = None
156
157    def _getTimeout(self):
158        return self.__timeout
159
160    def _setTimeout(self, value):
161        if not isinstance(value, (int, float)):
162            raise TypeError("Setting request timeout: got %r, expecting int "
163                            "float type" % type(value))
164        self.__timeout = value
165
166    timeout = property(fget=_getTimeout, 
167                       fset=_setTimeout, 
168                       doc="Timeout (seconds) for requests")
169
170    def _getOpenerDirector(self):
171        return self.__openerDirector
172
173    def _setOpenerDirector(self, value):
174        """This shouldn't need to be used much in practice because __init__
175        creates one"""
176        if not isinstance(value, urllib2.OpenerDirector):
177            raise TypeError("Setting opener: expecting %r; got %r" % 
178                            (urllib2.OpenerDirector, type(value)))
179        self.__openerDirector = value
180
181    openerDirector = property(fget=_getOpenerDirector, 
182                              fset=_setOpenerDirector, 
183                              doc="urllib2.OpenerDirector defines the "
184                                  "opener(s) for handling requests")
185   
186    def send(self, soapRequest):
187        """Make a request to the given URL with a SOAP Request object"""
188       
189        if not isinstance(soapRequest, UrlLib2SOAPRequest):
190            raise TypeError('UrlLib2SOAPClient.send: expecting %r '
191                            'derived type for SOAP request, got %r' % 
192                            (self.responseEnvelopeClass, type(soapRequest)))
193           
194        if not isinstance(soapRequest.envelope, self.responseEnvelopeClass):
195            raise TypeError('UrlLib2SOAPClient.send: expecting %r '
196                            'derived type for SOAP envelope, got %r' % 
197                            (self.responseEnvelopeClass, type(soapRequest)))
198                           
199        if self.timeout is not None:
200            arg = (self.timeout,)
201        else:
202            arg = ()
203           
204        soapRequestStr = soapRequest.envelope.serialize()
205       
206        if log.level <= logging.DEBUG:
207            from ndg.security.common.utils.etree import prettyPrint
208            log.debug("SOAP Request:")
209            log.debug("_"*80)
210            log.debug(prettyPrint(soapRequest.envelope.elem))
211
212        soapResponse = UrlLib2SOAPResponse()
213        response = self.openerDirector.open(soapRequest.url, 
214                                            soapRequestStr, 
215                                            *arg)
216        if response.code != httplib.OK:
217            output = response.read()
218            excep = HTTPException("Response is: %d %s" % (response.code, 
219                                                          response.msg))
220            excep.urllib2Response = response
221            raise excep
222       
223        if response.headers.typeheader not in \
224           UrlLib2SOAPClient.RESPONSE_CONTENT_TYPES:
225            responseType = ', '.join(UrlLib2SOAPClient.RESPONSE_CONTENT_TYPES)
226            excep = SOAPResponseError("Expecting %r response type; got %r for "
227                                      "request to [%s]" % 
228                                      (responseType, 
229                                       response.headers.typeheader,
230                                       soapRequest.url))
231            excep.urllib2Response = response
232            raise excep
233           
234        soapResponse.fileObject = response
235        soapResponse.envelope = self.responseEnvelopeClass() 
236       
237        try:
238            soapResponse.envelope.parse(soapResponse.fileObject)
239        except Exception, e:
240            raise SOAPParseError("%r type error raised parsing response for "
241                                 "request to [%s]: %s"
242                                 % (type(e), soapRequest.url, e))
243           
244        return soapResponse
Note: See TracBrowser for help on using the repository browser.