You are here: Wiki>Documentation Web>CompressedResponse (14 Aug 2015, DanielNuest)Edit Attach

Support compressed responses in web services


Querying a web service can result to huge responses, e.g. the response of a SOS GetObservation request. To reduce the response size HTTP offers the possibility to compress the response content.

Clients that support compressed responses can indicate this by adding the Accept-Encoding parameter with value gzip to the HTTP header of the request. If the response is compressed, the compression is indicated by the parameter Content-Encoding with value gzip in the HTTP header of the response.

Server

Using servlet container

Servlet containers often have out of the box compression support, please check the documentation.

Using libraries or filters

For a general introduction on using a Filter for compression see http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html.

Leveraging HTTP server

If your web service is behind an HTTP server such as nginx or Apache, it might be a good solution to leave the compression to the webserver instead of putting the load on the application or Servlet container.

Standalone implementation in a Java web application

Here is an example for the response content compression support. It checks if the indicator for response compression is set in the HTTP header. If the client supports compression and the response size is greater than 1000000 the service compresses the response and adds the compression indicator to the HTTP header of the response.

Example of a HttpServlet class with doPost method that contains the response compression funtionality

public class SosService extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {

        // process the request
        ServiceResponse serviceResponse = processTheRequest(request);

        OutputStream out = null;
        GZIPOutputStream gzip = null;
        try {
            String contentType = serviceResponse.getContentType();
            response.setContentType(contentType);
            out = response.getOutputStream();
            int contentLength = serviceResponse.getContentLength();

            // compressed response
            // Inversed boolean order check. greaterThan is cheaper
            if (contentLength > 1000000 && checkForClientGZipSupport(request)) {
                response.addHeader("Content-Encoding", "gzip");
                gzip = new GZIPOutputStream(out);
                gzip.write(serviceResponse.getBytes());
                gzip.flush();
                gzip.finish();
            } 
            // uncompressed response
            else {
                response.setContentLength(contentLength);
                out.write(serviceResponse.getBytes());
                out.flush();
            }
        } catch (IOException ioe) {
            String exceptionText = "Error while writing response to ServletResponse!";
            LOGGER.error(exceptionText, ioe);
            throw new ServletException(exceptionText, ioe);
        } finally {
            try {
                if (gzip != null) {
                    gzip.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException ioe) {
                LOGGER.debug("Error while closing output stream(s)!", ioe);
            }
        }
    }

    // method to check if the HTTP header contains 'Accept-Encoding' parameter with value 'gzip'
    private boolean checkForClientGZipSupport(HttpServletRequest httpServletRequest) {
        String header = httpServletRequest.getHeader("Accept-Encoding");
        if (header != null && !header.isEmpty()) {
            String[] split = header.split(",");
            for (String string : split) {
                if (string.equalsIgnoreCase("gzip")) {
                    return true;
                }
            }
        }
        return false;
    }
}
}

Client

Example Client Implementation (Apache HttpClient 4.x)

import java.io.IOException;
import java.util.Properties;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.protocol.HttpContext;

public class HttpClientWithGZIP {

    private static final String GZIP_ENCODING_VALUE = "gzip";
    private static final String ENCODING_HEADER_NAME = "Accept-Encoding";
    private static final String CONNECTION_TIMEOUT_KEY = "CONNECTION_TIMEOUT";
    private static final String USE_GZIP_KEY = "USE_GZIP";

    private DefaultHttpClient httpClient;
    private Properties configuration;

    public HttpClientWithGZIP() {
        //dummy configuration
        this.configuration = new Properties();
        this.configuration.setProperty(USE_GZIP_KEY, "true");
        this.configuration.setProperty(CONNECTION_TIMEOUT_KEY, "5000");

        this.httpClient = new DefaultHttpClient();
        setup();
    }

    public HttpResponse executeRequest(HttpUriRequest request) throws ClientProtocolException, IOException {
        return this.httpClient.execute(request);
    }

    private void setup() {
        if (Boolean.parseBoolean(configuration.getProperty(USE_GZIP_KEY))) {
            addGzipInterceptors(this.httpClient);
        }

        if (this.httpClient.getParams() == null) {
            this.httpClient.setParams(new BasicHttpParams());
        }

        int timeout = Integer.parseInt(configuration.getProperty(CONNECTION_TIMEOUT_KEY));
        this.httpClient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,
                timeout);
        this.httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT,
                timeout);
    }

    private void addGzipInterceptors(DefaultHttpClient httpclient) {
        httpclient.addRequestInterceptor(new HttpRequestInterceptor() {

            @Override
            public void process(HttpRequest request, HttpContext context)
            throws HttpException, IOException {
                if (!request.containsHeader(ENCODING_HEADER_NAME)) {
                    request.addHeader(ENCODING_HEADER_NAME, GZIP_ENCODING_VALUE);
                }                
            }
        });

        httpclient.addResponseInterceptor(new HttpResponseInterceptor() {
            @Override
            public void process(HttpResponse response, HttpContext context)
            throws HttpException, IOException {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    Header header = entity.getContentEncoding();
                    if (header != null) {
                        HeaderElement[] codecs = header.getElements();
                        for (int i = 0; i < codecs.length; i++) {
                            if (codecs[i].getName().equalsIgnoreCase(GZIP_ENCODING_VALUE)) {
                                response.setEntity(new GzipDecompressingEntity(response.getEntity()));
                                return;
                            }
                        }
                    }
                }                
            }
        });
    }

}

Using HttpClient from the OXFramework

The OXF offers easy HttpClient configuration so one is able to decorate configuration as needed. See how it works:

package org.n52.ows.request;

import org.apache.http.HttpEntity;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.junit.Test;
import org.n52.oxf.util.web.GzipEnabledHttpClient;
import org.n52.oxf.util.web.HttpClient;
import org.n52.oxf.util.web.ProxyAwareHttpClient;
import org.n52.oxf.util.web.SimpleHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpClientDecoratorTest {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientDecoratorTest.class);
    
    @Test
    public void testGetCapabilities() throws Exception {
        HttpClient proxyAwareClient = new ProxyAwareHttpClient(new SimpleHttpClient());
        HttpClient httpclient = new GzipEnabledHttpClient(proxyAwareClient);

        String baseUri = "http://sensorweb.demo.52north.org/PegelOnlineSOSv2.1/sos";
        GetCapabilitiesParameters parameters = new GetCapabilitiesParameters("SOS", "1.0.0");
        parameters.addAcceptedVersion("2.0.0");
        HttpEntity responseEntity = httpclient.executeGet(baseUri, parameters);
        XmlObject capabilities = XmlObject.Factory.parse(responseEntity.getContent());
        
        LOGGER.debug(capabilities.xmlText(new XmlOptions().setSavePrettyPrint()));
    }
}

Topic revision: r8 - 14 Aug 2015, DanielNuest
Legal Notice | Privacy Statement


This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Wiki? Send feedback