/* * Copyright 2012 The Solmix Project * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.gnu.org/licenses/ * or see the FSF site: http://www.fsf.org. */ package org.solmix.web; import java.io.IOException; import java.io.Writer; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.net.URLCodec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.solmix.api.context.WebContext; import org.solmix.api.exception.SlxException; import org.solmix.api.types.Texception; import org.solmix.api.types.Tmodule; import org.solmix.commons.util.DataUtils; /** * @author solmix.f@gmail.com * @since 0.0.1 * @version 110035 2010-12-21 solmix-utils */ public class ServletTools { private static Logger log = LoggerFactory.getLogger(ServletTools.class); /** * <code><HTML><BODY></code> */ private static String htmlStart = "<HTML><BODY>"; /** * </BODY></HTML> */ private static String htmlEnd = "</BODY></HTML>"; /** * Put the QueryString into Map. just like "name = value"into a map(name,value) * * @param queryString * @return * @throws Exception */ public static Map<String, String> parseQueryString(String queryString) throws SlxException { Map<String, String> params = new HashMap<String, String>(); if (queryString != null) { if (queryString.startsWith("?")) queryString = queryString.substring(1); URLCodec urlCodec = new URLCodec("UTF-8"); List<String> paramPairs = DataUtils.simpleSplit(queryString, "&"); for (String str : paramPairs) { List<String> keyVale = DataUtils.simpleSplit(str, "="); try { params.put(urlCodec.decode(keyVale.get(0)), urlCodec.decode(keyVale.get(1))); } catch (DecoderException e) { throw new SlxException(Tmodule.SERVLET, Texception.SERVLET_QUERYPARM_DECODE, e); } } } return params; } /** * According to the request path for MIME type * * @param context * @return * @throws Exception */ public static String mimeTypeForContext(WebContext context) throws Exception { return DataUtils.mimeTypeForFileName(context.getRequestPath()); } public static Map<String, Object> paramsToMap(ServletRequest request) { Map<String, Object> map = new HashMap<String, Object>(); for (Enumeration paramNames = request.getParameterNames(); paramNames.hasMoreElements();) { String name = (String) paramNames.nextElement(); String values[] = request.getParameterValues(name); if (values.length == 0) map.put(name, ""); else if (values.length == 1) map.put(name, values[0]); else map.put(name, Arrays.asList(values)); } return map; } public static String browserShortName(HttpServletRequest request) { if (browserIsMSIE(request)) return "MSIE"; if (browserIsNav4(request)) return "Nav4"; if (browserIsMoz(request)) return "Moz (Gecko)"; if (browserIsSafari(request)) return "Safari"; else return "Unsupported"; } public static String getBrowserSummary(WebContext context) { return (new StringBuilder()).append(browserShortName(context.getRequest())).append( browserClaimsGZSupport(context.getRequest()) ? " with" : " WITHOUT").append(" Accept-Encoding header").append( !browserIsMSIE(context.getRequest()) || !IEReadyForCompressedJS(context) ? "" : ", ready for compressed JS").toString(); } public static boolean IEReadyForCompressedJS(WebContext context) { if (!browserIsMSIE(context.getRequest())) return true; if (!jscriptContentEncodingCompressionEnabled()) return false; if (!browserClaimsGZSupport(context.getRequest())) return false; if (IEIsOlderThanIE6SP2(context.getRequest())) return compressionReadyCookieIsSet(context); else return true; } public static boolean compressionReadyCookieIsSet(WebContext context) { String cookieValue = getCookieValue("isc_cState", context.getRequest()); return cookieValue != null && !cookieValue.equals("__null__"); } public static Cookie getCookie(String targetCookieName, HttpServletRequest request) { Cookie cookies[] = request.getCookies(); if (cookies != null) { for (int ii = 0; ii < cookies.length; ii++) { String currentCookieName = cookies[ii].getName(); if (cookies[ii].getName().equals(targetCookieName)) if (cookies[ii].getValue().equals("__null__")) return null; else return cookies[ii]; } } return null; } public static String getCookieValue(String targetCookieName, HttpServletRequest request) { Cookie theCookie = getCookie(targetCookieName, request); if (theCookie == null) return null; else return theCookie.getValue(); } public static boolean IEIsOlderThanIE6SP2(HttpServletRequest request) { String UA = request.getHeader("User-Agent"); if (UA == null) return true; Float browserVersion; Float platformVersion; Float sixZero; Float fiveOne; try { browserVersion = IEBrowserVersion(UA); platformVersion = IEPlatformVersion(UA); sixZero = new Float("6.0"); fiveOne = new Float("5.1"); if (browserVersion.compareTo(sixZero) == -1 || platformVersion.compareTo(fiveOne) == -1) return true; } catch (Exception e) { log.warn((new StringBuilder()).append("Couldn't parser browser or platform version in UA: ").append(UA).toString()); return true; } if (browserVersion.compareTo(sixZero) == 0 && platformVersion.compareTo(fiveOne) == 0) return UA.indexOf("SV1") == -1; return false; } public static Float IEPlatformVersion(String UA) { int start = UA.indexOf("Windows NT"); if (start == -1) return new Float(3F); UA = UA.substring(start + 11); int end = UA.indexOf(";"); if (end == -1) end = UA.indexOf(")"); UA = UA.substring(0, end); String platformVersion = UA; return new Float(platformVersion); } public static Float IEBrowserVersion(String UA) { int start = UA.indexOf("MSIE"); UA = UA.substring(start + 5); int end = UA.indexOf(";"); UA = UA.substring(0, end); for (String browserVersion = UA; browserVersion.length() > 0;) try { return new Float(browserVersion); } catch (NumberFormatException nfe) { browserVersion = browserVersion.substring(0, browserVersion.length() - 1); } return new Float(4F); } public static boolean jscriptContentEncodingCompressionEnabled() { return compressionEnabled(); // && config.getBoolean("servlet.compress.jscriptContentEncoding", false); } public static boolean compressionEnabled() { // TODO return true; // return OSGIHelper.getCM().getSubtree("servlet").getBoolean("compress", false); } public static boolean browserClaimsGZSupport(HttpServletRequest request) { return false; // String viaHeader = request.getHeader("via"); // if (viaHeader != null) { // log.debug((new StringBuilder()).append("Client sent Via header: ").append(viaHeader).toString()); // int indexHttp10 = viaHeader.indexOf("1.0"); // TODO // if (indexHttp10 != -1 && // OSGIHelper.getCM().getSubtree("servlet").getBoolean("disableCompressionIfProxyChainContainsHTTP10", true)) { // String encodingHeader = request.getHeader("Accept-Encoding"); // log.info((new StringBuilder()).append( // "Disallowing compression for request whose proxy chain contains an HTTP 1.0 proxy. Via header: ").append(viaHeader).append( // " Accept-Encoding header: ").append(encodingHeader != null ? encodingHeader : "not set").toString()); // return false; } // } // TODO // if (OSGIHelper.getCM().getSubtree("servlet").getBoolean("useCompressionThroughProxies", false) // && (browserIsMSIE(request) || browserIsNav4(request) || browserIsMoz(request))) { // return true; // } else { // String encodingHeader = request.getHeader("Accept-Encoding"); // return encodingHeader != null && encodingHeader.indexOf("gzip") != -1; // } // } public static boolean browserIsNav4(HttpServletRequest request) { String userAgent = request.getHeader("User-Agent"); return userAgent != null && DataUtils.contains(userAgent, "Mozilla/4") && !DataUtils.contains(userAgent, "MSIE") && !DataUtils.contains(userAgent, "Opera"); } public static boolean browserIsMSIE(HttpServletRequest request) { String userAgent = request.getHeader("User-Agent"); return userAgent != null && DataUtils.contains(userAgent, "MSIE"); } public static boolean browserIsMoz(HttpServletRequest request) { String userAgent = request.getHeader("User-Agent"); return userAgent != null && DataUtils.contains(userAgent, "Gecko/"); } public static boolean browserIsSafari(HttpServletRequest request) { String userAgent = request.getHeader("User-Agent"); return userAgent != null && DataUtils.contains(userAgent, "Safari"); } /** * send HTML to client * * @param out always set by {@link javax.servlet.http.HttpServletResponse#getWriter getWriter()} * @throws SlxException * @throws Exception */ public static void sendHTMLStart(Writer out) throws SlxException { synchronized (out) { try { out.write(htmlStart); out.flush(); } catch (IOException e) { throw new SlxException(Tmodule.SERVLET, Texception.IO_EXCEPTION, e); } } } public static void sendHTMLEnd(Writer out) throws SlxException { synchronized (out) { try { out.write(htmlEnd); out.flush(); } catch (IOException e) { throw new SlxException(Tmodule.SERVLET, Texception.IO_EXCEPTION, e); } } } public static String getContainerPath(ServletContext servletContext) throws SlxException { URL url = null; try { url = servletContext.getResource("/"); } catch (MalformedURLException e) { throw new SlxException(Tmodule.SERVLET, Texception.IO_EXCEPTION, e); } if (url != null) return url.getPath(); else return null; } /** * @param response * @param errorMessage * @param t */ public static void handleServletError(HttpServletResponse response, String errorMessage, Throwable t) { // TODO Auto-generated method stub } public static boolean compressionEnabledForMimeType(String mimeType) { return compressionEnabledForMimeType(mimeType, null); } /** * @param mimeType * @param compressableMimeTypes * @return */ public static boolean compressionEnabledForMimeType(String mimeType, List<String> compressableMimeTypes) { if (mimeType == null) return false; String mimeTypeLC = mimeType.toLowerCase(); if (compressableMimeTypes != null) { for (String compressableMimeType : compressableMimeTypes) { if (mimeTypeLC.indexOf(compressableMimeType.toLowerCase()) != -1) return true; } return false; } if (alwaysCompressMimeType(mimeType)) return true; // TODO // compressableMimeTypes = (List<String>) // OSGIHelper.getCM().getSubtree("compression").getList("compressableMimeTypes"); // if (compressableMimeTypes == null) { // log.debug("compression.compressableMimeTypes is null, assuming compression enabled for all mime types."); // return true; // } for (String compressableMimeType : compressableMimeTypes) { if (mimeTypeLC.indexOf(compressableMimeType.toLowerCase()) != -1) return true; } return false; } public static boolean alwaysCompressMimeType(String mimeType) { if (mimeType == null) return false; String mimeTypeLC = mimeType.toLowerCase(); // TODO List alwaysCompressMimeTypes = new ArrayList(); // List alwaysCompressMimeTypes = // OSGIHelper.getCM().getSubtree("compression").getList("alwaysCompressMimeTypes"); for (Iterator i = alwaysCompressMimeTypes.iterator(); i.hasNext();) { String compressableMimeType = (String) i.next(); if (mimeTypeLC.indexOf(compressableMimeType.toLowerCase()) != -1) return true; } return false; } public static Float IEBrowserVersion(HttpServletRequest request) { return IEBrowserVersion(request.getHeader("User-Agent")); } /** * @param context * @param mimeType * @return */ public static boolean compressionWorksForMimeType(WebContext context, String mimeType) { if (mimeType == null) return false; if (DataUtils.contains(mimeType, "zip")) return false; if (DataUtils.contains(mimeType, "css") && (browserIsNav4(context.getRequest()) || browserIsMSIE(context.getRequest()) && IEBrowserVersion(context.getRequest()).floatValue() <= 5.5D)) return false; if (!DataUtils.contains(mimeType, "javascript")) return true; if (browserIsNav4(context.getRequest())) return false; return IEReadyForCompressedJS(context); } /** * @param context * @return */ public static boolean compressionWorksForContext(WebContext context) { return true; } /** * @param context * @return */ public static boolean contextIsIncluded(WebContext context) { // TODO Auto-generated method stub return false; } public static boolean IENeedsToSeeACompressedPage(WebContext context) { return browserIsMSIE(context.getRequest()) && browserClaimsGZSupport(context.getRequest()) && jscriptContentEncodingCompressionEnabled() && !IEReadyForCompressedJS(context); } /** * @param context */ public static void setCompressionReadyCookie(WebContext context) { setCookie(context, "isc_cState", "ready", "/"); } public static Cookie setCookie(WebContext context, String name, String value, String path) { return setCookie(context, name, value, path, null, -1); } public static Cookie setCookie(WebContext context, String name, String value, String path, String domain, int maxAge) { log.debug((new StringBuilder()).append("setting cookie '").append(name).append("' to: '").append(value).append("'").toString()); Cookie cookie = new Cookie(name, value); if (path == null) path = "/"; cookie.setPath(path); if (maxAge != -1) cookie.setMaxAge(maxAge); if (domain != null) { log.debug((new StringBuilder()).append("setting domain to: ").append(domain).append(" fo cookie: ").append(name).toString()); cookie.setDomain(domain); } context.getResponse().addCookie(cookie); return cookie; } public static String encodeParameter(String name, String value) { Pattern tspecials = Pattern.compile("[<()@,;:/?={} >\"\\[\\]\\t\\\\]"); Matcher matcher = tspecials.matcher(value); if (value.length() <= 78) if (!matcher.find()) return (new StringBuilder()).append(name).append("=").append(value).toString(); else return (new StringBuilder()).append(name).append("=").append("\"").append(value).append("\"").toString(); int counter = 0; String returnVal = ""; for (; value.length() > 78; value = value.substring(78)) { String work = value.substring(0, 78); matcher.reset(work); if (matcher.find()) work = (new StringBuilder()).append("\"").append(work).append("\"").toString(); if (counter > 0) returnVal = (new StringBuilder()).append(returnVal).append("; ").toString(); returnVal = (new StringBuilder()).append(returnVal).append(name).append("*").append(counter).append("=").append(work).toString(); counter++; } matcher.reset(value); if (matcher.find()) value = (new StringBuilder()).append("\"").append(value).append("\"").toString(); if (counter > 0) returnVal = (new StringBuilder()).append(returnVal).append("; ").toString(); returnVal = (new StringBuilder()).append(returnVal).append(name).append("*").append(counter).append("=").append(value).toString(); return returnVal; } }