kulas
kulas

Reputation: 57

Adding dynamic HTTP Headers to WS Outbound Gateway

I would like to enrich my HTTP/SOAP request (send with WS Outbound Gateway) with some custom HTTP Headers depending on what actually Message contains, i.e.

I considered the following solutions:

  1. Implementing custom ClientInterceptor - but there is no access to Message.header at this point
  2. Implementing custom SoapHeaderMapper - there is access to Message.header but I do not like this idea as it is rather meant to operate on SOAP envelope not on connection/request level.
  3. Implementing custom WebServiceMessageCallback - no access to Message at this point.

Also, in context of Authentication, all above solutions rely on adding necessary HTTP Authentication headers on our own, while I would like to do it in more correct way (at least in my opinion) and properly configure HttpClient.

So at this point I finished with custom HttpComponentsMessageSender that sets up HttpClientContext per each request. The problem is that again there is no access to Mesage.headers so I finished with some combination of ServiceActivator with SPEL and ThreadLocal as presented below.

Generally it works but... is there any other more correct path to go?

<int:service-activator expression="@basicAuthenticationMessageSender.setBasicAuth(#root, headers.username, headers.pass)" />


public class BasicAuthenticationMessageSender extends HttpComponentsMessageSender {

private static ThreadLocal<HttpClientContext> httpClientContextLocal = new ThreadLocal<>();

@Override
protected HttpContext createContext(URI uri) {
    HttpClientContext httpClientContext = httpClientContextLocal.get();

    // This part makes authentication preemptive:
    HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
    AuthCache authCache = new BasicAuthCache();
    authCache.put(targetHost, new BasicScheme());
    httpClientContext.setAuthCache(authCache);

    return httpClientContext;
}

public GenericMessage setBasicAuth(GenericMessage message, String username, String password) throws Exception {
    final HttpClientContext httpClientContext = HttpClientContext.create();
    CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
    credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
    httpClientContext.setCredentialsProvider(credentialsProvider);

    httpClientContextLocal.set(httpClientContext);
    return message;
}
}

Spring Integration 4.3.11

Upvotes: 1

Views: 815

Answers (1)

Artem Bilan
Artem Bilan

Reputation: 121552

That's right. Since Spring Integration WS support is fully based on the Spring WS project, there is nothing more we can in SI what the SWS provides for us.

Anyway you can inject WebServiceMessageCallback into the AbstractWebServiceOutboundGateway and there get access to the ThreadLocal variable for the Message populated there before calling this WS Gateway.

There in the WebServiceMessageCallback you should get access to the:

TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();

connection.getConnection().addRequestProperty("hea derParameter", "headerValue");

The same TransportContext you can access from the custom DefaultSoapHeaderMapper though and there already even without any ThreadLocal:

@Override
protected void populateUserDefinedHeader(String headerName, Object headerValue, SoapMessage target) {
    super.populateUserDefinedHeader(headerName, headerValue, target);

    TransportContext context = TransportContextHolder.getTransportContext();
    HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();

    connection.getConnection().addRequestProperty(headerName, headerValue);
}

Upvotes: 1

Related Questions