package cn.org.rapid_framework.web.httpinclude; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.io.Reader; import java.io.StringWriter; import java.io.Writer; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * <pre> * 完成于<jsp:include page''/>相同的功能 * 用于include其它页面以用于布局,可以用于在freemarker,velocity的servlet环境应用中直接include其它http请求 * </pre> * * <br /> * <b>Freemarker及Velocity示例使用:</b> * <pre> * ${httpInclude.include("http://www.google.com")}; * ${httpInclude.include("/servlet/head?p1=v1&p2=v2")}; * ${httpInclude.include("/head.jsp")}; * ${httpInclude.include("/head.do?p1=v1&p2=v2")}; * ${httpInclude.include("/head.htm")}; * </pre> * * @author badqiu * */ public class HttpInclude { private final static Log log = LogFactory.getLog(HttpInclude.class); public static String sessionIdKey = "JSESSIONID"; private HttpServletRequest request; private HttpServletResponse response; public HttpInclude(HttpServletRequest request, HttpServletResponse response) { this.request = request; this.response = response; } public String include(String includePath) { StringWriter sw = new StringWriter(8192); include(includePath,sw); return sw.toString(); } public void include(String includePath,Writer writer) { try { if(isRemoteHttpRequest(includePath)) { getRemoteContent(includePath,writer); }else { getLocalContent(includePath,writer); } } catch (ServletException e) { throw new RuntimeException("include error,path:"+includePath+" cause:"+e,e); } catch (IOException e) { throw new RuntimeException("include error,path:"+includePath+" cause:"+e,e); } } private static boolean isRemoteHttpRequest(String includePath) { return includePath != null && ( includePath.toLowerCase().startsWith("http://") || includePath.toLowerCase().startsWith("https://") ); } private void getLocalContent(String includePath,Writer writer) throws ServletException, IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(8192); CustomOutputHttpServletResponseWrapper customResponse = new CustomOutputHttpServletResponseWrapper(response,writer,outputStream); request.getRequestDispatcher(includePath).include(request, customResponse); customResponse.flushBuffer(); if(customResponse.useOutputStream) { writer.write(outputStream.toString(response.getCharacterEncoding())); //TODO: response.getCharacterEncoding()有可能为null } writer.flush(); } //TODO handle cookies and http query parameters encoding //TODO set inheritParams from request private void getRemoteContent(String urlString,Writer writer) throws MalformedURLException, IOException { URL url = new URL(getWithSessionIdUrl(urlString)); URLConnection conn = url.openConnection(); setConnectionHeaders(urlString, conn); InputStream input = conn.getInputStream(); try { Reader reader = new InputStreamReader(input,Utils.getContentEncoding(conn,response)); Utils.copy(reader,writer); }finally { if(input != null) input.close(); } writer.flush(); } private void setConnectionHeaders(String urlString, URLConnection conn) { conn.setReadTimeout(6000); conn.setConnectTimeout(6000); String cookie = getCookieString(); conn.setRequestProperty("Cookie", cookie); //TODO: 用于支持 httpinclude_header.properties // conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3"); // conn.setRequestProperty("Host", url.getHost()); if(log.isDebugEnabled()) { log.debug("request properties:"+conn.getRequestProperties()+" for url:"+urlString); } } //TODO add session id with url private String getWithSessionIdUrl(String url) { return url; } private static final String SET_COOKIE_SEPARATOR="; "; private String getCookieString() { StringBuffer sb = new StringBuffer(64); Cookie[] cookies = request.getCookies(); if(cookies != null ) { for(Cookie c : cookies) { if(!sessionIdKey.equals(c.getName())) { sb.append(c.getName()).append("=").append(c.getValue()).append(SET_COOKIE_SEPARATOR); } } } String sessionId = Utils.getSessionId(request); if(sessionId != null) { sb.append(sessionIdKey).append("=").append(sessionId).append(SET_COOKIE_SEPARATOR); } return sb.toString(); } public static class CustomOutputHttpServletResponseWrapper extends HttpServletResponseWrapper { public boolean useWriter = false; public boolean useOutputStream = false; // private PrintWriter printWriter; private ServletOutputStream servletOutputStream; public CustomOutputHttpServletResponseWrapper(HttpServletResponse response,final Writer customWriter,final OutputStream customOutputStream) { super(response); this.printWriter = new PrintWriter(customWriter); this.servletOutputStream = new ServletOutputStream() { @Override public void write(int b) throws IOException { customOutputStream.write(b); } @Override public void write(byte[] b) throws IOException { customOutputStream.write(b); } @Override public void write(byte[] b, int off, int len)throws IOException { customOutputStream.write(b, off, len); } }; } @Override public PrintWriter getWriter() throws IOException { if(useOutputStream) throw new IllegalStateException("getOutputStream() has already been called for this response"); useWriter = true; return printWriter; } @Override public ServletOutputStream getOutputStream() throws IOException { if(useWriter) throw new IllegalStateException("getWriter() has already been called for this response"); useOutputStream = true; return servletOutputStream; } @Override public void flushBuffer() throws IOException { if(useWriter) printWriter.flush(); if(useOutputStream) servletOutputStream.flush(); } } static class Utils { static String getContentEncoding(URLConnection conn,HttpServletResponse response) { String contentEncoding = conn.getContentEncoding(); if(conn.getContentEncoding() == null) { contentEncoding = parseContentTypeForCharset(conn.getContentType()); if(contentEncoding == null) { contentEncoding = response.getCharacterEncoding(); } } else { contentEncoding = conn.getContentEncoding(); } return contentEncoding; } static Pattern p = Pattern.compile("(charset=)(.*)",Pattern.CASE_INSENSITIVE); private static String parseContentTypeForCharset(String contentType) { if(contentType == null) return null; Matcher m = p.matcher(contentType); if(m.find()) { return m.group(2).trim(); } return null; } private static void copy(Reader in, Writer out) throws IOException { char[] buff = new char[8192]; while(in.read(buff) >= 0) { out.write(buff); } } private static String getSessionId(HttpServletRequest request) { HttpSession session = request.getSession(false); if(session == null) { return null; } return session.getId(); } } }