Logo WHITE MESA SOFTWARE
Home   Download   Links   Documentation
 

White Mesa SOAP Server: Header Processing

Some basics...

SOAP Header blocks are used to extend an application with additional features. They are a means to "layer" additional functionality over the core message. Things like authentication mechanisms or transaction control come to mind right away. A few things are worth noting about Header blocks:

  • They MUST be namespace qualified. As a practical matter, it makes sense to define a separate namespace for each "application" you might define, and qualify the Header block tag names accordingly.
  • Header blocks may possess an 'actor' (SOAP 1.1) or 'role' (SOAP 1.2) attribute. The value of this attribute is a URI, and is used to "target" the Header block. A SOAP message may pass through intermediary nodes on its way to the ultimate destination, and the 'actor' or 'role' attribute may be used to control which intermediary processes the Header block. This assumes that each intermediary "knows" which roles it identified with, so that it recognizes the Header blocks it needs to process. In the case of the White Mesa SOAP Server, one or more roles are specified for a service as one of its configuration settings. If no 'actor' or 'role' attribute is present, it is assumed that the Header block is targeted at the ultimate destination of the message (the 'default role'). In addition, the SOAP specs define a special actor URI:
    • http://schemas.xmlsoap.org/soap/actor/next (SOAP 1.1)
    • http://www.w3.org/2003/05/soap-envelope/role/next (SOAP 1.2)
    which targets a Header block at whichever processor receives it. All SOAP processing nodes must support the special role "next", making it useful for targeting an inserted Header block at the next processor in a message path. SOAP 1.2 Header blocks may be targeted at these additional special roles:
    • http://www.w3.org/2003/05/soap-envelope/role/none
    • http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver
    The use of role "none" indicates that the block has no target, and exists only to convey data needed for the processing of other Header blocks. The use of Role "ultimateReceiver" indicates that a Header block is targeted at the ultimate SOAP receiver in a message path. The White Mesa SOAP Server recognizes all of the above special roles and processes the Header blocks accordingly.
  • Header blocks may possess a 'mustUnderstand' attribute. If this attribute is present, with a value of "1" (or optionally "true" for SOAP 1.2), then the Header block MUST be understood and processed the receiver. If not, a SOAP "MustUnderstand" Fault message will be returned to the sender. The White Mesa SOAP Server ensures that all Header blocks are "understood" before processing begins, as described below.
  • Processing of Header blocks which are targeted at the receiver but which are not marked as "mustUnderstand" is not mandatory, such Header blocks may be ignored. If so, and the receiver is acting as a SOAP intermediary, these unprocessed blocks are removed from the message before it is forwarded to the next node in the message path.
  • SOAP 1.2 Header blocks may possess a 'relay' attribute. If this attribute is present with a value of "true" or "1", and the block is targeted at a receiver acting as a SOAP intermediary, and:
    • The block is targeted at the receiver.
    • The receiver is acting as a SOAP intermediary.
    • The receiver does not "understand" and process the block.
    then the receiver MUST forward the block on to the next node in the message path. The idea is that setting relay="true" forces SOAP processors to propagate a Header block along the message path that would otherwise be lost using the default processing rules. Naturally, the SOAP 1.2 spec states that this attribute has no effect if "mustUnderstand" is also set on the Header block. The White Mesa SOAP Server takes care of forwarding SOAP 1.2 Header blocks marked with relay="true" automatically.
  • Header blocks may appear as a "literal" XML instance, or be "encoded". The instance XML of an encoded Header block is the result of applying encoding (serialization) rules to a data graph. The "SOAP-ENV:encodingStyle" attribute is used to indicate if encoding is in force on an element contained within the SOAP message envelope, and if the value:
    • http://schemas.xmlsoap.org/soap/encoding/ (SOAP 1.1)
    • http://www.w3.org/2003/05/soap-encoding(SOAP 1.2)
    is in scope for a Header block, the relevant SOAP Encoding rules are in force. If an encodingStyle declaration is in scope with values:
    • an empty URI ("") (SOAP 1.1 and 1.2)
    • http://www.w3.org/2003/05/soap-envelope/encoding/none (SOAP 1.2)
    is in scope for a Header block, then it is presumed that the XML instance is not encoded.
    The use of the "SOAP-ENV:encodingStyle" attribute by a sender is optional. If a header block is received which has no encodingStyle declaration, the White Mesa SOAP Server will attempt to look up the Header blocks definition in the WSDL document for the service. If the Header block is declared as "encoded" within the WSDL document, then it is treated as such. If it is declared as "literal", or no definition for that Header block is found in the WSDL document, then it is treated as "literal".

So how does it work?

The White Mesa SOAP server requires that Header processing be done by COM components that implement an IDispatch interface with two special methods:

  • HRESULT OnProcessHeader([in] VARIANT SOAPMsgInterface, [out, retval] VARIANT_BOOL *pResult); >
  • HRESULT OnTestMUHeaderBlock([in] BSTR bstrNamespace, [in] BSTR bstrLocalName, [out, retval] VARIANT_BOOL *pResult);

For each service, any Header processors to be used are specified in the configuration data. This information is maintained using the Control Panel application. In the case of the Header processors, it is supplied as a space delimited list of the ProgIDs. When a SOAP message is received, each of the Header processors will be instantiated by the White Mesa SOAP Server. It is important to realize that the Header processing described below occurs twice on a node for each SOAP message.

For a node acting as the "ultimate receiver" in a message path:

  • When the message is first received, prior to Body processing
  • After Body processing is complete, prior to sending the response message. This is the point at which Header blocks may be inserted into the response message.

For a node acting as an SOAP processing intermediary in a message path:

  • When the message is first received, prior to forwarding it to the next node in the message path. At this point Header blocks may be inserted into the message to be sent on to the next node.
  • After the response has arrived from the next node in the message path, prior to sending the response message back to the previous node in the path. This is the point at which Header blocks may be inserted into the response message.
Header block processing proceeds in three steps:

  • (1) Verify that "mustUnderstand" Header blocks are "understood". The SOAP processor determines which of the received Header blocks are both:
    • targeted at a role supported by the service
    • marked as "mustUnderstand"
    and then attempts to discover whether or not the Header block is supported by at least one of the Header processors. It does this by calling each of the instantiated Header processors in turn on interface method "OnTestMUHeaderBlock". The local name and namespace of the Header block is passed in, and the Header processor returns VARIANT_TRUE if it "understands" the Header block, or VARIANT_FALSE otherwise. If no Header processor "understands" the block then processing stops and a SOAP "MustUnderstand" Fault message is returned to the sender. In this way the SOAP processor ensures that all "mustUnderstand" Header blocks targeted at the node are supported by a Header processor prior to the actual processing.
  • (2) Invoke Header processors. Each of the instantiated Header processors is then called on interface method "OnProcessHeader". The [in] parameter is a VARIANT containing a pointer to the Dispatch interface of the COM object which encapsulates the SOAP message itself. This will be an instance of the "wmsoapmsg2" component, and the Header processor may interact with it via the Iwmsoapmsg2 interface it implements.

    The Header processor can use this interface to:

    • Fetch a Header block by means of a call to one of the following:
      • "GetHeaderElem(...)" to fetch a Header block encoded using SOAP Encoding rules. A SOAP encoded Header block is automatically decoded when the message is received, so a Header processor can obtain the decoded value directly. The name and namespace of the Header block desired are passed in, and if such an block exists, its value is returned as a VARIANT. This may be a simple value, an array (as a SAFEARRAY), or a structure (as an IDispatch pointer to a wmsoapstruct object).
      • "GetHeaderElemLiteral(...)" to fetch a Header block that is not encoded ("literal"). The name and namespace of the Header block desired are passed in, and if such an block exists, its value is returned as a BSTR containing the actual XML instance. It is the responsibility of the Header processor to parse the XML it receives, using any means desired.
      If the Header block is targeted at the SOAP node (as determined by the roles the service is configure to support), it is removed from the SOAP Header, that is, it is consumed.
    • Insert a Header block by means of a call to one of the following:
      • "AddHeaderElem(...)" to add a Header block that will be serialized using the SOAP Encoding rules. The name and namespace of the new block are given, along with the content, contained in a VARIANT.
      • "AddHeaderElemLiteral(...)" to add a Header block that is not encoded ("literal"). The name and namespace of the new block are given, along with the content, contained in a BSTR containing the instance XML.
      In both cases values are specified for the 'mustUnderstand' and 'actor' (or 'role') attributes. If the value given for the 'mustUnderstand' attribute is VARIANT_TRUE, then the attribute appears on the serialized Header block element with value "1", otherwise the attribute does not appear (signifying the default value "0"). Any non-empty BSTR value given for the 'actor' (or 'role') attribute results in the appearance of the attribute, otherwise it does not appear on the serialized Header block element.
    • Generate a SOAP Fault by means of a call to "SetSOAPFault(...)". The faultString and faultCode values are given. The SOAP processor will fill in the 'faultActor' (SOAP 1.1) or 'Node' (SOAP 1.2) as necessary. This may be followed up by calls to "SetFaultDetailElem(...)" to supply Fault message 'detail' elements, or to "SetFaultHeaderElem(...)" to specify any Header blocks for the Fault message.
    • Detect whether the call is being made in the request phase or the response phase of message processing by means of a call to "IsRequestPhase()".
    • Set the Next Hop URL by means of a call to "SetNextHopURL(...)" to control the message path on a hop by hop basis. An empty URL has the effect that the service functions as the ultimate destination and executes the RPC call contained in the message body. This setting is irrelevant, of course, during the response phase of message processing.
  • (3) Verify that "mustUnderstand" Header blocks were actually processed.
    Since any received Header blocks that are both:
    • targeted at a role supported by the service
    • marked as "mustUnderstand"
    are "consumed" if a Header processor fetches it for processing via a call to "GetHeaderElem(...)", then the presence of such a block after Header processing is complete implies that it was not actually processed. The SOAP processor checks for this condition and if any Header block remains unprocessed returns a SOAP "MustUnderstand" Fault message to the sender.

See the source code for the SOAP Digest Authentication Server (wmsoapauth2.cpp) for a good example of processing SOAP Encoded Header blocks. An example of processing of "literal" Header blocks is found in the source code for the WS-Routing implementation (wmwsrouter2obj.cpp ), which uses the XML parser supplied as part of the White Mesa SOAP server package.

As outlined above the SOAP processing model allows for consumption, processing, and insertion of Header blocks as the message travels from sender to destination, that is, in what corresponds to the 'request' or outgoing phase of processing in this case. Since the transport is HTTP, the return message from destination to sender happens to pass back through any intermediary nodes, and the SOAP processor will call each Header processor a second time, again providing access to the message contents. This is done to give the Header processors the opportunity to generate any side effects desired (e.g. logging), or consume or insert response message Header blocks.

Headers blocks and WSDL

WSDL 1.1 provides a means to declare Header blocks as "message parts". This is very convenient and involves placing the soap:header extension element as a child of the associated input or output element for a particular operation in the SOAP protocol binding. Each soap:header element contains a message attribute which identifies a wsdl:message element by qualified name. Also present is a part attribute, which identifies the wsdl:part within that message which defines the makeup of the Header block element. Details such as the Header block element's name and type are learned by referencing the message part declaration. The namespace attribute of the soap:header element specifies the namespace with which the Header block element name is qualified. The use attribute declares whether the Header block will be a "literal" XML instance or be "encoded". If encoded, the value of the encodingStyle attribute is a URI identifying the encoding used. soap:header elements may contain soap:headerfault elements, which declare any Header block elements that would be returned in the response message in the event that processing of the Header block by the recipient results in a fault. These have the same syntax as the soap:header elements. Here is an example snippet:

<message name="Soapsvcmgr_Headers_Request">
  <part name="AuthCS" type="xsd2:AuthCS_Struct"/>
</message>
<message name="Soapsvcmgr_Headers_Response">
  <part name="AuthSC" type="xsd2:AuthSC_Struct"/>
</message>
<message name="Soapsvcmgr_Fault_Response">
  <part name="AuthSCF" type="xsd2:AuthSC_Fault_Struct"/>
</message>
<binding name="WMSOAPSvrMgr_SOAPBinding" type="tns:WMSOAPSvrMgr_portType">   <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
  <operation name="GetServerStats">
     <soap:operation soapAction="http://www.whitemesa.com/soapsvcmgr/GetServerStats"/>
    <input>
       <soap:body use="encoded" namespace="http://www.whitemesa.com/soapsvcmgr/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      <soap:header message="tns:Soapsvcmgr_Headers_Request" part="AuthCS" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://whitemesa.com/WMSOAPSvr/soapsvcmgr/headers.xsd">
         <soap:headerfault message="tns:Soapsvcmgr_Fault_Response" part="AuthSCF" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://whitemesa.com/WMSOAPSvr/soapsvcmgr/headers.xsd"/>
       </soap:header>

    </input>
    <output>
       <soap:body use="encoded" namespace="http://www.whitemesa.com/soapsvcmgr/" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      <soap:header message="tns:Soapsvcmgr_Headers_Response" part="AuthSC" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://whitemesa.com/WMSOAPSvr/soapsvcmgr/headers.xsd"/>
    </output>
  </operation>
</binding>

In this example, Header block elements with names "AuthCS", "AuthSC", and "AuthSCF" are declared, all in namespace "http://www.whitemesa.com/soapsvcmgr/". Their types are given by the type attributes in the corresponding wsdl:message part elements. These point to types found in a schema we will assume is present in the types element of the WSDL document. In this way the necessary type information is available to serialize/deserialize the Header blocks at runtime. Also, these Header blocks use SOAP 1.1 Section 5 encoding, as indicated by the value of the use attribute ("encoded"), and the value of the encodingStyle attribute ("http://schemas.xmlsoap.org/soap/encoding/"). Note that the input message soap:header elements contain soap:headerfault elements, declaring the Header blocks that will be received in any fault message returned to the sender by the recipient if processing of the Header specified by the soap:header element fails.

Home   Download   Links   Documentation

Copyright 2003 by Robert Cunnings.   cunnings@whitemesa.com  Last modified: Feb 2, 2003