1// 2// ======================================================================== 3// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4// ------------------------------------------------------------------------ 5// All rights reserved. This program and the accompanying materials 6// are made available under the terms of the Eclipse Public License v1.0 7// and Apache License v2.0 which accompanies this distribution. 8// 9// The Eclipse Public License is available at 10// http://www.eclipse.org/legal/epl-v10.html 11// 12// The Apache License v2.0 is available at 13// http://www.opensource.org/licenses/apache2.0.php 14// 15// You may elect to redistribute this code under either of these licenses. 16// ======================================================================== 17// 18 19package org.eclipse.jetty.server.handler; 20 21import java.io.IOException; 22import java.io.PrintWriter; 23import java.io.StringWriter; 24import java.io.Writer; 25 26import javax.servlet.ServletException; 27import javax.servlet.http.HttpServletRequest; 28import javax.servlet.http.HttpServletResponse; 29 30import org.eclipse.jetty.http.HttpHeaders; 31import org.eclipse.jetty.http.HttpMethods; 32import org.eclipse.jetty.http.HttpStatus; 33import org.eclipse.jetty.http.MimeTypes; 34import org.eclipse.jetty.server.AbstractHttpConnection; 35import org.eclipse.jetty.server.Dispatcher; 36import org.eclipse.jetty.server.Request; 37import org.eclipse.jetty.util.ByteArrayISO8859Writer; 38import org.eclipse.jetty.util.log.Log; 39import org.eclipse.jetty.util.log.Logger; 40 41/* ------------------------------------------------------------ */ 42/** Handler for Error pages 43 * An ErrorHandler is registered with {@link ContextHandler#setErrorHandler(ErrorHandler)} or 44 * {@link org.eclipse.jetty.server.Server#addBean(Object)}. 45 * It is called by the HttpResponse.sendError method to write a error page. 46 * 47 */ 48public class ErrorHandler extends AbstractHandler 49{ 50 private static final Logger LOG = Log.getLogger(ErrorHandler.class); 51 public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page"; 52 53 boolean _showStacks=true; 54 boolean _showMessageInTitle=true; 55 String _cacheControl="must-revalidate,no-cache,no-store"; 56 57 /* ------------------------------------------------------------ */ 58 /* 59 * @see org.eclipse.jetty.server.server.Handler#handle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int) 60 */ 61 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException 62 { 63 AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection(); 64 String method = request.getMethod(); 65 if(!method.equals(HttpMethods.GET) && !method.equals(HttpMethods.POST) && !method.equals(HttpMethods.HEAD)) 66 { 67 connection.getRequest().setHandled(true); 68 return; 69 } 70 71 if (this instanceof ErrorPageMapper) 72 { 73 String error_page=((ErrorPageMapper)this).getErrorPage(request); 74 if (error_page!=null && request.getServletContext()!=null) 75 { 76 String old_error_page=(String)request.getAttribute(ERROR_PAGE); 77 if (old_error_page==null || !old_error_page.equals(error_page)) 78 { 79 request.setAttribute(ERROR_PAGE, error_page); 80 81 Dispatcher dispatcher = (Dispatcher) request.getServletContext().getRequestDispatcher(error_page); 82 try 83 { 84 if(dispatcher!=null) 85 { 86 dispatcher.error(request, response); 87 return; 88 } 89 LOG.warn("No error page "+error_page); 90 } 91 catch (ServletException e) 92 { 93 LOG.warn(Log.EXCEPTION, e); 94 return; 95 } 96 } 97 } 98 } 99 100 connection.getRequest().setHandled(true); 101 response.setContentType(MimeTypes.TEXT_HTML_8859_1); 102 if (_cacheControl!=null) 103 response.setHeader(HttpHeaders.CACHE_CONTROL, _cacheControl); 104 ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(4096); 105 handleErrorPage(request, writer, connection.getResponse().getStatus(), connection.getResponse().getReason()); 106 writer.flush(); 107 response.setContentLength(writer.size()); 108 writer.writeTo(response.getOutputStream()); 109 writer.destroy(); 110 } 111 112 /* ------------------------------------------------------------ */ 113 protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message) 114 throws IOException 115 { 116 writeErrorPage(request, writer, code, message, _showStacks); 117 } 118 119 /* ------------------------------------------------------------ */ 120 protected void writeErrorPage(HttpServletRequest request, Writer writer, int code, String message, boolean showStacks) 121 throws IOException 122 { 123 if (message == null) 124 message=HttpStatus.getMessage(code); 125 126 writer.write("<html>\n<head>\n"); 127 writeErrorPageHead(request,writer,code,message); 128 writer.write("</head>\n<body>"); 129 writeErrorPageBody(request,writer,code,message,showStacks); 130 writer.write("\n</body>\n</html>\n"); 131 } 132 133 /* ------------------------------------------------------------ */ 134 protected void writeErrorPageHead(HttpServletRequest request, Writer writer, int code, String message) 135 throws IOException 136 { 137 writer.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\"/>\n"); 138 writer.write("<title>Error "); 139 writer.write(Integer.toString(code)); 140 141 if (_showMessageInTitle) 142 { 143 writer.write(' '); 144 write(writer,message); 145 } 146 writer.write("</title>\n"); 147 } 148 149 /* ------------------------------------------------------------ */ 150 protected void writeErrorPageBody(HttpServletRequest request, Writer writer, int code, String message, boolean showStacks) 151 throws IOException 152 { 153 String uri= request.getRequestURI(); 154 155 writeErrorPageMessage(request,writer,code,message,uri); 156 if (showStacks) 157 writeErrorPageStacks(request,writer); 158 writer.write("<hr /><i><small>Powered by Jetty://</small></i>"); 159 for (int i= 0; i < 20; i++) 160 writer.write("<br/> \n"); 161 } 162 163 /* ------------------------------------------------------------ */ 164 protected void writeErrorPageMessage(HttpServletRequest request, Writer writer, int code, String message,String uri) 165 throws IOException 166 { 167 writer.write("<h2>HTTP ERROR "); 168 writer.write(Integer.toString(code)); 169 writer.write("</h2>\n<p>Problem accessing "); 170 write(writer,uri); 171 writer.write(". Reason:\n<pre> "); 172 write(writer,message); 173 writer.write("</pre></p>"); 174 } 175 176 /* ------------------------------------------------------------ */ 177 protected void writeErrorPageStacks(HttpServletRequest request, Writer writer) 178 throws IOException 179 { 180 Throwable th = (Throwable)request.getAttribute("javax.servlet.error.exception"); 181 while(th!=null) 182 { 183 writer.write("<h3>Caused by:</h3><pre>"); 184 StringWriter sw = new StringWriter(); 185 PrintWriter pw = new PrintWriter(sw); 186 th.printStackTrace(pw); 187 pw.flush(); 188 write(writer,sw.getBuffer().toString()); 189 writer.write("</pre>\n"); 190 191 th =th.getCause(); 192 } 193 } 194 195 196 /* ------------------------------------------------------------ */ 197 /** Get the cacheControl. 198 * @return the cacheControl header to set on error responses. 199 */ 200 public String getCacheControl() 201 { 202 return _cacheControl; 203 } 204 205 /* ------------------------------------------------------------ */ 206 /** Set the cacheControl. 207 * @param cacheControl the cacheControl header to set on error responses. 208 */ 209 public void setCacheControl(String cacheControl) 210 { 211 _cacheControl = cacheControl; 212 } 213 214 /* ------------------------------------------------------------ */ 215 /** 216 * @return True if stack traces are shown in the error pages 217 */ 218 public boolean isShowStacks() 219 { 220 return _showStacks; 221 } 222 223 /* ------------------------------------------------------------ */ 224 /** 225 * @param showStacks True if stack traces are shown in the error pages 226 */ 227 public void setShowStacks(boolean showStacks) 228 { 229 _showStacks = showStacks; 230 } 231 232 /* ------------------------------------------------------------ */ 233 /** 234 * @param showMessageInTitle if true, the error message appears in page title 235 */ 236 public void setShowMessageInTitle(boolean showMessageInTitle) 237 { 238 _showMessageInTitle = showMessageInTitle; 239 } 240 241 242 /* ------------------------------------------------------------ */ 243 public boolean getShowMessageInTitle() 244 { 245 return _showMessageInTitle; 246 } 247 248 /* ------------------------------------------------------------ */ 249 protected void write(Writer writer,String string) 250 throws IOException 251 { 252 if (string==null) 253 return; 254 255 for (int i=0;i<string.length();i++) 256 { 257 char c=string.charAt(i); 258 259 switch(c) 260 { 261 case '&' : 262 writer.write("&"); 263 break; 264 case '<' : 265 writer.write("<"); 266 break; 267 case '>' : 268 writer.write(">"); 269 break; 270 271 default: 272 if (Character.isISOControl(c) && !Character.isWhitespace(c)) 273 writer.write('?'); 274 else 275 writer.write(c); 276 } 277 } 278 } 279 280 /* ------------------------------------------------------------ */ 281 public interface ErrorPageMapper 282 { 283 String getErrorPage(HttpServletRequest request); 284 } 285} 286