Loading…
Loading…
Handles SOAP-to-SOAP passthrough requests, preserving byte-for-byte fidelity of the SOAP body while managing HTTP headers for security and tracing.
The GatewaySoapHandler is a data path handler that provides a SOAP-to-SOAP passthrough brokerage. Its primary function is to forward an inbound SOAP request to a backend SOAP service without inspecting, parsing, or modifying the request body. This preserves the exact byte-for-byte fidelity of the SOAP message, including XML formatting, namespace prefixes, CDATA sections, and MTOM attachments.
This handler is designed for scenarios where an API publisher needs to expose a backend SOAP service through the gateway, but does not require any of the gateway's message transformation, validation, or fault-mapping capabilities. It deliberately bypasses components responsible for message conversion, such as SoapMessageMapper, PayloadTransformService, and schema validators.
The handler operates within the runtime service and is invoked by the main request controller. The controller is responsible for preliminary steps like route resolution, backend target selection, credential resolution, and mTLS context setup. The GatewaySoapHandler then takes over to perform the following key functions:
SoapClientService, which manages circuit breaking, retries, and egress controls.A backend's SOAP Fault is considered a legitimate response and is passed through to the client, not treated as a gateway error. The handler only generates its own SOAP Faults for issues it is directly responsible for, such as transport failures or configuration errors.
The GatewaySoapHandler processes requests in a distinct sequence, ensuring security and message fidelity. The process is initiated after the gateway's controller has already identified the route and selected a backend target.
flowchart TD
subgraph Gateway
A[Content-Type Gate] --> B{Header Manipulation};
B --> C[Backend Call];
C --> D[Response Header Filtering];
end
API_Consumer[API Consumer] --> A;
C --> SOAP_Backend[SOAP Backend];
SOAP_Backend --> C;
D --> API_Consumer;
style A text-align:center
style B text-align:center
style C text-align:center
style D text-align:centerContent-Type Validation: The handler first calls validateContentType to inspect the Content-Type header of the inbound HttpServletRequest. It permits text/xml (SOAP 1.1), application/soap+xml (SOAP 1.2), application/xml, and multipart/related (MTOM). If the content type is not one of these, the handler immediately generates an HTTP 415 Unsupported Media Type fault and stops processing.
Header Manipulation: The buildForwardHeaders method constructs the set of headers to be forwarded to the backend. This is a critical security and operational step:
X-Forwarded-*, Forwarded, and X-Real-IP style headers are dropped. A new X-Forwarded-For header is created containing only the trusted client address identified by the gateway. This prevents header spoofing and ensures the backend receives accurate, non-repudiable client information.X-Correlation-Id header, a new one is generated and added. If one is present, it is preserved.Backend Invocation: The handle method orchestrates the call to the backend.
dispatch_mode=relay, as the relay mechanism does not support the byte-fidelity required for passthrough.extraHeaders) from the backend profile, along with the Authorization header containing the resolved backend credentials.SoapClientService.callPassthrough, passing the raw request body byte[], headers, and connection parameters.Response Handling:
SoapClientService reports a transport-level failure (e.g., timeout, circuit breaker open), the handler generates a corresponding SOAP Fault (typically with an HTTP 502 or 503 status) and returns it to the client.filterResponseHeaders and relays the backend's HTTP status, filtered headers, and raw response body to the original client.The GatewaySoapHandler exposes several methods used by the runtime controller to manage the passthrough flow.
validateContentType(HttpServletRequest request)Validates that the inbound request's Content-Type is suitable for SOAP passthrough.
request: The inbound HttpServletRequest.null if the content type is valid. If invalid, it returns a Result object containing a generated SOAP Fault with HTTP status 415. Accepted content types include strings containing text/xml, application/soap+xml, application/xml, or multipart/related.detectSoapVersion(HttpServletRequest request)Detects the SOAP version from the Content-Type header to correctly format any gateway-generated faults. It does not inspect the message body.
request: The inbound HttpServletRequest."1.2" if the content type contains application/soap+xml; otherwise, returns "1.1".buildForwardHeaders(HttpServletRequest request, UUID correlationId, String clientAddress)Constructs the map of headers to be forwarded to the backend service.
request: The inbound HttpServletRequest to source headers from.correlationId: The correlation ID for the request. It will be added as X-Correlation-Id if not already present in the request headers.clientAddress: The trusted, authorized client IP address. This value is used to construct a new X-Forwarded-For header.Map<String, String> of headers to be sent to the backend.handle(...)Executes the main passthrough logic, calling the backend and returning its response.
| Name | Type | Description |
|---|---|---|
route | RouteMetadata | Metadata for the resolved route. |
backendTarget | BackendTargetMetadata | Metadata for the selected backend target. |
body | byte[] | The raw inbound request body. |
contentType | String | The inbound request's Content-Type header. |
forwardHeaders | Map<String, String> | The headers to forward, as built by buildForwardHeaders. |
authorizationHeader | String | The Authorization header value for the backend, if any. |
sslContext | SSLContext | The SSLContext for mTLS, if required by the backend. |
correlationId | UUID | The unique ID for tracing the request. |
soapVersion | String | The SOAP version ("1.1" or "1.2") for generating faults. |
Result object containing the outcome of the call.Result RecordThe handle method returns a Result object, which encapsulates the complete response to be sent to the client.
| Field | Type | Description |
|---|---|---|
httpStatus | int | The HTTP status code to return to the client. |
contentType | String | The Content-Type of the response body. |
body | byte[] | The raw response body bytes. |
responseHeaders | Map<String, String> | Headers to include in the response. |
resultCode | String | A code indicating the outcome (e.g., "success", "error", "fault"). |
errorCode | String | A specific error code for gateway-level failures. |
errorMessage | String | A descriptive error message for gateway-level failures. |
backendLatencyMs | Integer | The latency of the call to the backend, in milliseconds. |
faultClass | String | Classification of the outcome (e.g., "success", "transport_failure", "backend_fault"). |
The handler meticulously manages HTTP headers to ensure security and proper protocol behavior across multiple network hops.
The following headers are always removed from the inbound request and are not forwarded to the backend service. This list is a combination of standard hop-by-hop headers, headers managed by the underlying HTTP client, headers that could leak client credentials, and headers used for client address spoofing.
| Header Name | Reason for Dropping |
|---|---|
connection | Hop-by-hop (RFC 9110 §7.6.1) |
content-length | Calculated by the outbound HTTP client |
content-type | Set explicitly from the inbound request |
expect | Hop-by-hop |
host | Derived by the outbound HTTP client from the target URL |
keep-alive | Hop-by-hop (RFC 9110 §7.6.1) |
proxy-authenticate | Hop-by-hop (RFC 9110 §7.6.1) |
te | Hop-by-hop (RFC 9110 §7.6.1) |
trailer | Hop-by-hop (RFC 9110 §7.6.1) |
transfer-encoding | Hop-by-hop (RFC 9110 §7.6.1) |
upgrade | Hop-by-hop (RFC 9110 §7.6.1) |
forwarded | Rebuilt from a trusted source to prevent spoofing |
x-forwarded-for | Rebuilt from a trusted source to prevent spoofing |
x-forwarded-host | Dropped; part of the forwarded-for family |
x-forwarded-port | Dropped; part of the forwarded-for family |
x-forwarded-proto | Dropped; part of the forwarded-for family |
x-real-ip | Dropped; part of the forwarded-for family |
x-client-ip | Dropped; part of the forwarded-for family |
Additionally, all headers listed in the shared SensitiveHeaders.CREDENTIAL_HEADERS set are dropped to prevent leaking API consumer credentials to the backend. This includes Authorization, Proxy-Authorization, Cookie, X-Api-Key, and X-Auth-Token. The Authorization header is later replaced with the appropriate credential for the backend service.
The following headers are removed from the backend's response before it is relayed to the API consumer. These are primarily hop-by-hop headers that are not relevant to the client's connection with the gateway.
connectioncontent-lengthcontent-typekeep-aliveproxy-authenticateproxy-authorizationtetrailertransfer-encodingupgradeX-Correlation-Id: Injected into the request to the backend if not already present.X-Forwarded-For: Overwritten with a single, trusted client address. Any inbound X-Forwarded-For header is ignored and discarded.Authorization: Added before the backend call, containing the credentials resolved for the backend target.extraHeaders configuration of the backend profile are added. These are added after forwarded headers, so they will override any forwarded header with the same name.The GatewaySoapHandler generates its own SOAP fault messages for errors that occur within the gateway, such as a transport failure. It does not generate faults for errors returned by the backend. The following shows how a SOAP 1.1 fault is constructed.
// Example of internal fault generation
String faultXml = GatewaySoapHandler.buildFaultEnvelope(
"1.1", // soapVersion
"soap:Server", // faultCode
"Backend call failed", // faultString
"S2R-RUN-0503" // errorCode
);
This produces the following XML payload, which would be returned to the client with an appropriate HTTP status code (e.g., 502 or 503).
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Backend call failed</faultstring>
<detail>
<errorCode>S2R-RUN-0503</errorCode>
</detail>
</soap:Fault>
</soap:Body>
</soap:Envelope>
For a SOAP 1.2 fault, the structure is different:
// Example for SOAP 1.2
String faultXml = GatewaySoapHandler.buildFaultEnvelope(
"1.2",
"soap:Client",
"Unsupported Content-Type",
"S2R-RUN-0408"
);
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Body>
<env:Fault>
<env:Code>
<env:Value>env:Sender</env:Value>
</env:Code>
<env:Reason>
<env:Text xml:lang="en">Unsupported Content-Type</env:Text>
</env:Reason>
<env:Detail>
<errorCode>S2R-RUN-0408</errorCode>
</env:Detail>
</env:Fault>
</env:Body>
</env:Envelope>
When the gateway encounters an issue it cannot resolve by passthrough, it generates a SOAP Fault with a specific error code. These errors indicate a problem with the request's transport or the gateway's configuration, not a fault generated by the backend service.
IMPORTANT
A SOAP Fault returned by the backend service (e.g., an HTTP 500 response containing a <soap:Fault> body) is considered a successful passthrough. The GatewaySoapHandler will relay this response to the client verbatim. The errors below are generated exclusively by the gateway itself.
| HTTP Status | Error Code | Failure Type | Trigger |
|---|---|---|---|
| 415 | S2R-RUN-0408 | client_error | The inbound request's Content-Type header is not one of the accepted SOAP or MTOM types. |
| 502 | S2R-RUN-0424 | transport_failure | A connection or read timeout occurred when communicating with the backend. |
| 502 | S2R-RUN-0413 | transport_failure | The backend response payload was too large. |
| 502 | S2R-RUN-0502 | transport_failure | The call to the backend was blocked by an egress policy. |
| 502 | S2R-RUN-0503 | transport_failure | A generic network or transport error occurred. |
| 503 | S2R-RUN-0503 | client_error | The backend is configured with dispatch_mode=relay, which is incompatible with SOAP passthrough. |
| 503 | S2R-RUN-0503 | client_error | The backend target profile has no endpoint URL configured or is inactive. |
| 503 | S2R-RUN-0529 | transport_failure | The circuit breaker for the backend service is open. |
ConsumeSoapHandler.SoapClientService provides the underlying client for all outbound SOAP calls, managing connections, timeouts, and retries.