skip to content

A SOAP 1.2 message crosses a forwarding intermediary before its ultimate receiver; how do role, mustUnderstand and relay decide what the intermediary processes, removes or forwards?

level: seniorimportance: should knowfreq 12%

answer

  1. targeting by role, not by position
  2. next, ultimateReceiver, none, custom URIs
  3. processed blocks leave the message
  4. ignored blocks too, unless relayable
  5. SOAP 1.1's actor has no relay

basics

~20 s

The intermediary handles only header blocks whose role it plays, next or custom roles it assumes. When forwarding, it removes blocks it processed unless reinserted, removes targeted blocks it ignored unless relay="true", and passes on blocks aimed at other roles.

solid answer

~50 s

A SOAP 1.2 node first fixes the roles it acts in. Every intermediary and the ultimate receiver act as `next`; only the ultimate receiver acts as `ultimateReceiver`, which is also what an omitted `role` means; no node acts as `none`; custom role URIs are assumed as the node is configured. A block is targeted at the node if its `role` is one of those. Mandatory targeted blocks must be understood, or the node faults with `env:MustUnderstand` and forwards nothing. When forwarding, the intermediary MUST remove every block it processed, which the block's own specification may have it reinsert; MUST remove targeted blocks it ignored; and MUST retain ignored targeted blocks marked `relay="true"`. Blocks for roles it does not play travel on; `relay` has no effect on a block also marked `mustUnderstand="true"`. SOAP 1.1 targets with `actor` and has no `relay`: an intermediary removes everything aimed at it.

code

xml · 19 lines
xml
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
  <env:Header>
    <a:AuditStamp xmlns:a="http://example.org/audit"
        env:role="http://www.w3.org/2003/05/soap-envelope/role/next"
        env:mustUnderstand="true">gw-eu-1</a:AuditStamp>
    <c:CacheHint xmlns:c="http://example.org/cache"
        env:role="http://example.org/roles/cache"
        env:relay="true">max-age=60</c:CacheHint>
    <q:Priority xmlns:q="http://example.org/qos"
        env:role="http://www.w3.org/2003/05/soap-envelope/role/next">high</q:Priority>
    <t:TxContext xmlns:t="http://example.org/tx"
        env:mustUnderstand="true">tx-7781</t:TxContext>
  </env:Header>
  <env:Body>
    <m:SubmitClaim xmlns:m="http://example.org/claims">
      <m:ClaimId>C-5520</m:ClaimId>
    </m:SubmitClaim>
  </env:Body>
</env:Envelope>

go deeper

for a junior

Recall that header blocks can be aimed at intermediaries or at the final receiver, and that the Body is always for the ultimate receiver.

for a middle

Explain the three special SOAP 1.2 roles, that an omitted role means ultimateReceiver, and how SOAP 1.1's actor maps onto SOAP 1.2's role.

for a senior

Trace each header block through an intermediary with the forwarding table, processed, ignored with or without relay, or untargeted, and say where a mandatory flag overrides relay.

for a principal

Decide which cross-cutting concerns belong in role-targeted header blocks handled along the path and which in the payload, weighing decoupling against path-dependent behaviour that is hard to test.

## The message path A SOAP message travels from an **initial sender** to an **ultimate receiver** through zero or more **intermediaries**: nodes that receive a message and forward it, such as a gateway that stamps an audit record or a cache. SOAP 1.2 distinguishes **forwarding intermediaries**, which process and relay according to the header blocks they receive, from **active intermediaries**, which may also change the message in ways no inbound header asked for, for example by encrypting parts of it. The **Body** is the ultimate receiver's: only that node is required to process it. Header blocks are how work is handed to nodes along the way. ## Roles decide who is targeted In SOAP 1.2 each node acts in one or more **roles**, each named by a URI, and its set of roles stays fixed while it processes one message. A header block's `role` attribute names the role it is aimed at; the block is **targeted** at a node when the node acts in that role. | Role (short name) | URI | Who acts in it | |---|---|---| | `next` | `http://www.w3.org/2003/05/soap-envelope/role/next` | every intermediary and the ultimate receiver MUST | | `ultimateReceiver` | `http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver` | the ultimate receiver MUST; omitting `role` means this role | | `none` | `http://www.w3.org/2003/05/soap-envelope/role/none` | no node may; such blocks are never formally processed | | custom URI | any URI the application defines | whichever nodes are configured to assume it | Role names carry **no routing semantics**: naming a role does not deliver the message to any node. How a node decides which custom roles it plays (hard-coded choice, the address the message arrived at, configuration) is left to the implementation. ## Processing at the intermediary 1. Determine the roles the node acts in. 2. Find the mandatory targeted blocks. If any is not understood, generate one `env:MustUnderstand` fault and stop; nothing is forwarded. 3. Process the mandatory targeted blocks; optional targeted blocks MAY be processed or ignored. 4. If the exchange calls for it, build the forwarded message. ## What the forwarded message keeps When it builds the outbound message, a forwarding intermediary MUST remove every processed block, MUST remove every non-relayable targeted block it ignored, and MUST retain every **relayable** targeted block it ignored. Part 1's forwarding table sums it up: | Block's role | Node acts in it? | Processed? | Forwarded? | |---|---|---|---| | `next` or a custom role | yes | yes | no, unless reinserted | | `next` or a custom role | yes | no | no, unless `relay="true"` | | a custom role | no | n/a | yes | | `none` | no | n/a | yes | A block aimed at `ultimateReceiver`, or with no `role`, is not targeted at any intermediary, so it reaches the ultimate receiver with the Body. Three rules qualify `relay`: - It has **no effect** on blocks aimed at roles the intermediary does not play; those travel on anyway. - It has **no effect** on a block that also carries `mustUnderstand="true"`: that block is processed, which removes it, or the node faults. - It has **no effect** at the ultimate receiver, where nothing is forwarded. **Reinsertion** is how a block keeps travelling after being processed: the block's specification may call for it to be put back in the forwarded message. SOAP 1.2 deliberately describes this as removing and reinserting rather than leaving the block in place, to stress that every node along the path must process it. ## Walking the example Take an intermediary that acts in `next` and in `http://example.org/roles/cache`, understands `a:AuditStamp` and nothing else in the example message: - `a:AuditStamp` (role `next`, mandatory) is targeted, understood and processed, so it is **removed** unless its specification says to reinsert it. - `c:CacheHint` (custom cache role, `relay="true"`) is targeted and ignored, but relayable, so it is **retained**. - `q:Priority` (role `next`, optional, no relay) is targeted and ignored, so it is **removed**. - `t:TxContext` (no role, so `ultimateReceiver`) is not targeted here and is **forwarded**; the ultimate receiver must understand it or fault. Had the intermediary not understood `a:AuditStamp`, it would have faulted with `env:MustUnderstand` and forwarded nothing. ## SOAP 1.1's simpler model SOAP 1.1 targets entries with the **`actor`** attribute. Its only special URI is `http://schemas.xmlsoap.org/soap/actor/next`, meaning the first application that processes the message; omitting `actor` means the ultimate destination. There is no `none`, no named `ultimateReceiver` and no `relay`: a node that is not the ultimate destination MUST remove all parts aimed at it before forwarding, processed or not, and MAY insert a similar header of its own, creating a new contract with the next recipient.

  • After an intermediary processes a block aimed at the next role, can the following node still see it?
    Only if it is reinserted. Processed blocks are removed from the forwarded message; the block's own specification may call for reinsertion. SOAP 1.2 frames it as removal plus reinsertion, not leaving it in place, to make clear that each node on the path has to process it.
  • What is a header block aimed at the none role for?
    No node may act in the `none` role, so such a block is never formally processed. It can carry data that other header blocks need, and it is relayed to the ultimate receiver unless an intermediary removes it through its own processing.
  • Can relay="true" carry a mandatory block past an intermediary that does not understand it?
    No. `relay` has no effect when the block also carries `mustUnderstand="true"`. A mandatory block targeted at the intermediary must be understood and processed, or the intermediary generates an `env:MustUnderstand` fault and the message goes no further.

saying these in an interview costs you the question

  • An intermediary should process the Body as well as its own header blocks.
  • Header blocks an intermediary ignores always pass through unchanged.
  • relay="true" lets an unsupported mandatory block pass an intermediary.
  • A header block without a role attribute is aimed at every node.
  • SOAP 1.1's actor attribute supports relaying just like SOAP 1.2's role.
  • A role URI routes the message to the node at that address.