Glen Mazza's Weblog

« Replacing JAX-WS... | Main | Creating integration... »

https://glenmazza.net/blog/date/20180310 Saturday March 10, 2018

Compressing SOAP messages during transit

In this article I'll be demonstrating two ways to compress Apache CXF SOAP requests and responses to conserve network resources--Fast Infoset (FI) and GZip compression. We'll modify the introductory DoubleIt web service tutorial to test this functionality along with Wireshark to see how the SOAP requests and responses change as a result. The code modifications necessary are quite minimal, just a few lines over one or two source files.

As a reference point, let's use Wireshark to see the SOAP request and response for a standard DoubleIt call without compression:

POST /doubleit/services/doubleit HTTP/1.1
Content-Type: text/xml; charset=UTF-8
Accept: */*
SOAPAction: ""
User-Agent: Apache-CXF/3.2.2
Cache-Control: no-cache
Pragma: no-cache
Host: localhost:8080
Connection: keep-alive
Content-Length: 224

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <ns2:DoubleIt xmlns:ns2="http://www.example.org/schema/DoubleIt">
            <numberToDouble>10</numberToDouble>
        </ns2:DoubleIt>
    </soap:Body>
</soap:Envelope>
        
HTTP/1.1 200
Content-Type: text/xml;charset=UTF-8
Content-Length: 238
Date: Sat, 10 Mar 2018 16:09:14 GMT

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <ns2:DoubleItResponse xmlns:ns2="http://www.example.org/schema/DoubleIt">
            <doubledNumber>20</doubledNumber>
        </ns2:DoubleItResponse>
    </soap:Body>
</soap:Envelope>

First, let's activate fast infoset, both client- and service-side. The negotiation process involves the SOAP client sending an uncompressed Accept: application/fastinfoset HTTP header in the first SOAP request. If the server is configured to support FI, it will return a compressed SOAP response with a content type of application/fastinfoset. Subsequently all SOAP requests and responses between the client and web service provider will then use FI. However, if the server is not configured to use FI the server will return an uncompressed SOAP response with the usual text/xml Content-Type. The following table shows how to activate FI:

Component How to implement Fast Infoset Compression
CXF Web Service Provider

Add the FastInfoset dependency to your project. Then attach the @FastInfoset feature to either the Service Endpoint Interface (SEI) or SEI implementation:

@WebService(targetNamespace = "http://www.example.org/contract/DoubleIt"... 
@org.apache.cxf.annotations.FastInfoset
public class DoubleItPortTypeImpl implements DoubleItPortType {
    ....  
}

Alternative configuration options: Can configure via the org.apache.cxf.feature.FastInfosetFeature either on the ServerFactoryBean or the cxf:bus. As the FastInfosetFeature just installs the two FI interceptors, those interceptors can also be added programmatically to the web service endpoint.

Note FI will be activated only if the client requests it. The feature's force=true attribute (also settable via the FIStaxOutInterceptor constructor) can be used to force FI from the service to the client without negotiation, however there's no guarantee the client will be able to process it.

CXF SOAP Client

Add the FastInfoset dependency to your project. Then add the FastInfoset interceptors to the SOAP client class:

public class WSClient {

    public static void main (String[] args) {
        DoubleItService service = new DoubleItService();
        DoubleItPortType port = service.getDoubleItPort();

        Client client = ClientProxy.getClient(port);
        client.getInInterceptors().add(new org.apache.cxf.interceptor.FIStaxInInterceptor());
        client.getOutInterceptors().add(new org.apache.cxf.interceptor.FIStaxOutInterceptor());
        ...
    }
}

Alternative: add the FastInfosetFeature to the ClientFactoryBean.

Note FI will be activated only if the service is configured to provide it. FI can be forced from the client to the service without negotiation using the methods described above for the web service provider, however there is no guarantee the service provider will be able to process it.

Now, let's check the results over the wire:

POST /doubleit/services/doubleit HTTP/1.1
Content-Type: text/xml; charset=UTF-8
Accept: application/fastinfoset, */*
SOAPAction: ""
User-Agent: Apache-CXF/3.2.2
Cache-Control: no-cache
Pragma: no-cache
Host: localhost:8080
Connection: keep-alive
Content-Length: 224

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <ns2:DoubleIt xmlns:ns2="http://www.example.org/schema/DoubleIt">
            <numberToDouble>10</numberToDouble>
        </ns2:DoubleIt>
    </soap:Body>
</soap:Envelope>
        
HTTP/1.1 200
Content-Type: application/fastinfoset
Transfer-Encoding: chunked
Date: Sat, 10 Mar 2018 17:39:16 GMT

.....8..soap(http://schemas.xmlsoap.org/soap/envelope/.?...Envelope?...Body8..ns2%http://www.example.org/schema/DoubleIt.?...DoubleItResponse<.doubledNumber.20....POST /doubleit/services/doubleit HTTP/1.1
Content-Type: application/fastinfoset
Accept: application/fastinfoset, */*
SOAPAction: ""
User-Agent: Apache-CXF/3.2.2
Cache-Control: no-cache
Pragma: no-cache
Host: localhost:8080
Connection: keep-alive
Content-Length: 155

.....8..soap(http://schemas.xmlsoap.org/soap/envelope/.?...Envelope?...Body8..ns2%http://www.example.org/schema/DoubleIt.?...DoubleIt<
numberToDouble.0....HTTP/1.1 200
Content-Type: application/fastinfoset
Transfer-Encoding: chunked
Date: Sat, 10 Mar 2018 17:39:16 GMT

.....8..soap(http://schemas.xmlsoap.org/soap/envelope/.?...Envelope?...Body8..ns2%http://www.example.org/schema/DoubleIt.?...DoubleItResponse<.doubledNumber.0....

For GZIP, follow the below table for configuration information. The negotiation process involves the SOAP client sending an Accept-Encoding: gzip HTTP header in the first SOAP request. If the server is configured to support GZIP compression, it will return a compressed SOAP response with the Content-Encoding: gzip HTTP Header. Subsequently all SOAP requests and responses between the client and web service provider will then use GZIP. If the server is not configured to use GZIP then SOAP requests and responses will be sent as normal, uncompressed.

Component How to implement GZIP Compression
CXF Web Service Provider

Attach the @org.apache.cxf.annotations.GZIP annotation to DoubleItPortTypeImpl, or either the GZIPFeature or its two corresponding interceptors as described in the previous table for the FI configuration. Note the annotation, feature and GZIPOutInterceptor all provide an optional threshold value below which SOAP messages will not be compressed, if not provided, the default is 1KB.

CXF SOAP Client

Attach the GZIPFeature or the GZIP interceptors (along with the threshold value, if desired) as described in the previous table for FI.

The results with GZIP are shown below. I set the threshold to zero, both client- and service-side, to ensure compression of all messages. (Note following Wireshark's HTTP stream will uncompress by default, causing one to think no compression is occurring. To see the compressed values as below, right-click any packet part of the SOAP calls and choose "Follow->TCPStream" instead.)

POST /doubleit/services/doubleit HTTP/1.1
Content-Type: text/xml; charset=UTF-8
Accept: */*
Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0
SOAPAction: ""
User-Agent: Apache-CXF/3.2.2
Cache-Control: no-cache
Pragma: no-cache
Host: localhost:8080
Connection: keep-alive
Content-Length: 224

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Body>
        <ns2:DoubleIt xmlns:ns2="http://www.example.org/schema/DoubleIt">
            <numberToDouble>10</numberToDouble>
        </ns2:DoubleIt>
    </soap:Body>
</soap:Envelope>HTTP/1.1 200
Content-Encoding: gzip
Vary: Accept-Encoding
Content-Type: text/xml;charset=UTF-8
Content-Length: 158
Date: Sat, 10 Mar 2018 18:03:20 GMT

        ..........m.... .D....X.#A.F.^<..T6..,..........d&.fW1.(.aFG.w.w.e..M?.Q...GoX.........)A.U...>.
        ...M...xC..x....M)    \.....:.k33m..:......l......s.l../&.......
        
POST /doubleit/services/doubleit HTTP/1.1
Content-Type: text/xml; charset=UTF-8
Accept: */*
Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0
Content-Encoding: gzip
SOAPAction: ""
User-Agent: Apache-CXF/3.2.2
Cache-Control: no-cache
Pragma: no-cache
Host: localhost:8080
Connection: keep-alive
Content-Length: 150

..........].1..0.E..z..1F!......`.!..:m..q..
.....l.\..8....)....c...4..;....*W.?...k..kf.../k"......5.>A_])E..B...f=|...Ch............u..?..7Y.......HTTP/1.1 200
Content-Encoding: gzip
Vary: Accept-Encoding
Content-Type: text/xml;charset=UTF-8
Content-Length: 157
Date: Sat, 10 Mar 2018 18:03:20 GMT

..........m.... .D....X.. .../...*.{..t.......x..d..*&..)<.Q...]`Y.}7..%.....E../h.C....tZ5...S..;y..wxNW.H..n...........l3.4..6i/..q.[.kC...b........Q.7....

Comments

Post a Comment: