############################################################################ # Joshua R. Boverhof, LBNL # See Copyright for copyright notice! ########################################################################### import time, urlparse, socket from ZSI import _seqtypes, EvaluateException, WSActionException from TC import AnyElement, AnyType, TypeCode from schema import GED, GTD, _has_type_definition from ZSI.TCcompound import ComplexType from ZSI.wstools.Namespaces import WSA_LIST class Address(object): '''WS-Address Implemented is dependent on the default "wsdl2py" convention of generating aname, so the attributes representing element declaration names should be prefixed with an underscore. ''' def __init__(self, addressTo=None, wsAddressURI=None, action=None): self.wsAddressURI = wsAddressURI self.anonymousURI = None self._addressTo = addressTo self._messageID = None self._action = action self._endPointReference = None self._replyTo = None self._relatesTo = None self.setUp() def setUp(self): '''Look for WS-Address ''' toplist = filter(lambda wsa: wsa.ADDRESS==self.wsAddressURI, WSA_LIST) epr = 'EndpointReferenceType' for WSA in toplist+WSA_LIST: if (self.wsAddressURI is not None and self.wsAddressURI != WSA.ADDRESS) or \ _has_type_definition(WSA.ADDRESS, epr) is True: break else: raise EvaluateException,\ 'enabling wsAddressing requires the inclusion of that namespace' self.wsAddressURI = WSA.ADDRESS self.anonymousURI = WSA.ANONYMOUS self._replyTo = WSA.ANONYMOUS def _checkAction(self, action, value): '''WS-Address Action action -- Action value expecting. value -- Action value server returned. ''' if action is None: raise WSActionException, 'User did not specify WSAddress Action value to expect' if not value: raise WSActionException, 'missing WSAddress Action, expecting %s' %action if value != action: raise WSActionException, 'wrong WSAddress Action(%s), expecting %s'%(value,action) def _checkFrom(self, pyobj): '''WS-Address From, XXX currently not checking the hostname, not forwarding messages. pyobj -- From server returned. ''' if pyobj is None: return value = pyobj._Address if value != self._addressTo: scheme,netloc,path,query,fragment = urlparse.urlsplit(value) hostport = netloc.split(':') schemeF,netlocF,pathF,queryF,fragmentF = urlparse.urlsplit(self._addressTo) if scheme==schemeF and path==pathF and query==queryF and fragment==fragmentF: netloc = netloc.split(':') + ['80'] netlocF = netlocF.split(':') + ['80'] if netloc[1]==netlocF[1] and \ socket.gethostbyname(netloc[0])==socket.gethostbyname(netlocF[0]): return raise WSActionException, 'wrong WS-Address From(%s), expecting %s'%(value,self._addressTo) def _checkRelatesTo(self, value): '''WS-Address From value -- From server returned. ''' if value != self._messageID: raise WSActionException, 'wrong WS-Address RelatesTo(%s), expecting %s'%(value,self._messageID) def _checkReplyTo(self, value): '''WS-Address From value -- From server returned in wsa:To ''' if value != self._replyTo: raise WSActionException, 'wrong WS-Address ReplyTo(%s), expecting %s'%(value,self._replyTo) def setAction(self, action): self._action = action def getAction(self): return self._action def getRelatesTo(self): return self._relatesTo def getMessageID(self): return self._messageID def _getWSAddressTypeCodes(self, **kw): '''kw -- namespaceURI keys with sequence of element names. ''' typecodes = [] try: for nsuri,elements in kw.items(): for el in elements: typecode = GED(nsuri, el) if typecode is None: raise WSActionException, 'Missing namespace, import "%s"' %nsuri typecodes.append(typecode) else: pass except EvaluateException, ex: raise EvaluateException, \ 'To use ws-addressing register typecodes for namespace(%s)' %self.wsAddressURI return typecodes def checkResponse(self, ps, action): ''' ps -- ParsedSoap action -- ws-action for response ''' namespaceURI = self.wsAddressURI d = {namespaceURI:("MessageID","Action","To","From","RelatesTo")} typecodes = self._getWSAddressTypeCodes(**d) pyobjs = ps.ParseHeaderElements(typecodes) got_action = pyobjs.get((namespaceURI,"Action")) self._checkAction(got_action, action) From = pyobjs.get((namespaceURI,"From")) self._checkFrom(From) RelatesTo = pyobjs.get((namespaceURI,"RelatesTo")) self._checkRelatesTo(RelatesTo) To = pyobjs.get((namespaceURI,"To")) if To: self._checkReplyTo(To) def setRequest(self, endPointReference, action): '''Call For Request ''' self._action = action self.header_pyobjs = None pyobjs = [] namespaceURI = self.wsAddressURI addressTo = self._addressTo messageID = self._messageID = "uuid:%s" %time.time() # Set Message Information Headers # MessageID typecode = GED(namespaceURI, "MessageID") pyobjs.append(typecode.pyclass(messageID)) # Action typecode = GED(namespaceURI, "Action") pyobjs.append(typecode.pyclass(action)) # To typecode = GED(namespaceURI, "To") pyobjs.append(typecode.pyclass(addressTo)) # From typecode = GED(namespaceURI, "From") mihFrom = typecode.pyclass() mihFrom._Address = self.anonymousURI pyobjs.append(mihFrom) if endPointReference: if hasattr(endPointReference, 'typecode') is False: raise EvaluateException, 'endPointReference must have a typecode attribute' if isinstance(endPointReference.typecode, \ GTD(namespaceURI ,'EndpointReferenceType')) is False: raise EvaluateException, 'endPointReference must be of type %s' \ %GTD(namespaceURI ,'EndpointReferenceType') ReferenceProperties = endPointReference._ReferenceProperties any = ReferenceProperties._any or [] #if not (what.maxOccurs=='unbounded' and type(any) in _seqtypes): # raise EvaluateException, 'ReferenceProperties assumed maxOccurs unbounded' for v in any: if not hasattr(v,'typecode'): raise EvaluateException, ' element, instance missing typecode attribute' pyobjs.append(v) #pyobjs.append(v) self.header_pyobjs = tuple(pyobjs) def setResponseFromWSAddress(self, address, localURL): '''Server-side has to set these fields in response. address -- Address instance, representing a WS-Address ''' self.From = localURL self.header_pyobjs = None pyobjs = [] namespaceURI = self.wsAddressURI for nsuri,name,value in (\ (namespaceURI, "Action", self._action), (namespaceURI, "MessageID","uuid:%s" %time.time()), (namespaceURI, "RelatesTo", address.getMessageID()), (namespaceURI, "To", self.anonymousURI),): typecode = GED(nsuri, name) pyobjs.append(typecode.pyclass(value)) typecode = GED(nsuri, "From") pyobj = typecode.pyclass() pyobj._Address = self.From pyobjs.append(pyobj) self.header_pyobjs = tuple(pyobjs) def serialize(self, sw, **kw): ''' sw -- SoapWriter instance, add WS-Address header. ''' for pyobj in self.header_pyobjs: if hasattr(pyobj, 'typecode') is False: raise RuntimeError, 'all header pyobjs must have a typecode attribute' sw.serialize_header(pyobj, **kw) def parse(self, ps, **kw): ''' ps -- ParsedSoap instance ''' namespaceURI = self.wsAddressURI elements = ("MessageID","Action","To","From","RelatesTo") d = {namespaceURI:elements} typecodes = self._getWSAddressTypeCodes(**d) pyobjs = ps.ParseHeaderElements(typecodes) self._messageID = pyobjs[(namespaceURI,elements[0])] self._action = pyobjs[(namespaceURI,elements[1])] self._addressTo = pyobjs[(namespaceURI,elements[2])] self._from = pyobjs[(namespaceURI,elements[3])] self._relatesTo = pyobjs[(namespaceURI,elements[4])] # TODO: Remove MessageContainer. Hopefully the new functionality # makes this irrelevant. But could create an Interop problem. """ class MessageContainer: '''Automatically wraps all primitive types so attributes can be specified. ''' class IntHolder(int): pass class StrHolder(str): pass class UnicodeHolder(unicode): pass class LongHolder(long): pass class FloatHolder(float): pass class BoolHolder(int): pass #class TupleHolder(tuple): pass #class ListHolder(list): pass typecode = None pyclass_list = [IntHolder,StrHolder,UnicodeHolder,LongHolder, FloatHolder,BoolHolder,] def __setattr__(self, key, value): '''wrap all primitives with Holders if present. ''' if type(value) in _seqtypes: value = list(value) for indx in range(len(value)): try: item = self._wrap(value[indx]) except TypeError, ex: pass else: value[indx] = item elif type(value) is bool: value = BoolHolder(value) else: try: value = self._wrap(value) except TypeError, ex: pass self.__dict__[key] = value def _wrap(self, value): '''wrap primitive, return None ''' if value is None: return value for pyclass in self.pyclass_list: if issubclass(value.__class__, pyclass): break else: raise TypeError, 'MessageContainer does not know about type %s' %(type(value)) return pyclass(value) def setUp(self, typecode=None, pyclass=None): '''set up all attribute names (aname) in this python instance. If what is a ComplexType or a simpleType w/attributes instantiate a new MessageContainer, else set attribute aname to None. ''' if typecode is None: typecode = self.typecode else: self.typecode = typecode if not isinstance(typecode, TypeCode): raise TypeError, 'typecode must be a TypeCode class instance' if isinstance(typecode, ComplexType): if typecode.has_attributes() is True: setattr(self, typecode.attrs_aname, {}) if typecode.mixed is True: setattr(self, typecode.mixed_aname, None) for what in typecode.ofwhat: setattr(self, what.aname, None) if isinstance(what, ComplexType): setattr(self, what.aname, MessageContainer()) getattr(self, what.aname).setUp(typecode=what) else: raise TypeError, 'Primitive type' """ if __name__ == '__main__': print _copyright