Monday, November 08, 2010 at 12:05 AM.
system.verbs.builtins.soap.rpc.client
on client (actionURI, methodName, adrparams, rpcServer="localhost", rpcPort=user.inetd.config.http.port, username="", password="", fldebug=false, ticksToTimeOut=nil, flShowMessages=true, soapAction=nil, methodNamespace=nil, methodNamespaceURI=nil, customStructType=nil, customNamespace=nil, customNamespaceURI=nil, charset=soap.constants.charsetUsAscii, flEnforceSimpleTypes=true) { <<Changes: <<02/04/01; 5:37:09 PM by JES <<Verify that the supplied methodName is valid. Prevents generation of invalid XML. <<03/25/01; 2:22:56 AM by JES <<New optional parameter, soapAction, which overrides the value of the SOAPAction HTTP header. (Normally it defaults to the value of ActionURI.) <<New optional parameters, methodNamespace and methodNamespaceURI, facilitate namespace declaration in the methodCall element (the first sub-element of <Body>). <<03/25/01; 7:12:26 PM by JES <<New optional parameters: customArrayType, customNamespace, and customNamespaceURI -- for setting array type, namespace and namespaceURI for arrays of structs. <<04/01/01; 1:02:03 AM by JES <<If there are no input parameters, don't add a parameter accessor element. <<04/02/01; 5:10:05 PM by JES <<Fixed a bug where a closing tag for the method element wasn't generated. <<04/02/01; 7:29:04 PM by JES <<If methodNamespace is given, but methodNamespaceURI is not, generate an error. If methodNamespaceURI is given, but methodNamespace is not, generate an error. <<04/03/01; 7:21:40 PM by JES <<New optional parameter, charset, specifies the character encoding for the request. Default is us-ascii. <<04/06/01; 7:05:08 PM by JES <<Fixed a bug finding the response element: find the element whose name begins with the request method name. (see Section 7.1) <<04/07/01; 7:21:47 PM by JES <<Fixed a bug where void method calls (calls with no parameters) would not have namespace qualification on the method call element. Changed name of customArrayType parameter to customStructType. <<04/13/01; 12:52:58 PM by JES <<Fixed a bug where void method calls made without specifying a methodNamespace would not have a closing tag on the parameter element. <<05/29/01; 7:34:39 PM by JES <<Fixed a problem decoding entity-encoded markup. bundle { //make sure methodName is valid on methodNameError () { scriptError ("Can't encode the SOAP request because '" + methodName + "' is not a valid method name.")}; local (ch = methodName[1]); if not string.isAlpha (ch) { if (ch != '_') and (ch != ':') { methodNameError ()}}; local (i, ct = sizeOf (methodName)); on isValidNameChar (ch) { if string.isAlpha (ch) { return (true)}; if string.isNumeric (ch) { return (true)}; case ch { '.'; '-'; '_'; ':' { return (true)}}; return (false)}; if ct > 1 { for i = 2 to ct { if not isValidNameChar (methodName[i]) { methodNameError ()}}}}; soap.init (); if ticksToTimeOut == nil { ticksToTimeOut = user.soap.prefs.rpcClientDefaultTimeout}; local (returnvalue, responsename = methodname + "Response"); local (request, adrrequest = @request, requestText); local (response, adrresponse = @response, responseText, responseHeaders); local (flEntityEncodeHighAscii = false); bundle { //encode request local (adrdocument, adrenvelope, adrbody, adrmethodcall); bundle { //build document tree adrdocument = soap.xmlutils.createRequest (adrrequest); adrenvelope = soap.xmlutils.addElement (adrdocument, "Envelope"); soap.xmlutils.setNamespacePrefixOfElement (adrenvelope, soap.constants.nsEnvelopePrefix); soap.xmlutils.declareNamespaceInElement (adrenvelope, soap.constants.nsEnvelopePrefix, soap.constants.nsEnvelopeURI); soap.xmlutils.declareNamespaceInElement (adrenvelope, soap.constants.nsEncodingPrefix, soap.constants.nsEncodingURI); soap.xmlutils.declareNamespaceInElement (adrenvelope, soap.constants.nsSchemaPrefix, soap.constants.nsSchemaURI); soap.xmlutils.declareNamespaceInElement (adrenvelope, soap.constants.nsSchemaDataPrefix, soap.constants.nsSchemaDataURI); soap.xmlutils.addAttributeValue (adrenvelope, soap.constants.nsEnvelopePrefix +":" + "encodingStyle", soap.constants.nsEncodingURI); adrbody = soap.xmlutils.addElement (adrenvelope, "Body"); soap.xmlutils.setNamespacePrefixOfElement (adrbody, soap.constants.nsEnvelopePrefix); adrmethodcall = soap.xmlutils.addElement (adrbody, methodName); if methodNamespace != nil or methodNamespaceURI != nil { //04/02/2001 JES: error if both values aren't specified if methodNamespaceURI == nil { scriptError ("Can't encode the request because a methodNamespace was specified, but no methodNamespaceURI was given.")}; if methodNamespace == nil { scriptError ("Can't encode the request because a methodNamespaceURI was specified, but no methodNamespace was given.")}}; if methodNamespace != nil { //03/25/2001 JES: set the methodCall namespace soap.xmlutils.setNamespacePrefixOfElement (adrmethodcall, methodNamespace)}; if methodNamespaceURI != nil { //03/25/2001 JES: set the methodCall namespaceURI soap.xmlutils.declareNamespaceInElement (adrmethodcall, methodNamespace, methodNamespaceURI)}}; bundle { //determine character encoding, error if unsupported local (supportsUTF = true); if not defined (string.ansiToUtf16) { //check to see if we can do UTF supportsUTF = false}; case string.lower (charset) { soap.constants.charsetLatin1 { };//default behavior soap.constants.charsetUsAscii { flEntityEncodeHighAscii = true}; soap.constants.charsetUtf8 { if not supportsUTF { flEntityEncodeHighAscii = true}}; //treat UFT-8 as a super-set of US-ASCII, but encode high-ascii characters as XML entities soap.constants.charsetUtf16 { if not supportsUTF { scriptError ("Can't encode the request because you must be running at least version 7.0b42 in order to use UTF-16 character encoding.")}}} else { scriptError ("Can't encode the request because \"" + charset + "\" is not a supported character set.")}}; bundle { //encode parameters local (ix, ct = sizeOf (adrparams^)); if ct > 0 { case typeOf (adrparams^) { listType { for ix = 1 to ct { soap.encode.main (adrresponse, adrparams^[ix], adrmethodcall, "param" + ix, customStructType, customNamespace, customNamespaceURI, flEntityEncodeHighAscii)}}; recordType { for ix = 1 to ct { soap.encode.main (adrresponse, adrparams^[ix], adrmethodcall, nameOf (adrparams^[ix]), customStructType, customNamespace, customNamespaceURI, flEntityEncodeHighAscii)}}} else { scripterror ("Can't encode parameters because adrparams is not a list or record.")}} else { if methodNamespace == "" { table.assign (adrmethodcall, "")}}}; bundle { //decompile document tree into XML text requestText = xml.decompile (adrdocument)}; bundle { //convert the text to the character set specified by charset case string.lower (charset) { //assume us-ascii soap.constants.charsetUtf8 { if not flEntityEncodeHighAscii { requestText = string.ansiToUtf8 (requestText)}}; soap.constants.charsetUtf16 { requestText = string.ansiToUtf16 (requestText)}}}}; bundle { //make method call local (requestHeaders); new (tableType, @headers); if soapAction == nil { //03/25/2001 JES: default to actionURI soapAction = actionURI}; headers.SOAPAction = "\"" + soapAction + "\""; s = tcp.httpClient (method:"POST", server:rpcServer, port:rpcPort, path:actionURI, data:requestText, datatype:"text/xml; charset=\"" + charset + "\"", username:username, password:password, adrHdrTable:@headers, debug:fldebug, timeOutTicks:ticksToTimeOut, flMessages:flShowMessages); responseText = string.httpResultSplit (s, @responseHeaders)}; bundle { //decode response local (adrdocument, adrenvelope, adrheader, adrbody, adrothers, adrmethodresponse); bundle { //convert to response text to ascii, if needed local (responsecharset = "us-ascii"); //default to ascii if defined (responseHeaders.["Content-Type"]) { local (contentType = soap.stringutils.parseHeader (responseHeaders.["Content-Type"])); if defined (contentType.charset) { responsecharset = contentType.charset}}; case string.lower (responsecharset) { soap.constants.charsetUtf8; soap.constants.charsetUtf16 { if not defined (string.utf8ToAnsi) { scriptError ("Can't decode the response because \"" + responsecharset + "\" is not a supported character encoding.")}; if string.lower (responsecharset) == soap.constants.charsetUtf8 { responseText = string.utf8ToAnsi (responseText)} else { //utf16 responseText = string.utf16ToAnsi (responseText)}}}}; bundle { //build document tree from XML text on fixEntities (s) { <<Works around a bug in xml.compile, where both " and " are decoded as ". Clearly this shouldn't be the case, but to fix xml-compile at this point is asking for trouble. s = string.replaceAll (s, "&", "&amp;"); s = string.replaceAll (s, """, "&quot;"); s = string.replaceAll (s, "'", "&apos;"); s = string.replaceAll (s, "<", "&lt;"); s = string.replaceAll (s, ">", "&gt;"); return (s)}; local (xmltext = string (responseText)); xmltext = fixEntities (xmltext); adrdocument = soap.xmlutils.createResponse (adrresponse, string (xmltext))}; bundle { //locate envelope adrenvelope = soap.xmlutils.getFirstChildElement (adrdocument); soap.xmlutils.pushScope (adrresponse, adrenvelope); if not soap.xmlutils.elementMatches (adrresponse, adrenvelope, @soap.qname.envelope) { scripterror ("Can't decode the response because the Envelope element was not found.")}}; bundle { //locate header, body, and others local (adrtemp = soap.xmlutils.getFirstChildElement (adrenvelope)); soap.xmlutils.pushScope (adrresponse, adrtemp); if soap.xmlutils.elementMatches (adrresponse, adrtemp, @soap.qname.header) { adrheader = adrtemp; soap.xmlutils.popScope (adrresponse); adrtemp = soap.xmlutils.getNextSiblingElement (adrtemp); soap.xmlutils.pushScope (adrresponse, adrtemp)}; if soap.xmlutils.elementMatches (adrresponse, adrtemp, @soap.qname.body) { adrbody = adrtemp; adrothers = soap.xmlutils.getNextSiblingElement (adrtemp)} else { scripterror ("Can't decode the response because the Body element was not found.")}}; bundle { //check body for fault struct local (nomad = soap.xmlutils.getFirstChildElement (adrbody)); while (nomad) { if soap.xmlutils.elementMatches (adrresponse, nomad, @soap.qname.fault) { local (adrfaultcode = soap.xmlutils.getNamedChildElement (nomad, "faultcode")); local (adrfaultstring = soap.xmlutils.getNamedChildElement (nomad, "faultstring")); if adrfaultcode { scripterror ("The server, " + rpcServer + ", returned a " + soap.xmlutils.getCharacterData (adrfaultcode) + " fault: " + soap.xmlutils.decodeAmpersands (soap.xmlutils.getCharacterData (adrfaultstring)))} else { scripterror ("The server, " + rpcServer + ", returned a (malformed) fault with no faultcode element: " + soap.xmlutils.decodeAmpersands (soap.xmlutils.getCharacterData (adrfaultstring)))}}; nomad = soap.xmlutils.getNextSiblingElement (nomad)}}; bundle { //locate method call adrmethodresponse = soap.xmlutils.getFirstChildElement (adrbody); if not (xml.convertToDisplayName (nameOf (adrmethodresponse^)) beginsWith methodname) { scripterror ("Response name is incompatible with request method name.")}}; bundle { //decode the method response struct soap.xmlutils.pushScope (adrresponse, adrmethodresponse); local (adritem = soap.xmlutils.getFirstChildElement (adrmethodresponse)); returnvalue = soap.decode.main (adrresponse, adritem, flEnforceSimpleTypes)}}; return (returnvalue)}; <<bundle //debugging code <<bundle //examples/getStateList <<local (returnvalue, params = {{1, 2, 3}}) <<returnvalue = client (actionURI:"/examples", methodName:"getStateList", adrparams:@params, rpcServer:"superhonker.userland.com", fldebug:true) <<edit (@scratchpad.httpCommand) <<edit (@scratchpad.httpResult) <<dialog.notify (returnvalue) <<bundle //examples/getStateName <<local (returnvalue, params = {50}) <<returnvalue = client (actionURI:"/examples", methodName:"getStateName", adrparams:@params) <<dialog.notify (returnvalue) <<bundle //examples/getStateNames <<local (returnvalue, params = {11, 22, 33, 44}) <<returnvalue = client (actionURI:"/examples", methodName:"getStateNames", rpcServer:"superhonker.userland.com", adrparams:@params) <<dialog.notify (returnvalue) <<bundle //examples/getStateStruct <<local (returnvalue, params, struct) <<new (tableType, @struct) <<struct.a = 11 <<struct.b = 22 <<struct.c = 33 <<struct.d = 44 <<params = {struct} <<returnvalue = client (actionURI:"/examples", methodName:"getStateStruct", rpcServer:"superhonker.userland.com", adrparams:@params, fldebug:true) <<temp.returnvalue = returnvalue <<edit (@temp.returnvalue) <<bundle //examples/getCurrentTime <<local (returnvalue, params = {}) <<returnvalue = client (actionURI:"/examples", methodName:"getCurrentTime", rpcServer:"superhonker.userland.com", adrparams:@params, fldebug:true) <<dialog.notify (returnvalue) <<bundle //examples/getCurrentTime -- throws an error because there are too many parameters <<local (returnvalue, params = {1, 2, 3}) <<returnvalue = client (actionURI:"/examples", methodName:"getCurrentTime", rpcServer:"superhonker.userland.com", adrparams:@params, fldebug:true) <<dialog.notify (returnvalue)
This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.