def _Dispatch(ps, server, SendResponse, SendFault, post, action, nsdict={}, **kw):
    """Send ParsedSoap instance to ServiceContainer, which dispatches to
    appropriate service via post, and method via action.  Response is a
    self-describing pyobj, which is passed to a SoapWriter.

    Call SendResponse or SendFault to send the reply back, appropriately.
        server -- ServiceContainer instance

    """
    localURL = "http://%s:%d%s" % (server.server_name, server.server_port, post)
    address = action
    service = server.getNode(post)
    isWSResource = False
    if isinstance(service, WSAResource):
        isWSResource = True
        service.setServiceURL(localURL)
        address = Address()
        try:
            address.parse(ps)
        except Exception, e:
            return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
        if action and action != address.getAction():
            e = WSActionException(
                'SOAP Action("%s") must match WS-Action("%s") if specified.' % (action, address.getAction())
            )
            return SendFault(FaultFromException(e, 0, None), **kw)
        action = address.getAction()
Пример #2
0
    def processResponse(self, sw, **kw):
        if sw is None:
            self.address = None
            return
        
        request, resource = kw['request'], kw['resource']
        if isinstance(request, twisted.web.http.Request) is False:
            raise TypeError, '%s instance expected' %http.Request
                
        d = getattr(resource, 'wsAction', None)
        key = self.op_name
        if d is None or d.has_key(key) is False:
            raise WSActionNotSpecified,\
                'Error looking for key(%s) in wsAction dictionary(%s)' %(key, str(d))

        addressRsp = Address(action=d[key])
        if request.transport.TLS == 0:
            addressRsp.setResponseFromWSAddress(\
                 self.address, 'http://%s:%d%s' %(
                 request.host.host, request.host.port, request.path)
            )
        else:
            addressRsp.setResponseFromWSAddress(\
                 self.address, 'https://%s:%d%s' %(
                 request.host.host, request.host.port, request.path)
            )
            
        addressRsp.serialize(sw, typed=False)
        self.address = None
        return sw
Пример #3
0
    def processRequest(self, ps, **kw):
        # TODO: Clean this up
        resource = kw['resource']

        d = getattr(resource, 'root', None)
        key = _get_element_nsuri_name(ps.body_root)
        if d is None or d.has_key(key) is False:
            raise RuntimeError,\
                'Error looking for key(%s) in root dictionary(%s)' %(key, str(d))

        self.op_name = d[key]
        self.address = address = Address()
        address.parse(ps)
        action = address.getAction()
        if not action:
            raise WSActionException('No WS-Action specified in Request')

        request = kw['request']
        http_headers = request.getAllHeaders()
        soap_action = http_headers.get('soapaction')
        if soap_action and soap_action.strip('\'"') != action:
            raise WSActionException(\
                'SOAP Action("%s") must match WS-Action("%s") if specified.'\
                %(soap_action,action)
            )
            
        # Save WS-Address in ParsedSoap instance.
        ps.address = address
        return ps
Пример #4
0
def _Dispatch(ps,
              server,
              SendResponse,
              SendFault,
              post,
              action,
              nsdict={},
              **kw):
    '''Send ParsedSoap instance to ServiceContainer, which dispatches to
    appropriate service via post, and method via action.  Response is a
    self-describing pyobj, which is passed to a SoapWriter.

    Call SendResponse or SendFault to send the reply back, appropriately.
        server -- ServiceContainer instance

    '''
    localURL = 'http://%s:%d%s' % (server.server_name, server.server_port,
                                   post)
    address = action
    service = server.getNode(post)
    isWSResource = False
    if isinstance(service, WSAResource):
        isWSResource = True
        service.setServiceURL(localURL)
        address = Address()
        try:
            address.parse(ps)
        except Exception, e:
            return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
        if action and action != address.getAction():
            e = WSActionException('SOAP Action("%s") must match WS-Action("%s") if specified.' \
                %(action,address.getAction()))
            return SendFault(FaultFromException(e, 0, None), **kw)
        action = address.getAction()
Пример #5
0
    def processRequest(self,
                       sw,
                       wsaction=None,
                       url=None,
                       endPointReference=None,
                       **kw):
        from ZSI.address import Address
        if sw is None:
            self.address = None
            return

        if not sw.header:
            raise RuntimeError('expecting SOAP:Header')

        self.address = addr = Address(url, wsAddressURI=self.uri)
        addr.setRequest(endPointReference, wsaction)
        addr.serialize(sw, typed=False)

        return sw
Пример #6
0
    # Verify if Signed
    service.verify(ps)

    # If No response just return.
    if result is None:
        return SendResponse('', **kw)

    sw = SoapWriter(nsdict=nsdict)
    try:
        sw.serialize(result)
    except Exception, e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    if isWSResource is True:
        action = service.getResponseAction(ps, action)
        addressRsp = Address(action=action)
        try:
            addressRsp.setResponseFromWSAddress(address, localURL)
            addressRsp.serialize(sw)
        except Exception, e:
            return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    # Create Signatures
    service.sign(sw)

    try:
        soapdata = str(sw)
        return SendResponse(soapdata, **kw)
    except Exception, e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
Пример #7
0
    # Verify if Signed
    service.verify(ps)

    # If No response just return.
    if result is None:
        return SendResponse('', **kw)

    sw = SoapWriter(nsdict=nsdict)
    try:
        sw.serialize(result)
    except Exception, e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    if isWSResource is True:
        action = service.getResponseAction(ps, action)
        addressRsp = Address(action=action)
        try:
            addressRsp.setResponseFromWSAddress(address, localURL)
            addressRsp.serialize(sw)
        except Exception, e:
            return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    # Create Signatures
    service.sign(sw)

    try:
        soapdata = str(sw)
        return SendResponse(soapdata, **kw)
    except Exception, e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
Пример #8
0
class _Binding:
	'''Object that represents a binding (connection) to a SOAP server.
	Once the binding is created, various ways of sending and
	receiving SOAP messages are available.
	'''
	defaultHttpTransport = httplib.HTTPConnection
	defaultHttpsTransport = httplib.HTTPSConnection
	logger = _GetLogger('ZSI.client.Binding')

	def __init__(self, nsdict=None, transport=None, url=None, tracefile=None,
				 readerclass=None, writerclass=None, soapaction='', 
				 wsAddressURI=None, sig_handler=None, transdict=None, **kw):
		'''Initialize.
		Keyword arguments include:
			transport -- default use HTTPConnection. 
			transdict -- dict of values to pass to transport.
			url -- URL of resource, POST is path 
			soapaction -- value of SOAPAction header
			auth -- (type, name, password) triplet; default is unauth
			nsdict -- namespace entries to add
			tracefile -- file to dump packet traces
			cert_file, key_file -- SSL data (q.v.)
			readerclass -- DOM reader class
			writerclass -- DOM writer class, implements MessageInterface
			wsAddressURI -- namespaceURI of WS-Address to use.	By default 
			it's not used.
			sig_handler -- XML Signature handler, must sign and verify.
			endPointReference -- optional Endpoint Reference.
		'''
		self.data = None
		self.ps = None
		self.user_headers = []
		self.nsdict = nsdict or {}
		self.transport = transport
		self.transdict = transdict or {}
		self.url = url
		self.trace = tracefile
		self.readerclass = readerclass
		self.writerclass = writerclass
		self.soapaction = soapaction
		self.wsAddressURI = wsAddressURI
		self.sig_handler = sig_handler
		self.address = None
		self.endPointReference = kw.get('endPointReference', None)
		self.cookies = Cookie.SimpleCookie()
		self.http_callbacks = {}

		if kw.has_key('auth'):
			self.SetAuth(*kw['auth'])
		else:
			self.SetAuth(AUTH.none)

	def SetAuth(self, style, user=None, password=None):
		'''Change auth style, return object to user.
		'''
		self.auth_style, self.auth_user, self.auth_pass = \
			style, user, password
		return self

	def SetURL(self, url):
		'''Set the URL we post to.
		'''
		self.url = url
		return self

	def ResetHeaders(self):
		'''Empty the list of additional headers.
		'''
		self.user_headers = []
		return self

	def ResetCookies(self):
		'''Empty the list of cookies.
		'''
		self.cookies = Cookie.SimpleCookie()

	def AddHeader(self, header, value):
		'''Add a header to send.
		'''
		self.user_headers.append((header, value))
		return self

	def __addcookies(self):
		'''Add cookies from self.cookies to request in self.h
		'''
		for cname, morsel in self.cookies.items():
			attrs = []
			value = morsel.get('version', '')
			if value != '' and value != '0':
				attrs.append('$Version=%s' % value)
			attrs.append('%s=%s' % (cname, morsel.coded_value))
			value = morsel.get('path')
			if value:
				attrs.append('$Path=%s' % value)
			value = morsel.get('domain')
			if value:
				attrs.append('$Domain=%s' % value)
			self.h.putheader('Cookie', "; ".join(attrs))

	def RPC(self, url, opname, obj, replytype=None, **kw):
		'''Send a request, return the reply.  See Send() and Recieve()
		docstrings for details.
		'''
		self.Send(url, opname, obj, **kw)
		return self.Receive(replytype, **kw)

	def Send(self, url, opname, obj, nsdict={}, soapaction=None, wsaction=None, 
			 endPointReference=None, soapheaders=(), **kw):
		'''Send a message.	If url is None, use the value from the
		constructor (else error). obj is the object (data) to send.
		Data may be described with a requesttypecode keyword, the default 
		is the class's typecode (if there is one), else Any.

		Try to serialize as a Struct, if this is not possible serialize an Array.  If 
		data is a sequence of built-in python data types, it will be serialized as an
		Array, unless requesttypecode is specified.

		arguments:
			url -- 
			opname -- struct wrapper
			obj -- python instance

		key word arguments:
			nsdict -- 
			soapaction --
			wsaction -- WS-Address Action, goes in SOAP Header.
			endPointReference --  set by calling party, must be an 
				EndPointReference type instance.
			soapheaders -- list of pyobj, typically w/typecode attribute.
				serialized in the SOAP:Header.
			requesttypecode -- 

		'''
		url = url or self.url
		endPointReference = endPointReference or self.endPointReference

		# Serialize the object.
		d = {}
		d.update(self.nsdict)
		d.update(nsdict)

		sw = SoapWriter(nsdict=d, header=True, outputclass=self.writerclass, 
				 encodingStyle=kw.get('encodingStyle'),)
		
		requesttypecode = kw.get('requesttypecode')
		if kw.has_key('_args'): #NamedParamBinding
			tc = requesttypecode or TC.Any(pname=opname, aslist=False)
			sw.serialize(kw['_args'], tc)
		elif not requesttypecode:
			tc = getattr(obj, 'typecode', None) or TC.Any(pname=opname, aslist=False)
			try:
				if type(obj) in _seqtypes:
					obj = dict(map(lambda i: (i.typecode.pname,i), obj))
			except AttributeError:
				# can't do anything but serialize this in a SOAP:Array
				tc = TC.Any(pname=opname, aslist=True)
			else:
				tc = TC.Any(pname=opname, aslist=False)

			sw.serialize(obj, tc)
		else:
			sw.serialize(obj, requesttypecode)
			
		for i in soapheaders:
		   sw.serialize_header(i) 
			
		# 
		# Determine the SOAP auth element.	SOAP:Header element
		if self.auth_style & AUTH.zsibasic:
			sw.serialize_header(_AuthHeader(self.auth_user, self.auth_pass),
				_AuthHeader.typecode)

		# 
		# Serialize WS-Address
		if self.wsAddressURI is not None:
			if self.soapaction and wsaction.strip('\'"') != self.soapaction:
				raise WSActionException, 'soapAction(%s) and WS-Action(%s) must match'\
					%(self.soapaction,wsaction)

			self.address = Address(url, self.wsAddressURI)
			self.address.setRequest(endPointReference, wsaction)
			self.address.serialize(sw)

		# 
		# WS-Security Signature Handler
		if self.sig_handler is not None:
			self.sig_handler.sign(sw)

		scheme,netloc,path,nil,nil,nil = urlparse.urlparse(url)
		transport = self.transport
		if transport is None and url is not None:
			if scheme == 'https':
				transport = self.defaultHttpsTransport
			elif scheme == 'http':
				transport = self.defaultHttpTransport
			else:
				raise RuntimeError, 'must specify transport or url startswith https/http'

		# Send the request.
		if issubclass(transport, httplib.HTTPConnection) is False:
			raise TypeError, 'transport must be a HTTPConnection'

		soapdata = str(sw)
		self.h = transport(netloc, None, **self.transdict)
		self.h.connect()
		self.SendSOAPData(soapdata, url, soapaction, **kw)

	def SendSOAPData(self, soapdata, url, soapaction, headers={}, **kw):
		# Tracing?
		if self.trace:
			print >>self.trace, "_" * 33, time.ctime(time.time()), "REQUEST:"
			print >>self.trace, soapdata

		url = url or self.url
		request_uri = _get_postvalue_from_absoluteURI(url)
		self.h.putrequest("POST", request_uri)
		self.h.putheader("Content-Length", "%d" % len(soapdata))
		self.h.putheader("Content-Type", 'text/xml; charset="%s"' %UNICODE_ENCODING)
		self.__addcookies()

		for header,value in headers.items():
			self.h.putheader(header, value)

		SOAPActionValue = '"%s"' % (soapaction or self.soapaction)
		self.h.putheader("SOAPAction", SOAPActionValue)
		if self.auth_style & AUTH.httpbasic:
			val = _b64_encode(self.auth_user + ':' + self.auth_pass) \
						.replace("\012", "")
			self.h.putheader('Authorization', 'Basic ' + val)
		elif self.auth_style == AUTH.httpdigest and not headers.has_key('Authorization') \
			and not headers.has_key('Expect'):
			def digest_auth_cb(response):
				self.SendSOAPDataHTTPDigestAuth(response, soapdata, url, request_uri, soapaction, **kw)
				self.http_callbacks[401] = None
			self.http_callbacks[401] = digest_auth_cb

		for header,value in self.user_headers:
			self.h.putheader(header, value)
		self.h.endheaders()
		self.h.send(soapdata)

		print "\n------send-----"
		print soapdata
		print "\n------send-----"

		# Clear prior receive state.
		self.data, self.ps = None, None

	def SendSOAPDataHTTPDigestAuth(self, response, soapdata, url, request_uri, soapaction, **kw):
		'''Resend the initial request w/http digest authorization headers.
		The SOAP server has requested authorization.  Fetch the challenge, 
		generate the authdict for building a response.
		'''
		if self.trace:
			print >>self.trace, "------ Digest Auth Header"
		url = url or self.url
		if response.status != 401:
			raise RuntimeError, 'Expecting HTTP 401 response.'
		if self.auth_style != AUTH.httpdigest:
			raise RuntimeError,\
				'Auth style(%d) does not support requested digest authorization.' %self.auth_style

		from ZSI.digest_auth import fetch_challenge,\
			generate_response,\
			build_authorization_arg,\
			dict_fetch

		chaldict = fetch_challenge( response.getheader('www-authenticate') )
		if dict_fetch(chaldict,'challenge','').lower() == 'digest' and \
			dict_fetch(chaldict,'nonce',None) and \
			dict_fetch(chaldict,'realm',None) and \
			dict_fetch(chaldict,'qop',None):
			authdict = generate_response(chaldict,
				request_uri, self.auth_user, self.auth_pass, method='POST')
			headers = {\
				'Authorization':build_authorization_arg(authdict),
				'Expect':'100-continue',
			}
			self.SendSOAPData(soapdata, url, soapaction, headers, **kw)
			return

		raise RuntimeError,\
			'Client expecting digest authorization challenge.'

	def ReceiveRaw(self, **kw):
		'''Read a server reply, unconverted to any format and return it.
		'''
		if self.data: return self.data
		trace = self.trace
		while 1:
			response = self.h.getresponse()
			self.reply_code, self.reply_msg, self.reply_headers, self.data = \
				response.status, response.reason, response.msg, response.read()
			if trace:
				print >>trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
				for i in (self.reply_code, self.reply_msg,):
					print >>trace, str(i)
				print >>trace, "-------"
				print >>trace, str(self.reply_headers)
				print >>trace, self.data
			saved = None
			for d in response.msg.getallmatchingheaders('set-cookie'):
				if d[0] in [ ' ', '\t' ]:
					saved += d.strip()
				else:
					if saved: self.cookies.load(saved)
					saved = d.strip()
			if saved: self.cookies.load(saved)
			if response.status == 401:
				if not callable(self.http_callbacks.get(response.status,None)):
					raise RuntimeError, 'HTTP Digest Authorization Failed'
				self.http_callbacks[response.status](response)
				continue
			if response.status != 100: break

			# The httplib doesn't understand the HTTP continuation header.
			# Horrible internals hack to patch things up.
			self.h._HTTPConnection__state = httplib._CS_REQ_SENT
			self.h._HTTPConnection__response = None
		return self.data

	def IsSOAP(self):
		if self.ps: return 1
		self.ReceiveRaw()
		mimetype = self.reply_headers.type
		return mimetype == 'text/xml'

	def ReceiveSOAP(self, readerclass=None, **kw):
		'''Get back a SOAP message.
		'''
		if self.ps: return self.ps
		print "\n--------"
		print self.data
		print "--------"		
		if not self.IsSOAP():
			raise TypeError(
				'Response is "%s", not "text/xml"' % self.reply_headers.type)
		if len(self.data) == 0:
			raise TypeError('Received empty response')

		self.ps = ParsedSoap(self.data, 
						readerclass=readerclass or self.readerclass, 
						encodingStyle=kw.get('encodingStyle'))

		if self.sig_handler is not None:
			self.sig_handler.verify(self.ps)

		return self.ps

	def IsAFault(self):
		'''Get a SOAP message, see if it has a fault.
		'''
		self.ReceiveSOAP()
		return self.ps.IsAFault()

	def ReceiveFault(self, **kw):
		'''Parse incoming message as a fault. Raise TypeError if no
		fault found.
		'''
		self.ReceiveSOAP(**kw)
		if not self.ps.IsAFault():
			raise TypeError("Expected SOAP Fault not found")
		return FaultFromFaultMessage(self.ps)

	def Receive(self, replytype, **kw):
		'''Parse message, create Python object.

		KeyWord data:
			faults	 -- list of WSDL operation.fault typecodes
			wsaction -- If using WS-Address, must specify Action value we expect to
				receive.
		'''
		self.ReceiveSOAP(**kw)
		if self.ps.IsAFault():
			msg = FaultFromFaultMessage(self.ps)
			raise FaultException(msg)

		tc = replytype
		if hasattr(replytype, 'typecode'):
			tc = replytype.typecode

		reply = self.ps.Parse(tc)
		if self.address is not None:
			self.address.checkResponse(self.ps, kw.get('wsaction'))
		return reply

	def __repr__(self):
		return "<%s instance %s>" % (self.__class__.__name__, _get_idstr(self))
Пример #9
0
	def Send(self, url, opname, obj, nsdict={}, soapaction=None, wsaction=None, 
			 endPointReference=None, soapheaders=(), **kw):
		'''Send a message.	If url is None, use the value from the
		constructor (else error). obj is the object (data) to send.
		Data may be described with a requesttypecode keyword, the default 
		is the class's typecode (if there is one), else Any.

		Try to serialize as a Struct, if this is not possible serialize an Array.  If 
		data is a sequence of built-in python data types, it will be serialized as an
		Array, unless requesttypecode is specified.

		arguments:
			url -- 
			opname -- struct wrapper
			obj -- python instance

		key word arguments:
			nsdict -- 
			soapaction --
			wsaction -- WS-Address Action, goes in SOAP Header.
			endPointReference --  set by calling party, must be an 
				EndPointReference type instance.
			soapheaders -- list of pyobj, typically w/typecode attribute.
				serialized in the SOAP:Header.
			requesttypecode -- 

		'''
		url = url or self.url
		endPointReference = endPointReference or self.endPointReference

		# Serialize the object.
		d = {}
		d.update(self.nsdict)
		d.update(nsdict)

		sw = SoapWriter(nsdict=d, header=True, outputclass=self.writerclass, 
				 encodingStyle=kw.get('encodingStyle'),)
		
		requesttypecode = kw.get('requesttypecode')
		if kw.has_key('_args'): #NamedParamBinding
			tc = requesttypecode or TC.Any(pname=opname, aslist=False)
			sw.serialize(kw['_args'], tc)
		elif not requesttypecode:
			tc = getattr(obj, 'typecode', None) or TC.Any(pname=opname, aslist=False)
			try:
				if type(obj) in _seqtypes:
					obj = dict(map(lambda i: (i.typecode.pname,i), obj))
			except AttributeError:
				# can't do anything but serialize this in a SOAP:Array
				tc = TC.Any(pname=opname, aslist=True)
			else:
				tc = TC.Any(pname=opname, aslist=False)

			sw.serialize(obj, tc)
		else:
			sw.serialize(obj, requesttypecode)
			
		for i in soapheaders:
		   sw.serialize_header(i) 
			
		# 
		# Determine the SOAP auth element.	SOAP:Header element
		if self.auth_style & AUTH.zsibasic:
			sw.serialize_header(_AuthHeader(self.auth_user, self.auth_pass),
				_AuthHeader.typecode)

		# 
		# Serialize WS-Address
		if self.wsAddressURI is not None:
			if self.soapaction and wsaction.strip('\'"') != self.soapaction:
				raise WSActionException, 'soapAction(%s) and WS-Action(%s) must match'\
					%(self.soapaction,wsaction)

			self.address = Address(url, self.wsAddressURI)
			self.address.setRequest(endPointReference, wsaction)
			self.address.serialize(sw)

		# 
		# WS-Security Signature Handler
		if self.sig_handler is not None:
			self.sig_handler.sign(sw)

		scheme,netloc,path,nil,nil,nil = urlparse.urlparse(url)
		transport = self.transport
		if transport is None and url is not None:
			if scheme == 'https':
				transport = self.defaultHttpsTransport
			elif scheme == 'http':
				transport = self.defaultHttpTransport
			else:
				raise RuntimeError, 'must specify transport or url startswith https/http'

		# Send the request.
		if issubclass(transport, httplib.HTTPConnection) is False:
			raise TypeError, 'transport must be a HTTPConnection'

		soapdata = str(sw)
		self.h = transport(netloc, None, **self.transdict)
		self.h.connect()
		self.SendSOAPData(soapdata, url, soapaction, **kw)
Пример #10
0
    def Send(self,
             url,
             opname,
             obj,
             nsdict={},
             soapaction=None,
             wsaction=None,
             endPointReference=None,
             soapheaders=(),
             **kw):
        '''Send a message.  If url is None, use the value from the
        constructor (else error). obj is the object (data) to send.
        Data may be described with a requesttypecode keyword, the default 
        is the class's typecode (if there is one), else Any.

        Try to serialize as a Struct, if this is not possible serialize an Array.  If 
        data is a sequence of built-in python data types, it will be serialized as an
        Array, unless requesttypecode is specified.

        arguments:
            url -- 
            opname -- struct wrapper
            obj -- python instance

        key word arguments:
            nsdict -- 
            soapaction --
            wsaction -- WS-Address Action, goes in SOAP Header.
            endPointReference --  set by calling party, must be an 
                EndPointReference type instance.
            soapheaders -- list of pyobj, typically w/typecode attribute.
                serialized in the SOAP:Header.
            requesttypecode -- 

        '''
        url = url or self.url
        endPointReference = endPointReference or self.endPointReference

        # Serialize the object.
        d = {}
        d.update(self.nsdict)
        d.update(nsdict)

        sw = SoapWriter(
            nsdict=d,
            header=True,
            outputclass=self.writerclass,
            encodingStyle=kw.get('encodingStyle'),
        )

        requesttypecode = kw.get('requesttypecode')
        if kw.has_key('_args'):  #NamedParamBinding
            tc = requesttypecode or TC.Any(pname=opname, aslist=False)
            sw.serialize(kw['_args'], tc)
        elif not requesttypecode:
            tc = getattr(obj, 'typecode', None) or TC.Any(pname=opname,
                                                          aslist=False)
            try:
                if type(obj) in _seqtypes:
                    obj = dict(map(lambda i: (i.typecode.pname, i), obj))
            except AttributeError:
                # can't do anything but serialize this in a SOAP:Array
                tc = TC.Any(pname=opname, aslist=True)
            else:
                tc = TC.Any(pname=opname, aslist=False)

            sw.serialize(obj, tc)
        else:
            sw.serialize(obj, requesttypecode)

        for i in soapheaders:
            sw.serialize_header(i)

        #
        # Determine the SOAP auth element.  SOAP:Header element
        if self.auth_style & AUTH.zsibasic:
            sw.serialize_header(_AuthHeader(self.auth_user, self.auth_pass),
                                _AuthHeader.typecode)

        #
        # Serialize WS-Address
        if self.wsAddressURI is not None:
            if self.soapaction and wsaction.strip('\'"') != self.soapaction:
                raise WSActionException, 'soapAction(%s) and WS-Action(%s) must match'\
                    %(self.soapaction,wsaction)

            self.address = Address(url, self.wsAddressURI)
            self.address.setRequest(endPointReference, wsaction)
            self.address.serialize(sw)

        #
        # WS-Security Signature Handler
        if self.sig_handler is not None:
            self.sig_handler.sign(sw)

        scheme, netloc, path, nil, nil, nil = urlparse.urlparse(url)
        transport = self.transport
        if transport is None and url is not None:
            if scheme == 'https':
                transport = self.defaultHttpsTransport
            elif scheme == 'http':
                transport = self.defaultHttpTransport
            else:
                raise RuntimeError, 'must specify transport or url startswith https/http'

        # Send the request.
        if issubclass(transport, httplib.HTTPConnection) is False:
            raise TypeError, 'transport must be a HTTPConnection'

        soapdata = str(sw)
        self.h = transport(netloc, None, **self.transdict)
        self.h.connect()
        self.SendSOAPData(soapdata, url, soapaction, **kw)
Пример #11
0
class _Binding:
    '''Object that represents a binding (connection) to a SOAP server.
    Once the binding is created, various ways of sending and
    receiving SOAP messages are available.
    '''
    defaultHttpTransport = httplib.HTTPConnection
    defaultHttpsTransport = httplib.HTTPSConnection
    logger = _GetLogger('ZSI.client.Binding')

    def __init__(self,
                 nsdict=None,
                 transport=None,
                 url=None,
                 tracefile=None,
                 readerclass=None,
                 writerclass=None,
                 soapaction='',
                 wsAddressURI=None,
                 sig_handler=None,
                 transdict=None,
                 **kw):
        '''Initialize.
        Keyword arguments include:
            transport -- default use HTTPConnection. 
            transdict -- dict of values to pass to transport.
            url -- URL of resource, POST is path 
            soapaction -- value of SOAPAction header
            auth -- (type, name, password) triplet; default is unauth
            nsdict -- namespace entries to add
            tracefile -- file to dump packet traces
            cert_file, key_file -- SSL data (q.v.)
            readerclass -- DOM reader class
            writerclass -- DOM writer class, implements MessageInterface
            wsAddressURI -- namespaceURI of WS-Address to use.  By default 
            it's not used.
            sig_handler -- XML Signature handler, must sign and verify.
            endPointReference -- optional Endpoint Reference.
        '''
        self.data = None
        self.ps = None
        self.user_headers = []
        self.nsdict = nsdict or {}
        self.transport = transport
        self.transdict = transdict or {}
        self.url = url
        self.trace = tracefile
        self.readerclass = readerclass
        self.writerclass = writerclass
        self.soapaction = soapaction
        self.wsAddressURI = wsAddressURI
        self.sig_handler = sig_handler
        self.address = None
        self.endPointReference = kw.get('endPointReference', None)
        self.cookies = Cookie.SimpleCookie()
        self.http_callbacks = {}

        if kw.has_key('auth'):
            self.SetAuth(*kw['auth'])
        else:
            self.SetAuth(AUTH.none)

    def SetAuth(self, style, user=None, password=None):
        '''Change auth style, return object to user.
        '''
        self.auth_style, self.auth_user, self.auth_pass = \
            style, user, password
        return self

    def SetURL(self, url):
        '''Set the URL we post to.
        '''
        self.url = url
        return self

    def ResetHeaders(self):
        '''Empty the list of additional headers.
        '''
        self.user_headers = []
        return self

    def ResetCookies(self):
        '''Empty the list of cookies.
        '''
        self.cookies = Cookie.SimpleCookie()

    def AddHeader(self, header, value):
        '''Add a header to send.
        '''
        self.user_headers.append((header, value))
        return self

    def __addcookies(self):
        '''Add cookies from self.cookies to request in self.h
        '''
        for cname, morsel in self.cookies.items():
            attrs = []
            value = morsel.get('version', '')
            if value != '' and value != '0':
                attrs.append('$Version=%s' % value)
            attrs.append('%s=%s' % (cname, morsel.coded_value))
            value = morsel.get('path')
            if value:
                attrs.append('$Path=%s' % value)
            value = morsel.get('domain')
            if value:
                attrs.append('$Domain=%s' % value)
            self.h.putheader('Cookie', "; ".join(attrs))

    def RPC(self, url, opname, obj, replytype=None, **kw):
        '''Send a request, return the reply.  See Send() and Recieve()
        docstrings for details.
        '''
        self.Send(url, opname, obj, **kw)
        return self.Receive(replytype, **kw)

    def Send(self,
             url,
             opname,
             obj,
             nsdict={},
             soapaction=None,
             wsaction=None,
             endPointReference=None,
             soapheaders=(),
             **kw):
        '''Send a message.  If url is None, use the value from the
        constructor (else error). obj is the object (data) to send.
        Data may be described with a requesttypecode keyword, the default 
        is the class's typecode (if there is one), else Any.

        Try to serialize as a Struct, if this is not possible serialize an Array.  If 
        data is a sequence of built-in python data types, it will be serialized as an
        Array, unless requesttypecode is specified.

        arguments:
            url -- 
            opname -- struct wrapper
            obj -- python instance

        key word arguments:
            nsdict -- 
            soapaction --
            wsaction -- WS-Address Action, goes in SOAP Header.
            endPointReference --  set by calling party, must be an 
                EndPointReference type instance.
            soapheaders -- list of pyobj, typically w/typecode attribute.
                serialized in the SOAP:Header.
            requesttypecode -- 

        '''
        url = url or self.url
        endPointReference = endPointReference or self.endPointReference

        # Serialize the object.
        d = {}
        d.update(self.nsdict)
        d.update(nsdict)

        sw = SoapWriter(
            nsdict=d,
            header=True,
            outputclass=self.writerclass,
            encodingStyle=kw.get('encodingStyle'),
        )

        requesttypecode = kw.get('requesttypecode')
        if kw.has_key('_args'):  #NamedParamBinding
            tc = requesttypecode or TC.Any(pname=opname, aslist=False)
            sw.serialize(kw['_args'], tc)
        elif not requesttypecode:
            tc = getattr(obj, 'typecode', None) or TC.Any(pname=opname,
                                                          aslist=False)
            try:
                if type(obj) in _seqtypes:
                    obj = dict(map(lambda i: (i.typecode.pname, i), obj))
            except AttributeError:
                # can't do anything but serialize this in a SOAP:Array
                tc = TC.Any(pname=opname, aslist=True)
            else:
                tc = TC.Any(pname=opname, aslist=False)

            sw.serialize(obj, tc)
        else:
            sw.serialize(obj, requesttypecode)

        for i in soapheaders:
            sw.serialize_header(i)

        #
        # Determine the SOAP auth element.  SOAP:Header element
        if self.auth_style & AUTH.zsibasic:
            sw.serialize_header(_AuthHeader(self.auth_user, self.auth_pass),
                                _AuthHeader.typecode)

        #
        # Serialize WS-Address
        if self.wsAddressURI is not None:
            if self.soapaction and wsaction.strip('\'"') != self.soapaction:
                raise WSActionException, 'soapAction(%s) and WS-Action(%s) must match'\
                    %(self.soapaction,wsaction)

            self.address = Address(url, self.wsAddressURI)
            self.address.setRequest(endPointReference, wsaction)
            self.address.serialize(sw)

        #
        # WS-Security Signature Handler
        if self.sig_handler is not None:
            self.sig_handler.sign(sw)

        scheme, netloc, path, nil, nil, nil = urlparse.urlparse(url)
        transport = self.transport
        if transport is None and url is not None:
            if scheme == 'https':
                transport = self.defaultHttpsTransport
            elif scheme == 'http':
                transport = self.defaultHttpTransport
            else:
                raise RuntimeError, 'must specify transport or url startswith https/http'

        # Send the request.
        if issubclass(transport, httplib.HTTPConnection) is False:
            raise TypeError, 'transport must be a HTTPConnection'

        soapdata = str(sw)
        self.h = transport(netloc, None, **self.transdict)
        self.h.connect()
        self.SendSOAPData(soapdata, url, soapaction, **kw)

    def SendSOAPData(self, soapdata, url, soapaction, headers={}, **kw):
        # Tracing?
        if self.trace:
            print >> self.trace, "_" * 33, time.ctime(time.time()), "REQUEST:"
            print >> self.trace, soapdata

        url = url or self.url
        request_uri = _get_postvalue_from_absoluteURI(url)
        self.h.putrequest("POST", request_uri)
        self.h.putheader("Content-Length", "%d" % len(soapdata))
        self.h.putheader("Content-Type",
                         'text/xml; charset="%s"' % UNICODE_ENCODING)
        self.__addcookies()

        for header, value in headers.items():
            self.h.putheader(header, value)

        SOAPActionValue = '"%s"' % (soapaction or self.soapaction)
        self.h.putheader("SOAPAction", SOAPActionValue)
        if self.auth_style & AUTH.httpbasic:
            val = _b64_encode(self.auth_user + ':' + self.auth_pass) \
                        .replace("\012", "")
            self.h.putheader('Authorization', 'Basic ' + val)
        elif self.auth_style == AUTH.httpdigest and not headers.has_key('Authorization') \
            and not headers.has_key('Expect'):

            def digest_auth_cb(response):
                self.SendSOAPDataHTTPDigestAuth(response, soapdata, url,
                                                request_uri, soapaction, **kw)
                self.http_callbacks[401] = None

            self.http_callbacks[401] = digest_auth_cb

        for header, value in self.user_headers:
            self.h.putheader(header, value)
        self.h.endheaders()
        self.h.send(soapdata)

        # Clear prior receive state.
        self.data, self.ps = None, None

    def SendSOAPDataHTTPDigestAuth(self, response, soapdata, url, request_uri,
                                   soapaction, **kw):
        '''Resend the initial request w/http digest authorization headers.
        The SOAP server has requested authorization.  Fetch the challenge, 
        generate the authdict for building a response.
        '''
        if self.trace:
            print >> self.trace, "------ Digest Auth Header"
        url = url or self.url
        if response.status != 401:
            raise RuntimeError, 'Expecting HTTP 401 response.'
        if self.auth_style != AUTH.httpdigest:
            raise RuntimeError,\
                'Auth style(%d) does not support requested digest authorization.' %self.auth_style

        from ZSI.digest_auth import fetch_challenge,\
            generate_response,\
            build_authorization_arg,\
            dict_fetch

        chaldict = fetch_challenge(response.getheader('www-authenticate'))
        if dict_fetch(chaldict,'challenge','').lower() == 'digest' and \
            dict_fetch(chaldict,'nonce',None) and \
            dict_fetch(chaldict,'realm',None) and \
            dict_fetch(chaldict,'qop',None):
            authdict = generate_response(chaldict,
                                         request_uri,
                                         self.auth_user,
                                         self.auth_pass,
                                         method='POST')
            headers = {\
                'Authorization':build_authorization_arg(authdict),
                'Expect':'100-continue',
            }
            self.SendSOAPData(soapdata, url, soapaction, headers, **kw)
            return

        raise RuntimeError,\
            'Client expecting digest authorization challenge.'

    def ReceiveRaw(self, **kw):
        '''Read a server reply, unconverted to any format and return it.
        '''
        if self.data: return self.data
        trace = self.trace
        while 1:
            response = self.h.getresponse()
            self.reply_code, self.reply_msg, self.reply_headers, self.data = \
                response.status, response.reason, response.msg, response.read()
            if trace:
                print >> trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
                for i in (
                        self.reply_code,
                        self.reply_msg,
                ):
                    print >> trace, str(i)
                print >> trace, "-------"
                print >> trace, str(self.reply_headers)
                print >> trace, self.data
            saved = None
            for d in response.msg.getallmatchingheaders('set-cookie'):
                if d[0] in [' ', '\t']:
                    saved += d.strip()
                else:
                    if saved: self.cookies.load(saved)
                    saved = d.strip()
            if saved: self.cookies.load(saved)
            if response.status == 401:
                if not callable(self.http_callbacks.get(response.status,
                                                        None)):
                    raise RuntimeError, 'HTTP Digest Authorization Failed'
                self.http_callbacks[response.status](response)
                continue
            if response.status != 100: break

            # The httplib doesn't understand the HTTP continuation header.
            # Horrible internals hack to patch things up.
            self.h._HTTPConnection__state = httplib._CS_REQ_SENT
            self.h._HTTPConnection__response = None
        return self.data

    def IsSOAP(self):
        if self.ps: return 1
        self.ReceiveRaw()
        mimetype = self.reply_headers.type
        return mimetype == 'text/xml'

    def ReceiveSOAP(self, readerclass=None, **kw):
        '''Get back a SOAP message.
        '''
        if self.ps: return self.ps
        if not self.IsSOAP():
            raise TypeError('Response is "%s", not "text/xml"' %
                            self.reply_headers.type)
        if len(self.data) == 0:
            raise TypeError('Received empty response')

        self.ps = ParsedSoap(self.data,
                             readerclass=readerclass or self.readerclass,
                             encodingStyle=kw.get('encodingStyle'))

        if self.sig_handler is not None:
            self.sig_handler.verify(self.ps)

        return self.ps

    def IsAFault(self):
        '''Get a SOAP message, see if it has a fault.
        '''
        self.ReceiveSOAP()
        return self.ps.IsAFault()

    def ReceiveFault(self, **kw):
        '''Parse incoming message as a fault. Raise TypeError if no
        fault found.
        '''
        self.ReceiveSOAP(**kw)
        if not self.ps.IsAFault():
            raise TypeError("Expected SOAP Fault not found")
        return FaultFromFaultMessage(self.ps)

    def Receive(self, replytype, **kw):
        '''Parse message, create Python object.

        KeyWord data:
            faults   -- list of WSDL operation.fault typecodes
            wsaction -- If using WS-Address, must specify Action value we expect to
                receive.
        '''
        self.ReceiveSOAP(**kw)
        if self.ps.IsAFault():
            msg = FaultFromFaultMessage(self.ps)
            raise FaultException(msg)

        tc = replytype
        if hasattr(replytype, 'typecode'):
            tc = replytype.typecode

        reply = self.ps.Parse(tc)
        if self.address is not None:
            self.address.checkResponse(self.ps, kw.get('wsaction'))
        return reply

    def __repr__(self):
        return "<%s instance %s>" % (self.__class__.__name__, _get_idstr(self))
Пример #12
0
def _Dispatch(ps,
              server,
              SendResponse,
              SendFault,
              post,
              action,
              nsdict={},
              **kw):
    '''Send ParsedSoap instance to ServiceContainer, which dispatches to
    appropriate service via post, and method via action.  Response is a
    self-describing pyobj, which is passed to a SoapWriter.

    Call SendResponse or SendFault to send the reply back, appropriately.
        server -- ServiceContainer instance

    '''
    localURL = 'http://%s:%d%s' % (server.server_name, server.server_port,
                                   post)
    address = action
    service = server.getNode(post)
    isWSResource = False
    if isinstance(service, WSAResource):
        isWSResource = True
        service.setServiceURL(localURL)
        address = Address()
        try:
            address.parse(ps)
        except Exception as e:
            return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
        if action and action != address.getAction():
            e = WSActionException('SOAP Action("%s") must match WS-Action("%s") if specified.' \
                %(action,address.getAction()))
            return SendFault(FaultFromException(e, 0, None), **kw)
        action = address.getAction()

    if isinstance(service, ServiceInterface) is False:
        e = NoSuchService('no service at POST(%s) in container: %s' %
                          (post, server))
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    if not service.authorize(None, post, action):
        return SendFault(Fault(Fault.Server, "Not authorized"), code=401)
        #try:
        #    raise NotAuthorized()
        #except Exception, e:
        #return SendFault(FaultFromException(e, 0, None), code=401, **kw)
        ##return SendFault(FaultFromException(NotAuthorized(), 0, None), code=401, **kw)

    try:
        method = service.getOperation(ps, address)
    except Exception as e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    try:
        if isWSResource is True:
            _, result = method(ps, address)
        else:
            _, result = method(ps)
    except Exception as e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    # Verify if Signed
    service.verify(ps)

    # If No response just return.
    if result is None:
        return SendResponse('', **kw)

    sw = SoapWriter(nsdict=nsdict)
    try:
        sw.serialize(result)
    except Exception as e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    if isWSResource is True:
        action = service.getResponseAction(ps, action)
        addressRsp = Address(action=action)
        try:
            addressRsp.setResponseFromWSAddress(address, localURL)
            addressRsp.serialize(sw)
        except Exception as e:
            return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    # Create Signatures
    service.sign(sw)

    try:
        soapdata = str(sw)
        return SendResponse(soapdata, **kw)
    except Exception as e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
Пример #13
0
def _Dispatch(ps, server, SendResponse, SendFault, post, action, nsdict={}, **kw):
    '''Send ParsedSoap instance to ServiceContainer, which dispatches to
    appropriate service via post, and method via action.  Response is a
    self-describing pyobj, which is passed to a SoapWriter.

    Call SendResponse or SendFault to send the reply back, appropriately.
        server -- ServiceContainer instance

    '''
    localURL = 'http://%s:%d%s' %(server.server_name,server.server_port,post)
    address = action
    service = server.getNode(post)
    isWSResource = False
    if isinstance(service, SimpleWSResource):
        isWSResource = True
        service.setServiceURL(localURL)
        address = Address()
        try:
            address.parse(ps)
        except Exception as e:
            return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
        if action and action != address.getAction():
            e = WSActionException('SOAP Action("%s") must match WS-Action("%s") if specified.' \
                %(action,address.getAction()))
            return SendFault(FaultFromException(e, 0, None), **kw)
        action = address.getAction()

    if isinstance(service, ServiceInterface) is False:
        e = NoSuchService('no service at POST(%s) in container: %s' %(post,server))
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    if not service.authorize(None, post, action):
        return SendFault(Fault(Fault.Server, "Not authorized"), code=401)
        #try:
        #    raise NotAuthorized()
        #except Exception, e:
            #return SendFault(FaultFromException(e, 0, None), code=401, **kw)
            ##return SendFault(FaultFromException(NotAuthorized(), 0, None), code=401, **kw)

    try:
        method = service.getOperation(ps, address)
    except Exception as e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    try:
        if isWSResource is True: 
            result = method(ps, address)
        else: 
            result = method(ps)
    except Exception as e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    # Verify if Signed
    service.verify(ps)

    # If No response just return.
    if result is None:
        return

    sw = SoapWriter(nsdict=nsdict)
    try:
        sw.serialize(result)
    except Exception as e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    if isWSResource is True:
        action = service.getResponseAction(action)
        addressRsp = Address(action=action)
        try:
            addressRsp.setResponseFromWSAddress(address, localURL)
            addressRsp.serialize(sw)
        except Exception as e:
            return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)

    # Create Signatures
    service.sign(sw)

    try:
        soapdata = str(sw)
        return SendResponse(soapdata, **kw)
    except Exception as e:
        return SendFault(FaultFromException(e, 0, sys.exc_info()[2]), **kw)
    def Send(self, url, opname, obj, nsdict={}, soapaction=None, wsaction=None, 
             endPointReference=None, **kw):
        '''Send a message.  If url is None, use the value from the
        constructor (else error). obj is the object (data) to send.
        Data may be described with a requesttypecode keyword, or a
        requestclass keyword; default is the class's typecode (if
        there is one), else Any.

        Optional WS-Address Keywords
            wsaction -- WS-Address Action, goes in SOAP Header.
            endPointReference --  set by calling party, must be an 
                EndPointReference type instance.

        '''
        url = url or self.url
        # Get the TC for the obj.
        if kw.has_key('requesttypecode'):
            tc = kw['requesttypecode']
        elif kw.has_key('requestclass'):
            tc = kw['requestclass'].typecode
        elif type(obj) == types.InstanceType:
            tc = getattr(obj.__class__, 'typecode')
            if tc is None: tc = TC.Any(opname, aslist=1)
        else:
            tc = TC.Any(opname, aslist=1)

        endPointReference = endPointReference or self.endPointReference

        # Serialize the object.
        d = {}

        d.update(self.nsdict)
        d.update(nsdict)

        useWSAddress = self.wsAddressURI is not None
        sw = SoapWriter(nsdict=d, header=True, outputclass=self.writerclass, 
                 encodingStyle=kw.get('encodingStyle'),)
        if kw.has_key('_args'):
            sw.serialize(kw['_args'], tc)
        else:
            sw.serialize(obj, tc)

        # Determine the SOAP auth element.  SOAP:Header element
        if self.auth_style & AUTH.zsibasic:
            sw.serialize_header(_AuthHeader(self.auth_user, self.auth_pass),
                _AuthHeader.typecode)

        # Serialize WS-Address
        if useWSAddress is True:
            if self.soapaction and wsaction.strip('\'"') != self.soapaction:
                raise WSActionException, 'soapAction(%s) and WS-Action(%s) must match'\
                    %(self.soapaction,wsaction)
            self.address = Address(url, self.wsAddressURI)
            self.address.setRequest(endPointReference, wsaction)
            self.address.serialize(sw)

        # WS-Security Signature Handler
        if self.sig_handler is not None:
            self.sig_handler.sign(sw)
        soapdata = str(sw)

        scheme,netloc,path,nil,nil,nil = urlparse.urlparse(url)

        # self.transport httplib.HTTPConnection derived class set-up removed
        # from HERE - this now handled by urllib2.urlopen()
        self.SendSOAPData(soapdata, url, soapaction, **kw)
class URLlib2Binding(client.Binding):
    def Send(self, url, opname, obj, nsdict={}, soapaction=None, wsaction=None, 
             endPointReference=None, **kw):
        '''Send a message.  If url is None, use the value from the
        constructor (else error). obj is the object (data) to send.
        Data may be described with a requesttypecode keyword, or a
        requestclass keyword; default is the class's typecode (if
        there is one), else Any.

        Optional WS-Address Keywords
            wsaction -- WS-Address Action, goes in SOAP Header.
            endPointReference --  set by calling party, must be an 
                EndPointReference type instance.

        '''
        url = url or self.url
        # Get the TC for the obj.
        if kw.has_key('requesttypecode'):
            tc = kw['requesttypecode']
        elif kw.has_key('requestclass'):
            tc = kw['requestclass'].typecode
        elif type(obj) == types.InstanceType:
            tc = getattr(obj.__class__, 'typecode')
            if tc is None: tc = TC.Any(opname, aslist=1)
        else:
            tc = TC.Any(opname, aslist=1)

        endPointReference = endPointReference or self.endPointReference

        # Serialize the object.
        d = {}

        d.update(self.nsdict)
        d.update(nsdict)

        useWSAddress = self.wsAddressURI is not None
        sw = SoapWriter(nsdict=d, header=True, outputclass=self.writerclass, 
                 encodingStyle=kw.get('encodingStyle'),)
        if kw.has_key('_args'):
            sw.serialize(kw['_args'], tc)
        else:
            sw.serialize(obj, tc)

        # Determine the SOAP auth element.  SOAP:Header element
        if self.auth_style & AUTH.zsibasic:
            sw.serialize_header(_AuthHeader(self.auth_user, self.auth_pass),
                _AuthHeader.typecode)

        # Serialize WS-Address
        if useWSAddress is True:
            if self.soapaction and wsaction.strip('\'"') != self.soapaction:
                raise WSActionException, 'soapAction(%s) and WS-Action(%s) must match'\
                    %(self.soapaction,wsaction)
            self.address = Address(url, self.wsAddressURI)
            self.address.setRequest(endPointReference, wsaction)
            self.address.serialize(sw)

        # WS-Security Signature Handler
        if self.sig_handler is not None:
            self.sig_handler.sign(sw)
        soapdata = str(sw)

        scheme,netloc,path,nil,nil,nil = urlparse.urlparse(url)

        # self.transport httplib.HTTPConnection derived class set-up removed
        # from HERE - this now handled by urllib2.urlopen()
        self.SendSOAPData(soapdata, url, soapaction, **kw)

    def SendSOAPData(self, soapdata, url, soapaction, headers={}, **kw):
        # Tracing?
        if self.trace:
            print >>self.trace, "_" * 33, time.ctime(time.time()), "REQUEST:"
            print >>self.trace, soapdata


        #scheme,netloc,path,nil,nil,nil = urlparse.urlparse(url)
        path = _get_postvalue_from_absoluteURI(url)
 
        
        # Create a request   
        req = urllib2.Request(url, data=soapdata)

        req.add_header("Content-length", "%d" % len(soapdata))
        req.add_header("Content-type", 'text/xml; charset=utf-8')
        
        # TODO: equivalent method for cookies using urllib2 
        #self.__addcookies()

        for header,value in headers.items():
            req.add_header(header, value)

        SOAPActionValue = '"%s"' % (soapaction or self.soapaction)
        req.add_header("SOAPAction", SOAPActionValue)
        
        # client.Binding has Authentication handler set-up code here - 
        # urllib2.HTTPBasicAuthHandler can do this instead?

        for header,value in self.user_headers:
            req.add_header(header, value)
        
        # Check for custom urllib2 handler class
        if 'urlHandler' in kw:
            if not isinstance(kw['urlHandler'], urllib2.BaseHandler):
                raise TypeError, \
            "URL Handler class %s must be derived from urllib2.BaseHandler" %\
                                    kw['urlHandler']
            
            # Make an opener and make it the default so that urllib2.urlopen
            # will use it
            urlOpener = urllib2.build_opener(kw['urlHandler'])
            urllib2.install_opener(urlOpener)
            
        # Send request [and receive response all in one (!) - implications
        # for client.Binding architecture + functionality??]
        self.response = urllib2.urlopen(req)
         
        # Clear prior receive state.
        self.data, self.ps = None, None
        
        
    def ReceiveRaw(self, **kw):
        '''Read a server reply, unconverted to any format and return it.
        '''
        if self.data: return self.data
        trace = self.trace
        
        if hasattr(self, 'response') and self.response is not None:
            self.reply_code, self.reply_msg, self.reply_headers, self.data = \
                self.response.code, self.response.msg, self.response.headers,\
                self.response.read()
           
            # Reset response for next call
            self.response = None
            if trace:
                print >>trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
                for i in (self.reply_code, self.reply_msg,):
                    print >>trace, str(i)
                print >>trace, "-------"
                print >>trace, str(self.reply_headers)
                print >>trace, self.data

            return self.data
        
        # else Send didn't use SendSOAPData...
        while 1:
            response = self.h.getresponse()
            self.reply_code, self.reply_msg, self.reply_headers, self.data = \
                response.status, response.reason, response.msg, response.read()
            if trace:
                print >>trace, "_" * 33, time.ctime(time.time()), "RESPONSE:"
                for i in (self.reply_code, self.reply_msg,):
                    print >>trace, str(i)
                print >>trace, "-------"
                print >>trace, str(self.reply_headers)
                print >>trace, self.data
            saved = None
            for d in response.msg.getallmatchingheaders('set-cookie'):
                if d[0] in [ ' ', '\t' ]:
                    saved += d.strip()
                else:
                    if saved: self.cookies.load(saved)
                    saved = d.strip()
            if saved: self.cookies.load(saved)
            if response.status == 401:
                if not callable(self.http_callbacks.get(response.status,None)):
                    raise RuntimeError, 'HTTP Digest Authorization Failed'
                self.http_callbacks[response.status](response)
                continue
            if response.status != 100: break

            # The httplib doesn't understand the HTTP continuation header.
            # Horrible internals hack to patch things up.
            self.h._HTTPConnection__state = httplib._CS_REQ_SENT
            self.h._HTTPConnection__response = None
        return self.data