/** * This Source Code Form is subject to the terms of the Mozilla Public License, * v. 2.0. If a copy of the MPL was not distributed with this file, You can * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. * * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS * graphic logo is a trademark of OpenMRS Inc. */ package org.openmrs.web.filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.openmrs.api.APIException; import org.openmrs.api.context.Context; import org.openmrs.util.OpenmrsConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.filter.OncePerRequestFilter; /** * Filter that compresses output with gzip (assuming that browser supports gzip). Code from <a * href="http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html"> * http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html</a>. © 2003 Jayson Falkner You * may freely use the code both commercially and non-commercially. */ public class GZIPFilter extends OncePerRequestFilter { private static final Logger log = LoggerFactory.getLogger(GZIPFilter.class); private Boolean cachedGZipEnabledFlag = null; private String cachedGZipCompressedRequestForPathAccepted = null; /** * @see org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(javax.servlet.http.HttpServletRequest, * javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) */ @Override public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { try { request = performGZIPRequest(request); } catch (APIException e) { response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); return; } if (isGZIPSupported(request) && isGZIPEnabled()) { log.debug("GZIP supported and enabled, compressing response"); GZIPResponseWrapper wrappedResponse = new GZIPResponseWrapper(response); chain.doFilter(request, wrappedResponse); wrappedResponse.finishResponse(); return; } chain.doFilter(request, response); } /** * Supports GZIP requests * @param req request * @return gzipped request */ public HttpServletRequest performGZIPRequest(HttpServletRequest req) { String contentEncoding = req.getHeader("Content-encoding"); if (contentEncoding != null && contentEncoding.contains("gzip")) { if (!isCompressedRequestForPathAccepted(req.getRequestURI())) { throw new APIException("Unsupported Media Type"); } if (log.isDebugEnabled()) { log.debug("GZIP request supported"); } try { GZIPRequestWrapper wrapperRequest = new GZIPRequestWrapper(req); if (log.isDebugEnabled()) { log.debug("GZIP request wrapped successfully"); } return wrapperRequest; } catch (IOException e) { log.error("Error during wrapping GZIP request " + e); return req; } } else { return req; } } /** * Convenience method to test for GZIP capabilities * * @param req The current user request * @return boolean indicating GZIP support */ private boolean isGZIPSupported(HttpServletRequest req) { String browserEncodings = req.getHeader("accept-encoding"); boolean supported = ((browserEncodings != null) && (browserEncodings.indexOf("gzip") != -1)); String userAgent = req.getHeader("user-agent"); if ((userAgent != null) && userAgent.startsWith("httpunit")) { log.debug("httpunit detected, disabling filter..."); return false; } else { return supported; } } /** * Returns global property gzip.enabled as boolean */ private boolean isGZIPEnabled() { if (cachedGZipEnabledFlag != null) { return cachedGZipEnabledFlag; } try { String gzipEnabled = Context.getAdministrationService().getGlobalProperty( OpenmrsConstants.GLOBAL_PROPERTY_GZIP_ENABLED, ""); boolean isEnabled = Boolean.valueOf(gzipEnabled); cachedGZipEnabledFlag = isEnabled; return cachedGZipEnabledFlag; } catch (Exception e) { log.warn("Unable to get the global property: " + OpenmrsConstants.GLOBAL_PROPERTY_GZIP_ENABLED, e); // not caching the enabled flag here in case it becomes available // before the next request return false; } } /** * Returns true if path matches pattern in gzip.acceptCompressedRequestsForPaths property */ private boolean isCompressedRequestForPathAccepted(String path) { try { if (cachedGZipCompressedRequestForPathAccepted == null) { cachedGZipCompressedRequestForPathAccepted = Context.getAdministrationService().getGlobalProperty( OpenmrsConstants.GLOBAL_PROPERTY_GZIP_ACCEPT_COMPRESSED_REQUESTS_FOR_PATHS, ""); } for (String acceptPath : cachedGZipCompressedRequestForPathAccepted.split(",")) { if (path.matches(acceptPath)) { return true; } } return false; } catch (Exception e) { log.warn("Unable to process the global property: " + OpenmrsConstants.GLOBAL_PROPERTY_GZIP_ACCEPT_COMPRESSED_REQUESTS_FOR_PATHS, e); return false; } } }