The SOAP Interface

Summary:
The Curl® language provides support for requests for Web services using SOAP 1.1 over HTTP. This includes:
  • automatic mapping from XML Schema simple types to Curl language types
  • the creation of Curl language descriptors corresponding to SOAP remote procedure calls (RPCs)
  • a straightforward way to define your own marshaling and unmarshaling of XML types, and to register those mappings
  • a mapping from SOAP faults to Curl language exceptions


Note: There is a more complete and full featured SOAP implementation in the Curl® WSDK. It includes full support for almost all SOAP data types, support for references, asynchronous SOAP calls and automatic code generation from WSDL files. See the Curl web site for more information about the Curl WSDK.

Introduction

Adding access to Web services to your applet written in the Curl language is straightforward via the SOAP (Simple Object Access Protocol) interface, contained in the CURL.XML.SOAP package. Suppose, for example, that you have an applet that does some statistical analysis of stock prices. The analytic algorithms run locally on the client machine, but it is necessary periodically to go to a Web site to get current data for analysis. For that, you need to add code to your applet that sends a request for information to the Web service and handles the returning data.
SOAP is an XML-based protocol for the exchange of information in a distributed environment. You add SOAP remote procedure calls (RPCs) to an applet written in the Curl language using the Soap-1-1-HttpOperation class to describe the operation and the Soap-1-1-StandardArgumentDescriptor class to define its input and output arguments.
The runtime supports SOAP over HTTP by automatically converting many XML Schema built-in data types to Curl language types. In addition, there is support for creating and registering the marshalers and unmarshalers needed to convert between Curl language types and other XML types.

A Recipe for Calling SOAP Services from an Applet

A. To use the Curl language to call a SOAP service over HTTP, follow these steps:
  1. Locate the necessary information about the interface of a SOAP service. This includes its URL, the value of the SOAPAction HTTP header, the operation name, and the names and types of its input and output arguments. This information may be available from a Web Service Description Language (WSDL) file or in some other form.
  2. Since the types of the arguments will be XML types, they need to be mapped into Curl language types. The runtime supplies some mappings between XML types and Curl language types, and in addition gives you the means to supply your own mappings. If all of the types of the input and output arguments are included in the runtime-supplied mappings, there is no need to supply your own mappings. For the case where you do need to create such mappings, see Part B below.
  3. From the information in Steps 1 and 2, initialize an instance of Soap-1-1-HttpOperation and instances of Soap-1-1-StandardArgumentDescriptors. You create one instance of a Soap-1-1-StandardArgumentDescriptor for each input and output argument.
  4. Call the SOAP service using the call method on the instance of Soap-1-1-HttpOperation you initialized in the previous step.
B. In the case referred to in Step 2 above where the arguments are of types that are not mapped automatically, you need to supply your own mappings. To do this, for each such mapping between an XML type and Curl language type:
  1. Identify the existing Curl language type or create a new Curl language type you want to map the XML type to or from. See Data Types for details on existing Curl language types.
  2. Write a marshaler or unmarshaler for the mapping. For input arguments, you must supply a marshaler that converts values of the Curl language type to values of the XML type. For output arguments, you will need an unmarshaler that converts values of an XML type to values of a Curl language type.

    In addition, before calling a SOAP service that requires user-supplied mappings:
  3. Create an instance of XMLCurlMappingRegistry.
  4. Call the map-types method on the instance of XMLCurlMappingRegistry for each mapping.
  5. Include the instance of XMLCurlMappingRegistry as the value of the registry keyword argument in the Soap-1-1-HttpOperation.call method.
These steps are explained in greater detail below.
Note: To use the SOAP interface, you need to import the CURL.XML.SOAP package into your applet; include the statement:
{import * from CURL.XML.SOAP}
in any applet that will use the classes in this package.

Getting from WSDL to Your SOAP Call

Consider the following WSDL (Web Service Description Language; see www.w3.org/TR/wsdl for more about WSDL) file describing the interface to a weather Web service; the service takes as input a zip code and returns the temperature in that zip code's location.

A Sample WSDL File

Here is an example of a WSDL file describing a Web service.
<definitions name="TemperatureService" 
targetNamespace="http://www.xmethods.net/sd/TemperatureService.wsdl">
  <message name="getTempRequest">
    <part name="zipcode" type="xsd:string"/>
  </message>
  <message name="getTempResponse">
    <part name="return" type="xsd:float"/>
  </message>
  <portType name="TemperaturePortType">
    <operation name="getTemp">
      <input message="tns:getTempRequest"/>
      <output message="tns:getTempResponse"/>
    </operation>
  </portType>
  <binding name="TemperatureBinding" type="tns:TemperaturePortType">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="getTemp">
      <soap:operation soapAction=""/>
      <input>
        <soap:body use="encoded" namespace="urn:xmethods-Temperature-Demo" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </input>
      <output>
        <soap:body use="encoded" namespace="urn:xmethods-Temperature-Demo" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </output>
    </operation>
  </binding>
  <service name="TemperatureService">
    <documentation>
      Returns current temperature in a given U.S. zipcode  
    </documentation>
    <port name="TemperaturePort" binding="tns:TemperatureBinding">
      <soap:address location="http://services.xmethods.net:80/soap/servlet/rpcrouter"/>
    </port>
  </service>
</definitions>

Translating from WSDL into Soap-1-1-HttpOperation

The process of finding the various pieces of information contained in the WSDL, or an equivalent Web service description, consists mainly in identifying: the name of the operation, any input arguments, and any output arguments. Since elements are nested, delimited by <element> <\element>, you will need to search inside some elements to find others.
The default constructor for Soap-1-1-HttpOperation has these arguments: The following sections explain how to extract values for them from the Web service description.

The operation Argument in the Soap-1-1-HttpOperation Constructor

Contained within the binding element is an operation element. The operation element has a name attribute. Contained within the operation element is a body element. The namespace of the body element is "http://schemas.xmlsoap.org/wsdl/soap/". The body element has a namespace attribute. The operation argument in the Soap-1-1-HttpOperation is XMLName, whose namespace is the value of the namespace attribute of the body element and whose local-name is the value of the name attribute of the operation element.
In the WSDL file above, the value of the namespace attribute of the body element is "urn:xmethods-Temperature", and the value of the name attribute of the operation element is "getTemp". So the value of the operation argument in the Soap-1-1-HttpOperation is {new XMLName, "urn:xmethods-Temperature", "getTemp"}.

The address Argument in the Soap-1-1-HttpOperation Constructor

Locate the port element corresponding to the binding element referred to above. The port element is contained within a service element. Locate the address element contained in the port element. The namespace of the address element is "http://schemas.xmlsoap.org/wsdl/soap/". The address argument in the Soap-1-1-HttpOperation constructor is the value of the location attribute in the address element.
In the WSDL file shown above, the value of the location attribute in the address element is "http://services.xmethods.net:80/soap/servlet/rpcrouter". This is the value for the address argument in the Soap-1-1-HttpOperation constructor.

The soap-action Argument in the Soap-1-1-HttpOperation Constructor

Contained in the operation element with the name attribute is an operation element. This operation element has the namespace "http://schemas.xmlsoap.org/wsdl/soap/". The soap-action argument in the Soap-1-1-HttpOperation constructor is the value of the soapAction attribute in the operation element.
In the WSDL file above, the value of the soapAction attribute of the operation element is "" (the empty string). So the value of the soap-action argument in the Soap-1-1-HttpOperation is "".

The input-arguments and output-arguments Arguments in the Soap-1-1-HttpOperation Constructor

Translating the input and output argument information from the WSDL file can be straightforward or complicated, depending on how the WSDL file is written and the complexity of the arguments. We will describe the straightforward case, as exemplified in the WSDL shown above.
Locate the operation element where we found the operation name above. It is contained in a binding element that has a type attribute. The value of the type attribute references a portType element. The value of the name attribute of the portType element will match the value of the type attribute of the binding element.
Now locate the matching operation element contained in the portType element. Along with the operation element will be an input element and an output element. Both the input element and the output element have message attributes. The value of each message attribute references a message element. The value of the name attribute of the message element will be the same as the value of the message attribute of the input element or output element. The message element describes the input or output arguments, depending on whether it is referenced by an input or output element.
Each part element corresponds to an argument. The message corresponding to "getTemp"'s input arguments is "getTempRequest", and the message corresponding to "getTemp"'s output arguments is "getTempResponse". The "getTempRequest" message contains one part element, so the "getTemp" operation has just one input argument.

How to Construct a Soap-1-1-StandardArgumentDescriptor from a part Element

The default constructor for Soap-1-1-StandardArgumentDescriptor has these arguments: The following paragraphs explain how to extract values for them from the Web service description.
The name argument of the Soap-1-1-StandardArgumentDescriptor constructor is the value of the name attribute of the part element. For the part element in "getTempRequest", the value of the name attribute is "zipcode". Since "zipcode" is not namespace qualified, the value of the name argument to the Soap-1-1-StandardArgumentDescriptor is {new XMLName, "", "zipcode"}.
The xml-type argument of the Soap-1-1-StandardArgumentDescriptor is the value of the type attribute of the part element. For the part element in "getTempRequest", the value of the type attribute is "xsd:string", which is namespace qualified. The namespace corresponding to the namespace prefix "xsd" is "http://www.w3.org/2001/XMLSchema". So the value of the xml-type argument of the Soap-1-1-StandardArgumentDescriptor constructor is {new XMLName, "http://www.w3.org/2001/XMLSchema", "string"}.
We want to use the runtime-supplied mapping for "string" (see Supplied Mapping of Data Types), so the value of the curl-type argument to the Soap-1-1-StandardArgumentDescriptor is String.
We can accept the default value for the encoding-style argument; in this case there is no need to specify a different value.

Putting It All Together

Now that we have the various pieces, we need to instantiate the Soap-1-1-HttpOperation and Soap-1-1-StandardArgumentDescriptor classes, we can put all the operation components together in a Curl applet. The Curl language signature corresponding to the operation declared in the WSDL file shown above is:
{define-proc {getTemp zipcode:String}:float}
The procedure getTemp will be defined in the descriptors you create: the input and output arguments, and the SOAP operation being requested. Here are the argument descriptors and the SOAP operation for the request shown above, as defined in the Curl language:
{import * from CURL.XML.SOAP}
{let xsd:String = "http://www.w3.org/2001/XMLSchema"}
{let xsi:String = "http://www.w3.org/2001/XMLSchema-instance"}

|| One input argument named "zipcode" of type "string"
|| corresponding to Curl language type String.
{let input-arguments:{Array-of Soap-1-1-ArgumentDescriptor} =
    {{Array-of Soap-1-1-ArgumentDescriptor}
        {Soap-1-1-StandardArgumentDescriptor
            {XMLName "", "zipcode"},
            {XMLName xsd, "string"},
            String
        }
    }
}

|| One output argument named "return" of XML type float
|| corresponding to Curl language type float.
{let output-arguments:{Array-of Soap-1-1-ArgumentDescriptor} =
    {{Array-of Soap-1-1-ArgumentDescriptor}
        {Soap-1-1-StandardArgumentDescriptor
            {XMLName "", "return"},
            {XMLName xsd, "float"},
            float
        }
    }
}

|| The operation descriptor.
{let get-temp:Soap-1-1-HttpOperation =
    {Soap-1-1-HttpOperation
        || operation name
        {XMLName "urn:xmethods-Temperature-Demo", "getTemp"},
        || url
        {url "http://services.xmethods.net:80/soap/servlet/rpcrouter" },
        || SOAPAction value in HTTP headers
        "",
        input-arguments,
        output-arguments,
        include-type-attribute? = true, || ###
        xsi = xsi || ### 
    }
}

{let result = {get-temp.call trace?= true, "02142"}}
{value result[0]}

The SOAP Remote Procedure Call

Once you have defined the SOAP operation and specified its input and output arguments, you can add the following SOAP RPC to your Curl applet to request the service:
|| The actual SOAP call.
{let anys:{Array-of any}={get-temp.call "01915"}}
The output arguments of a SOAP RPC are returned in an Array-of any. In this example, the SOAP RPC returns a single output argument of type float. So anys[0] contains the result, a float representing a temperature.
The header and body of the SOAP message in XML corresponding to the SOAP call shown above can be viewed in An Aid to Debugging SOAP Calls.

Supplied Mapping of Data Types

When you are defining the arguments to your SOAP call, most of the common XML built-in data types are converted for you to and from the corresponding Curl language data types. Other XML types must be marshaled and unmarshaled manually; see Writing Your Own Marshalers and Unmarshalers for details.
The following table shows the supplied mapping between XML primitive and derived data types and Curl language data types.
XML TypeCurl Language Type
stringString
booleanbool
floatfloat
doubledouble
base64BinaryByteVec
longint64
intint32
shortint16
byteint8
unsignedShortuint16
unsignedByteuint8
In addition, the Curl language SOAP support can handle the mapping of array arguments. See the API Reference Manual entry for Soap-1-1-ArrayArgumentDescriptor for details.

Writing Your Own Marshalers and Unmarshalers

For the XML types (for example, struct) for which conversions to and from Curl language types are not provided, you will need to write your own marshalers and unmarshalers (also called serializers and deserializers). The following sections show how this is done.

Marshaling Example

The following example calls a SOAP operation named "calculateExchangeRate", which takes one input argument of XML type ConvertMessage, which is a struct composed of three fields: a string, a double, and a string. This input argument contains the name of a currency, an amount in that currency, and the name of a currency to convert to. The operation "calculateExchangeRate" returns a single result of XML type double, which is the corresponding amount in the second currency.
Here is an instance of the XML type ConvertMessage:
<currency>NLG</currency>
<amount>1.00000000000000000E+002</amount>
<toCurrency>DEM</toCurrency>
Here is the Curl language class corresponding to the XML type ConvertMessage; note that we have given the Curl language class the same name as the XML type's, which is not necessary.
{define-class public ConvertMessage
  field public currency:String
  field public amount:double
  field public to-currency:String

  {constructor public {default
                          currency:String,
                          amount:double,
                          to-currency:String
                      }
    set self.currency=currency
    set self.amount=amount
    set self.to-currency=to-currency
  }
}
This code for marshaling a struct uses a marshaler helper, which marshals one field of the struct, and the marshaler itself, which puts all the pieces together:
|| marshaler helper -- marshals a field of a struct.
{define-proc public {marshaler-helper
                        mapper:XMLCurlMappingRegistry,
                        encoding-style:String,
                        xos:XMLOutputStream,
                        element-name:XMLName,
                        content-xml-type:XMLName,
                        content-curl-type:Type,
                        content-value:any,
                        include-type-attribute?:bool,
                        xsi:String
                    }:void
    {xos.write-one {new XMLStartElement, element-name}}
    {mapper.marshal
        encoding-style,
        content-value asa content-curl-type,
        content-curl-type,
        content-xml-type,
        xos,
        include-type-attribute?,
        xsi
    }
    {xos.write-one {new XMLEndElement}}
    {return}
}

|| converts Curl language value to XML for ConvertMessage type
{define-proc public {convert-message-marshaler
                        mapper:XMLCurlMappingRegistry,
                        encoding-style:String,
                        value:any,
                        xos:XMLOutputStream,
                        include-type-attribute?:bool,
                        xsi:String
                    }:void

    let cm:ConvertMessage = value asa ConvertMessage
    {marshaler-helper
        mapper,
        encoding-style,
        xos,
        {new XMLName, "", "currency"},
        {new XMLName, "", "string"},
        String,
        cm.currency,
        include-type-attribute?,
        xsi
    }
    {marshaler-helper
        mapper,
        encoding-style,
        xos,
        {new XMLName, "", "amount"},
        {new XMLName, "", "double"},
        double,
        cm.amount,
        include-type-attribute?,
        xsi
    }
    {marshaler-helper
        mapper,
        encoding-style,
        xos,
        {new XMLName, "", "toCurrency"},
        {new XMLName, "", "string"},
        String,
        cm.to-currency,
        include-type-attribute?,
        xsi
    }
    {return}
}

Unmarshaling Example

The following code is an example of a custom unmarshaler. The operation, named "translate", takes an input argument of XML type string containing a hostname and does a DNS lookup on the hostname. It returns a struct called InternetAddress, containing the IP address and the hostname. Both fields of the struct are of XML type string.
Here is an instance of the XML type InternetAddress:
<IP>167.216.248.234</IP>
<Hostname>www.curl.com</Hostname>
Here is the Curl language class corresponding to the XML type InternetAddress; note that we have given the Curl language class the same name as the XML type's, which is not necessary.
{define-class public InternetAddress
  field public ip:String
  field public hostname:String

  {constructor public {default ip:String, hostname:String}
    set self.ip=ip
    set self.hostname=hostname
  }
}
As is the case for the marshaler, there is an unmarshaler helper, which unmarshals one field, and the unmarshaler itself, which puts the pieces together:
|| This procedure unmarshals a filed of a struct
{define-proc public {unmarshaler-helper
                        mapper:XMLCurlMappingRegistry,
                        encoding-style:String,
                        input:XMLInputStream,
                        element-name:XMLName,
                        content-xml-type:XMLName,
                        content-curl-type:Type
                    }:any

    let xml-token:XMLToken={input.read-one}
    {if {type-of xml-token} != XMLStartElement then
        {error "expecting XMLStartElement"}
    }
    {if (xml-token asa XMLStartElement).element != element-name then
        {error "element name mismatch"}
    }
    let return-value:any = {mapper.unmarshal
                               encoding-style,
                               content-curl-type,
                               content-xml-type,
                               input
                           }
    set xml-token={input.read-one}
    {if {type-of xml-token} != XMLEndElement then
        {error "expecting XMLEndElement"}
    }
    {return return-value}
}

||Convert XML to Curl language for type InternetAddress
{define-proc public {internet-address-unmarshaler
                        mapper:XMLCurlMappingRegistry,
                        encoding-style:String,
                        input:XMLInputStream
                    }:any

    let ip:any={unmarshaler-helper
                   mapper,
                   encoding-style,
                   input,
                   {new XMLName, "", "IP"},
                   {new XMLName, "", "string"},
                   String
               }
    let hostname:any={unmarshaler-helper
                         mapper,
                         encoding-style,
                         input,
                         {new XMLName, "", "Hostname"},
                         {new XMLName, "", "string"},
                         String
                     }
    {return {new InternetAddress, (ip asa String), (hostname asa
                                                    String)
            }
    }
}

Registering Your Type Mapping

After you have created any marshalers and unmarshalers you need, you can register the new type mappings. The process consists of: initializing a type mapping registry, registering the marshaler or unmarshaler, and including the registry keyword argument in the Soap-1-1-HttpOperation.call. For the marshaler in the example above, the code looks like this (an unmarshaler is registered exactly the same way):
|| 1. create a type mapping registry
{let my-registry:XMLCurlMappingRegistry={new XMLCurlMappingRegistry}}

|| 2. register the marshaler for ConvertMessage
{my-registry.map-types
    soap-encoding,
    {new XMLName,
        "",
        "ConvertMessage"},
    ConvertMessage,
    convert-message-marshaler,
    null
}

|| 3. the actual SOAP call
{let anys:#{Array-of any}={currency-converter.call
                              {new ConvertMessage, "NLG", 100.0, "DEM"},
                              registry=my-registry
                          }
}

Troubleshooting

Most of the problems that arise when you are using SOAP RPCs return Curl language exceptions; that is, the SOAP faults are translated to Curl language exception types. Here are some suggestions about where to look for errors when things are not working as you expected:
What Went WrongWhy It Happened
The Soap-1-1-HttpOperation.call threw an HttpException.This happens when the response status from an HTTP request containing a SOAP message is not OK (200) and the response body is empty or does not contain a SOAP message. The response status included in the HttpException will provide more information on the nature of the error. It may be that the Web server is not available, or that the address field in the Soap-1-1-HttpOperation was not set correctly.
The Soap-1-1-HttpOperation.call threw a Soap-1-1-FaultException.This happens when the response message from the SOAP service contains a SOAP fault element. A Soap-1-1-FaultException displays the fault code and fault string sub-elements of the SOAP fault element. The fault string is intended to provide a human-readable explanation of the fault. SOAP 1.1 defines four possible values for the fault code. They are:
  • "VersionMismatch"—The SOAP service found an invalid namespace for the SOAP envelope element. This may indicate that the service is not a SOAP 1.1 service.
  • "MustUnderstand"—The request contained a SOAP header sub-element with the mustUnderstand attribute, and the SOAP service could not understand or comply.
  • "Client"—The message in the request was incorrectly formed or did not contain the appropriate information to succeed.
  • "Server"—The request could not be processed for reasons not directly attributable to the contents of the message but rather to the processing of the message.
Note: A Soap-1-1-FaultException displays the fault code and the fault string, but not the rest of SOAP fault element. Most SOAP fault elements contain a detail sub-element. Set the trace? argument on the Soap-1-1-HttpOperation.call to true to display the full response in the output log and view additional sub-elements of the SOAP fault.
The Soap-1-1-HttpOperation.call gave an error of the form:
"unable to marshal value of Curl language type {type-of value} to XML type xml-type using encoding style encoding-style"
This error indicates that a mapping could not be found to marshal a value of a Curl language type to an XML type. The runtime provides a number of mappings, and you may write custom marshalers (serializers) and register your own mappings. This error indicates that a mapping could not be found in either the user-supplied registry (if a user-supplied registry is provided in the call via the registry keyword argument) or the runtime-supplied registry. The cause of this error might be that a Soap-1-1-StandardArgumentDescriptor has been incorrectly specified.
The Soap-1-1-HttpOperation.call gave an error of the form:
"unable to unmarshal value of XML type xml-type to Curl type curl-type using encoding style encoding-style"
This error indicates a mapping could not be found to unmarshal a value of an XML type to a Curl language type. The runtime provides a number of mappings, and you may write custom unmarshalers (deserializers) and register your own mappings. This error indicates that a mapping could not be found in either the user-supplied registry (if a user-supplied registry is provided in the call via the registry keyword argument) or the runtime-supplied registry. The cause of this error might be that a Soap-1-1-StandardArgumentDescriptor has been incorrectly specified.
A conversion error occurred in the marshaling or unmarshaling during a Soap-1-1-HttpOperation.call.Possible causes include: an input argument supplied in the call is of an incorrect type; a Soap-1-1-StandardArgumentDescriptor has been incorrectly specified; or there is an error in a user-supplied marshaler or unmarshaler.
The Soap-1-1-HttpOperation.call gave an error parsing the return message.One possible cause is an error in a user-supplied marshaler or unmarshaler.

An Aid to Debugging SOAP Calls

The trace? keyword argument on Soap-1-1-HttpOperation.call, when set to true, can be used to examine the body of the HTTP request containing the SOAP RPC and the body of the HTTP response. For example, when a Soap-1-1-FaultException exception is returned, you can use this to display all the sub-elements of the SOAP fault.

This argument is described in the API Reference Manual entry for Soap-1-1-HttpOperation.call.

Reading and Writing SOAP Headers

The SOAP interface also supports the reading and writing of SOAP headers; the following example makes a call to a Web service that echoes SOAP headers. This SOAP operation named "echoVoid" takes no arguments and returns no result, it simply echoes the SOAP headers used in the call.
{import * from CURL.XML.SOAP}

{let xsd:String = "http://www.w3.org/2001/XMLSchema"}

|| 0 input arguments
{let input-arguments:{Array-of Soap-1-1-ArgumentDescriptor}=
    {new {Array-of Soap-1-1-ArgumentDescriptor}}
}

|| 0 output arguments
{let output-arguments:{Array-of Soap-1-1-ArgumentDescriptor}=
    {new {Array-of Soap-1-1-ArgumentDescriptor}}
}

|| construct a header as an array of XMLToken
{let input-headers:{Array-of XMLToken}={new {Array-of XMLToken}}}
|| the header element is "echoMeStringRequest" containing a string
{input-headers.append {new XMLStartElement,
                          {new XMLName,
                              "http://soapinterop.org/echoheader/",
                              "echoMeStringRequest"}
                      }
}
{input-headers.append {new XMLCharacters, "Hello, Header"}}
{input-headers.append {new XMLEndElement}}

|| operation descriptor
{let echo-void:Soap-1-1-HttpOperation=
    {new Soap-1-1-HttpOperation,
        || operation name
        {new XMLName,"http://soapinterop.org/", "echoVoid"},
        || url
        || {url "http://mssoapinterop.org/stk/InteropC.wsdl" },
        {url "http://www.whitemesa.net/interop/std/echohdr"},
        || SOAPAction value
        || "urn:soapinterop",
        "http://soapinterop.org/",
        input-arguments,
        output-arguments
    }
}

|| the actual SOAP call -- headers are passed a value
|| of keyword argument
{let (anys:#{Array-of any}, return-headers:#{Array-of XMLToken})=
    {echo-void.call headers=input-headers, trace?=true}
}

|| The output arguments are returned in array of anys
|| This soap call returns 0 arguments
{if anys.size !=0 then
    {error "expected 0 return arguments, but got " & anys.size}
}

|| The headers are returned as array of XMLToken.
|| The header passed back should have the same contents
|| as the string passed in -- "Hello, Header"
{if return-headers.size < 4 then
    {error "error in return headers"}
}
{if {type-of return-headers[0]} != XMLStartElement then
    {error "expected first element of return headers to be
    XMLStartElement"}
}
{if {type-of return-headers[1]} != XMLAttribute then
    {error "expected first element of return headers to be
    XMLAttribute"}
}
{type-switch return-headers[2]
 case chars:XMLCharacters do
    {output "content of return header element: " & chars.characters}
 else
    {error "expect XMLCharacters as second element of return headers"}
}
{if {type-of return-headers[3]} != XMLEndElement then
    {error "expected first element of return headers to be
    XMLEndElement"}
}

Security Implications

The HTTP requests initiated by SOAP calls are subject to the same security checks as other HTTP requests. The restrictions are described in the section on Network Access Restrictions.
If your applets are accessing Web services provided by your own organization, the security impact is minimal.