package org.archive.wayback.webapp; import java.io.IOException; import java.io.PrintWriter; import java.util.logging.Logger; 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 org.archive.wayback.webapp.PerfStats.OutputFormat; import org.archive.wayback.webapp.PerfStats.PerfStatEntry; public class PerfWritingHttpServletResponse extends HttpServletResponseWrapper { protected final Enum<?> perfStat; protected final String perfStatsHeader; protected boolean hasWritten; protected final HttpServletResponse httpResponse; protected OutputFormat outputFormat; protected int expireTimeout = 60; protected final String requestURI; protected boolean perfCookie = false; /** * Initialize with default output format. * @param request * @param response * @param stat * @param perfStatsHeader */ public PerfWritingHttpServletResponse(HttpServletRequest request, HttpServletResponse response, Enum<?> stat, String perfStatsHeader) { this(request, response, stat, perfStatsHeader, OutputFormat.BRACKET); } /** * Initialize with all parameters. * @param request {@code requestURI} is used as cookie path * @param response wrapped response * @param stat names stat for <i>total</i> elapsed time. * @param perfStatsHeader names HTTP header field for dumping all stats. * @param format format of {@code perfStatsHeader} */ public PerfWritingHttpServletResponse(HttpServletRequest request, HttpServletResponse response, Enum<?> stat, String perfStatsHeader, OutputFormat format) { super(response); this.httpResponse = response; this.requestURI = request.getRequestURI(); this.perfStat = stat; this.perfStatsHeader = perfStatsHeader; this.outputFormat = format; } /** * Write performance metrics to HTTP header field and Cookie. * You don't need to call this method explicitly. It is called * implicitly by calls to {@link #sendError(int)}, {@link #sendRedirect(String)}, * {@link #getWriter()} or {@link #getOutputStream()}. * 2014-11-17 Now it doesn't call {@code timeEnd} for * {@code perfStat}. Be sure to call {@code endNow()} explicitly. */ public void writePerfStats() { if (hasWritten) { return; } // call timeEnd only if it's not already called, so as // not to change its value. long elapsed = PerfStats.getTotal(perfStat); if (elapsed <= 0) { elapsed = PerfStats.timeEnd(perfStat); } if (perfStatsHeader != null) { httpResponse.setHeader(perfStatsHeader, PerfStats.getAllStats(outputFormat)); } if (perfCookie && requestURI != null) { Cookie cookie = new Cookie("wb_total_perf", String.valueOf(elapsed)); cookie.setMaxAge(expireTimeout); //cookie.setDomain(domainName); cookie.setPath(requestURI); try { httpResponse.addCookie(cookie); } catch (IllegalArgumentException ex) { Logger logger = Logger.getLogger(getClass().getName()); logger.warning("addCookie failed for " + cookie + " (path=\"" + requestURI + "\"): " + ex.getMessage()); } } hasWritten = true; } @Override public void sendError(int sc, String msg) throws IOException { writePerfStats(); super.sendError(sc, msg); } @Override public void sendError(int sc) throws IOException { writePerfStats(); super.sendError(sc); } @Override public void sendRedirect(String location) throws IOException { writePerfStats(); super.sendRedirect(location); } @Override public ServletOutputStream getOutputStream() throws IOException { writePerfStats(); return super.getOutputStream(); } @Override public PrintWriter getWriter() throws IOException { writePerfStats(); return super.getWriter(); } /** * @deprecated 1.8.1, no replacement. this method has no effect. */ public void enablePerfCookie() { this.perfCookie = true; } /** * Set to {@code true} if {@code wb_total_perf} cookie should be set * in the response. In general this is a bad idea because it'll defeat * front-end caching. As such, this is off by default. * @param perfCookie {@code true} for sending out the cookie */ public void setPerfCookie(boolean perfCookie) { this.perfCookie = perfCookie; } }