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()
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
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
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()
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
# 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)
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))
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 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)
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))
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)
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