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("&amp;");
263                    break;
264                case '<' :
265                    writer.write("&lt;");
266                    break;
267                case '>' :
268                    writer.write("&gt;");
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