Search code examples
javaspring-bootgzipspring-wshttp-compression

Spring WS request GZIP compression


I'm building a Spring Boot application that behaves as a client to another web service. Using WebServiceTemplate to send SOAP messages and I have a case where a request is big enough that the target service requires it to be gzip compressed. As I understand handling compressed responses is done by default on the client's side, but not requests as that is not the standard. I'm using Java 8, Spring Boot 2.1 and Spring WS 3.0.3

Setting mime headers does not do the trick for me as that does not get the payload compressed, neither does setting server.compression.enabled (along with the various mime-types) in the application properties and I know it's not a faulty service on the other end because it does work with SoapUI.

So my question is - how can I enable gzip compression for outgoing requests?


Solution

  • A solution that worked for us was making a Http interceptor that does the compression and giving the WebServiceTemplate a new HttpComponentMessageSender with that interceptor. Here's what the interceptor looks like:

    import org.apache.http.HttpEntity;
    import org.apache.http.HttpEntityEnclosingRequest;
    import org.apache.http.HttpRequest;
    import org.apache.http.HttpRequestInterceptor;
    import org.apache.http.client.entity.GzipCompressingEntity;
    import org.apache.http.protocol.HTTP;
    import org.apache.http.protocol.HttpContext;
    
    import java.net.URI;
    import java.net.URISyntaxException;
    
    public class GzipHttpRequestInterceptor implements HttpRequestInterceptor {
    
        private final String targetHost;
    
        public GzipHttpRequestInterceptor(String targetUrl) throws URISyntaxException {
            this.targetHost = getDomainName(targetUrl);
        }
    
        private String getDomainName(String url) throws URISyntaxException {
            URI uri = new URI(url);
            String domain = uri.getHost() + ":" + uri.getPort();
            return domain.startsWith("www.") ? domain.substring(4) : domain;
        }
    
        @Override
        public void process(HttpRequest httpRequest, HttpContext httpContext) {
            final HttpEntityEnclosingRequest entityRequest = (HttpEntityEnclosingRequest) httpRequest;
            final HttpEntity entity = entityRequest.getEntity();
            if (entity != null) {
                final GzipCompressingEntity zippedEntity = new GzipCompressingEntity(entity);
                entityRequest.setEntity(zippedEntity);
    
                httpRequest.removeHeaders(HTTP.CONTENT_ENCODING);
                httpRequest.addHeader(zippedEntity.getContentEncoding());
    
                httpRequest.removeHeaders(HTTP.CONTENT_LEN);
                httpRequest.removeHeaders("Accept");
    
                httpRequest.removeHeaders(HTTP.TRANSFER_ENCODING);
                httpRequest.addHeader(HTTP.TRANSFER_ENCODING, HTTP.CHUNK_CODING);
                httpRequest.addHeader(HTTP.TARGET_HOST, targetHost);
            }
        }
    }
    

    In our web configuration we assemble the org.apache.http.protocol.HttpProcessor and org.springframework.ws.transport.http.HttpComponentsMessageSender beans:

        @Bean
        public HttpProcessor httpRequestCompressionProcessor(String url) throws URISyntaxException {
            return HttpProcessorBuilder.create()
                    .add(new GzipHttpRequestInterceptor(url))
                    .build();
        }
    
        @Bean
        public HttpComponentsMessageSender messageGzipSender(String url) throws URISyntaxException {
            return new HttpComponentsMessageSender(HttpClients.custom()
                    .addInterceptorFirst(new HttpComponentsMessageSender.RemoveSoapHeadersInterceptor())
                    .setHttpProcessor(httpRequestCompressionProcessor(url))
                    .build());
        }
    

    And then assign that message sender to our WebServiceTemplate using setMessageSender(messageGzipSender(url)

    I guess I wouldn't mind comments on this code, in case it can be improved and still eager to hear if there is a simpler way.