1 29 30 package com.caucho.server.webapp; 31 32 import com.caucho.Version; 33 import com.caucho.config.*; 34 import com.caucho.i18n.CharacterEncoding; 35 import com.caucho.java.LineMap; 36 import com.caucho.java.LineMapException; 37 import com.caucho.java.ScriptStackTrace; 38 import com.caucho.server.connection.AbstractHttpRequest; 39 import com.caucho.server.connection.AbstractHttpResponse; 40 import com.caucho.server.connection.CauchoRequest; 41 import com.caucho.server.connection.CauchoResponse; 42 import com.caucho.server.dispatch.BadRequestException; 43 import com.caucho.server.util.CauchoSystem; 44 import com.caucho.util.CharBuffer; 45 import com.caucho.util.CompileException; 46 import com.caucho.util.HTTPUtil; 47 import com.caucho.util.L10N; 48 import com.caucho.util.LineCompileException; 49 import com.caucho.util.Log; 50 import com.caucho.vfs.ClientDisconnectException; 51 import com.caucho.vfs.Encoding; 52 53 import javax.servlet.RequestDispatcher ; 54 import javax.servlet.ServletException ; 55 import javax.servlet.ServletRequest ; 56 import javax.servlet.ServletResponse ; 57 import javax.servlet.UnavailableException ; 58 import javax.servlet.http.HttpServletRequest ; 59 import javax.servlet.http.HttpServletResponse ; 60 import java.io.CharArrayWriter ; 61 import java.io.IOException ; 62 import java.io.PrintWriter ; 63 import java.util.HashMap ; 64 import java.util.Locale ; 65 import java.util.logging.Level ; 66 import java.util.logging.Logger ; 67 68 71 public class ErrorPageManager { 72 private final static L10N L = new L10N(ErrorPageManager.class); 73 private final static Logger log = Log.open(ErrorPageManager.class); 74 75 public static final char []MSIE_PADDING; 76 77 public static String REQUEST_URI = "javax.servlet.include.request_uri"; 78 public static String CONTEXT_PATH = "javax.servlet.include.context_path"; 79 public static String SERVLET_PATH = "javax.servlet.include.servlet_path"; 80 public static String PATH_INFO = "javax.servlet.include.path_info"; 81 public static String QUERY_STRING = "javax.servlet.include.query_string"; 82 83 public static String STATUS_CODE = "javax.servlet.error.status_code"; 84 public static String EXCEPTION_TYPE = "javax.servlet.error.exception_type"; 85 public static String MESSAGE = "javax.servlet.error.message"; 86 public static String EXCEPTION = "javax.servlet.error.exception"; 87 public static String ERROR_URI = "javax.servlet.error.request_uri"; 88 public static String SERVLET_NAME = "javax.servlet.error.servlet_name"; 89 90 public static String JSP_EXCEPTION = "javax.servlet.jsp.jspException"; 91 92 public static String SHUTDOWN = "com.caucho.shutdown"; 93 94 private WebApp _app; 95 private WebAppContainer _appContainer; 96 private HashMap <Object ,String > _errorPageMap = new HashMap <Object ,String >(); 97 private String _defaultLocation; 98 99 private ErrorPageManager _parent; 100 101 104 public ErrorPageManager() 105 { 106 } 107 108 111 public void setParent(ErrorPageManager parent) 112 { 113 _parent = parent; 114 } 115 116 119 public ErrorPageManager getParent() 120 { 121 return _parent; 122 } 123 124 127 public void addErrorPage(ErrorPage errorPage) 128 { 129 if (errorPage.getExceptionType() != null) { 130 _errorPageMap.put(errorPage.getExceptionType(), 131 errorPage.getLocation()); 132 } 133 else if (errorPage.getErrorCode() < 0) { 134 _defaultLocation = errorPage.getLocation(); 135 } 136 else 137 _errorPageMap.put(new Integer (errorPage.getErrorCode()), 138 errorPage.getLocation()); 139 } 140 141 144 public void setWebApp(WebApp app) 145 { 146 _app = app; 147 } 148 149 152 public void setWebAppContainer(WebAppContainer appContainer) 153 { 154 _appContainer = appContainer; 155 } 156 157 160 public void sendServletError(Throwable e, 161 ServletRequest req, 162 ServletResponse res) 163 throws IOException 164 { 165 HttpServletResponse response = (HttpServletResponse ) res; 166 HttpServletRequest request = (HttpServletRequest ) req; 167 Throwable rootExn = e; 168 Throwable errorPageExn = null; 169 LineMap lineMap = null; 170 171 if (response instanceof AbstractHttpResponse) { 172 ((AbstractHttpResponse) response).killCache(); 173 ((AbstractHttpResponse) response).setNoCache(true); 174 } 175 176 try { 177 response.reset(); 178 } catch (IllegalStateException e1) { 179 } 180 181 if (rootExn instanceof ClientDisconnectException) 182 throw (ClientDisconnectException) rootExn; 183 184 String location = null; 185 186 String title = "500 Servlet Exception"; 187 boolean badRequest = false; 188 boolean doStackTrace = true; 189 boolean isCompileException = false; 190 boolean isLineCompileException = false; 191 boolean isServletException = false; 192 Throwable compileException = null; 193 String lineMessage = null; 194 195 boolean lookupErrorPage = true; 196 while (true) { 197 if (rootExn instanceof LineMapException) 198 lineMap = ((LineMapException) rootExn).getLineMap(); 199 200 if (lookupErrorPage) { 201 errorPageExn = rootExn; 202 } 203 204 if (rootExn instanceof CompileException) { 205 doStackTrace = false; 206 isCompileException = true; 207 208 if (rootExn instanceof LineCompileException) { 210 compileException = rootExn; 211 212 isLineCompileException = true; 213 } 214 else if (compileException == null) compileException = rootExn; 216 } 217 else if (rootExn instanceof LineException) { 218 if (lineMessage == null) 219 lineMessage = rootExn.getMessage(); 220 } 221 222 if (rootExn instanceof BadRequestException) 223 badRequest = true; 224 225 if (location != null || ! lookupErrorPage) { 226 } 227 else if (rootExn instanceof LineMapException && 228 rootExn instanceof ServletException && 229 ! (rootExn instanceof LineCompileException) && 230 rootExn.getCause() != null) { 231 } 233 else if (! isServletException) { 234 location = getErrorPage(rootExn); 237 isServletException = true; 238 } 239 else { 240 location = getErrorPage(rootExn); 241 lookupErrorPage = false; 242 } 243 244 if (location != null) 245 lookupErrorPage = false; 246 247 Throwable cause = null; 248 if (rootExn instanceof ServletException && 249 ! (rootExn instanceof LineCompileException)) 250 cause = ((ServletException ) rootExn).getRootCause(); 251 else { 252 lookupErrorPage = false; 253 cause = rootExn.getCause(); 254 } 255 256 if (cause != null) 257 rootExn = cause; 258 else { 259 break; 260 } 261 } 262 263 if (location == null && lookupErrorPage) { 264 location = getErrorPage(rootExn); 265 } 266 267 if (location == null) 268 location = getErrorPage(500); 269 270 if (location == null && _defaultLocation == null && _parent != null) { 271 _parent.sendServletError(e, req, res); 272 return; 273 } 274 275 if (isCompileException) 276 log.warning(compileException.getMessage()); 277 else if (! doStackTrace) 278 log.warning(rootExn.toString()); 279 else 280 log.log(Level.WARNING, e.toString(), e); 281 282 if (badRequest) { 283 title = rootExn.getMessage(); 284 doStackTrace = false; 285 badRequest = true; 286 287 if (request instanceof CauchoRequest) 288 ((CauchoRequest) request).killKeepalive(); 289 290 response.resetBuffer(); 291 292 response.setStatus(response.SC_BAD_REQUEST, rootExn.getMessage()); 293 294 } 295 else if (rootExn instanceof UnavailableException ) { 296 UnavailableException unAvail = (UnavailableException ) rootExn; 297 298 if (unAvail.isPermanent()) { 299 response.setStatus(HttpServletResponse.SC_NOT_FOUND); 300 title = "404 Not Found"; 301 302 if (location == null) 303 location = getErrorPage(response.SC_NOT_FOUND); 304 } 305 else { 306 response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); 307 title = "503 Unavailable"; 308 309 if (unAvail.getUnavailableSeconds() > 0) 310 response.setIntHeader("Retry-After", 311 unAvail.getUnavailableSeconds()); 312 313 if (location == null) 314 location = getErrorPage(response.SC_SERVICE_UNAVAILABLE); 315 } 316 } 317 323 else 324 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 325 326 if (location == null) 327 location = _defaultLocation; 328 329 if (location != null) { 330 if (errorPageExn == null) 331 errorPageExn = rootExn; 332 333 request.setAttribute(JSP_EXCEPTION, errorPageExn); 334 request.setAttribute(EXCEPTION, errorPageExn); 335 request.setAttribute(EXCEPTION_TYPE, errorPageExn.getClass()); 336 if (request instanceof HttpServletRequest ) 337 request.setAttribute(ERROR_URI, 338 ((HttpServletRequest ) request).getRequestURI()); 339 if (request instanceof AbstractHttpRequest) 340 request.setAttribute(AbstractHttpRequest.SERVLET_NAME, 341 ((AbstractHttpRequest) request).getServletName()); 342 343 request.setAttribute(STATUS_CODE, new Integer (500)); 344 request.setAttribute(MESSAGE, errorPageExn.getMessage()); 345 349 350 try { 351 RequestDispatcher disp = null; 352 355 if (_app != null) 356 disp = _app.getRequestDispatcher(location); 357 else if (_appContainer != null) 358 disp = _appContainer.getRequestDispatcher(location); 359 360 if (disp != null) { 361 ((RequestDispatcherImpl) disp).error(request, response); 362 return; 363 } 364 } catch (Throwable e1) { 365 log.log(Level.INFO, e1.toString(), e1); 366 rootExn = e1; 367 } 368 } 369 370 response.setContentType("text/html"); 371 372 String encoding = CharacterEncoding.getLocalEncoding(); 373 374 if (encoding != null) 375 response.setCharacterEncoding(encoding); 376 else { 377 Locale locale = Locale.getDefault(); 378 if (! "ISO-8859-1".equals(Encoding.getMimeName(locale))) 379 response.setLocale(Locale.getDefault()); 380 } 381 382 PrintWriter out = response.getWriter(); 383 384 out.println("<html>"); 385 if (! response.isCommitted()) 386 out.println("<head><title>" + title + "</title></head>"); 387 out.println("<body>"); 388 out.println("<h1>" + title + "</h1>"); 389 390 out.println("<code><pre>"); 391 392 399 400 if (compileException != null) 401 out.println(escapeHtml(compileException.getMessage())); 402 else if (! doStackTrace) 403 out.println(escapeHtml(rootExn.toString())); 404 405 if (doStackTrace || log.isLoggable(Level.FINE)) { 406 printStackTrace(out, lineMessage, rootExn, lineMap); 407 } 408 409 out.println("</pre></code>"); 410 411 if (CauchoSystem.isTesting() || _app == null) { 412 } 413 else { 414 out.println("<p /><hr />"); 415 out.println("<small>"); 416 417 if (_app.getServer() != null 418 && _app.getServer().getServerHeader() != null) { 419 out.println(_app.getServer().getServerHeader()); 420 } 421 else 422 out.println(com.caucho.Version.FULL_VERSION); 423 424 out.println("</small>"); 425 } 426 427 out.println("</body></html>"); 428 429 String userAgent = request.getHeader("User-Agent"); 430 431 if (userAgent != null && userAgent.indexOf("MSIE") >= 0) { 432 out.print(MSIE_PADDING); 433 } 434 435 out.close(); 436 } 437 438 444 public void sendError(CauchoRequest request, 445 CauchoResponse response, 446 int code, String message) 447 throws IOException 448 { 449 response.resetBuffer(); 450 451 455 456 response.setStatus(code, message); 457 458 try { 459 if (handleErrorStatus(request, response, code, message) || 460 code == HttpServletResponse.SC_NOT_MODIFIED) 461 return; 462 463 response.setContentType("text/html"); 464 PrintWriter out = response.getWriter(); 465 466 out.println("<html>"); 467 if (! response.isCommitted()) { 468 out.print("<head><title>"); 469 out.print(code); 470 out.print(" "); 471 out.print(message); 472 out.println("</title></head>"); 473 } 474 475 out.println("<body>"); 476 out.print("<h1>"); 477 out.print(code); 478 out.print(" "); 479 out.print(message); 480 out.println("</h1>"); 481 482 if (code == HttpServletResponse.SC_NOT_FOUND) { 483 out.println(L.l("{0} was not found on this server.", 484 HTTPUtil.encodeString(request.getPageURI()))); 485 } 486 487 if (! CauchoSystem.isTesting()) { 488 out.println("<p /><hr />"); 489 out.println("<small>"); 490 out.println(com.caucho.Version.FULL_VERSION); 491 out.println("</small>"); 492 } 493 out.println("</body></html>"); 494 495 String userAgent = request.getHeader("User-Agent"); 496 497 if (userAgent != null && userAgent.indexOf("MSIE") >= 0) { 498 out.write(MSIE_PADDING, 0, MSIE_PADDING.length); 499 } 500 } catch (Exception e) { 501 log.log(Level.WARNING, e.toString(), e); 502 } 503 } 504 505 510 private boolean handleErrorStatus(CauchoRequest request, 511 CauchoResponse response, 512 int code, String message) 513 throws ServletException , IOException 514 { 515 if (code == HttpServletResponse.SC_OK || 516 code == HttpServletResponse.SC_MOVED_TEMPORARILY || 517 code == HttpServletResponse.SC_NOT_MODIFIED) 518 return false; 519 520 if (request.getRequestDepth(0) > 16) 521 return false; 522 523 else if (request.getAttribute(AbstractHttpRequest.ERROR_URI) != null) 524 return false; 525 526 response.killCache(); 527 528 String location = getErrorPage(code); 529 530 if (location == null) 531 location = _defaultLocation; 532 533 if (location == null && _parent != null) 534 return _parent.handleErrorStatus(request, response, code, message); 535 536 if (_app == null && _appContainer == null) 537 return false; 538 539 if (location != null && ! location.equals(request.getRequestURI())) { 540 542 request.setAttribute(AbstractHttpRequest.STATUS_CODE, 543 new Integer (code)); 544 request.setAttribute(AbstractHttpRequest.MESSAGE, 545 message); 546 request.setAttribute(AbstractHttpRequest.ERROR_URI, 547 request.getRequestURI()); 548 if (request instanceof AbstractHttpRequest) 549 request.setAttribute(AbstractHttpRequest.SERVLET_NAME, 550 ((AbstractHttpRequest) request).getServletName()); 551 552 try { 553 RequestDispatcher disp = null; 554 if (_app != null) 557 disp = _app.getRequestDispatcher(location); 558 else if (_appContainer != null) 559 disp = _appContainer.getRequestDispatcher(location); 560 561 563 if (disp != null) 564 ((RequestDispatcherImpl) disp).error(request, response); 565 else 566 return false; 567 } catch (Throwable e) { 568 sendServletError(e, request, response); 569 } 570 571 return true; 572 } 573 574 return false; 575 } 576 577 580 String getErrorPage(Throwable e) 581 { 582 return getErrorPage(e, Throwable .class); 583 } 584 585 588 String getErrorPage(Throwable e, Class limit) 589 { 590 Class cl = e.getClass(); 591 for (; cl != null; cl = cl.getSuperclass()) { 592 String location = (String ) _errorPageMap.get(cl.getName()); 593 if (location != null) 594 return location; 595 596 if (cl == limit) 597 break; 598 } 599 600 for (cl = e.getClass(); cl != null; cl = cl.getSuperclass()) { 601 String name = cl.getName(); 602 int p = name.lastIndexOf('.'); 603 604 if (p > 0) { 605 name = name.substring(p + 1); 606 607 String location = (String ) _errorPageMap.get(name); 608 if (location != null) 609 return location; 610 } 611 612 if (cl == limit) 613 break; 614 } 615 616 return null; 617 } 618 619 622 String getErrorPage(int code) 623 { 624 Integer key = new Integer (code); 625 626 String location = (String ) _errorPageMap.get(key); 627 if (location != null) 628 return location; 629 630 return (String ) _errorPageMap.get(new Integer (0)); 631 } 632 633 636 private void printStackTrace(PrintWriter out, 637 String lineMessage, 638 Throwable e, 639 LineMap lineMap) 640 { 641 CharArrayWriter writer = new CharArrayWriter (); 642 PrintWriter pw = new PrintWriter (writer); 643 644 if (lineMessage != null) 645 pw.println(lineMessage); 646 647 if (lineMap != null) 648 lineMap.printStackTrace(e, pw); 649 else 650 ScriptStackTrace.printStackTrace(e, pw); 651 652 pw.close(); 653 654 char []array = writer.toCharArray(); 655 out.print(escapeHtml(new String (array))); 656 } 657 658 661 private String escapeHtml(String s) 662 { 663 if (s == null) 664 return null; 665 666 CharBuffer cb = new CharBuffer(); 667 int lineCharacter = 0; 668 boolean startsWithSpace = false; 669 670 for (int i = 0; i < s.length(); i++) { 671 char ch = s.charAt(i); 672 673 lineCharacter++; 674 675 if (ch == '<') 676 cb.append("<"); 677 else if (ch == '&') 678 cb.append("&"); 679 else if (ch == '\n' || ch == '\r') { 680 lineCharacter = 0; 681 cb.append(ch); 682 startsWithSpace = false; 683 } 684 else if (lineCharacter > 70 && ch == ' ' && ! startsWithSpace) { 685 lineCharacter = 0; 686 cb.append('\n'); 687 for (; i + 1 < s.length() && s.charAt(i + 1) == ' '; i++) { 688 } 689 } 690 else if (lineCharacter == 1 && (ch == ' ' || ch == '\t')) { 691 cb.append((char) ch); 692 startsWithSpace = true; 693 } 694 else 695 cb.append(ch); 696 } 697 698 return cb.toString(); 699 } 700 701 static { 702 MSIE_PADDING = ("\n\n\n\n" + 703 "<!--\n" + 704 " - Unfortunately, Microsoft has added a clever new\n" + 705 " - \"feature\" to Internet Explorer. If the text in\n" + 706 " - an error's message is \"too small\", specifically\n" + 707 " - less than 512 bytes, Internet Explorer returns\n" + 708 " - its own error message. Yes, you can turn that\n" + 709 " - off, but *surprise* it's pretty tricky to find\n" + 710 " - buried as a switch called \"smart error\n" + 711 " - messages\" That means, of course, that many of\n" + 712 " - Resin's error messages are censored by default.\n" + 713 " - And, of course, you'll be shocked to learn that\n" + 714 " - IIS always returns error messages that are long\n" + 715 " - enough to make Internet Explorer happy. The\n" + 716 " - workaround is pretty simple: pad the error\n" + 717 " - message with a big comment to push it over the\n" + 718 " - five hundred and twelve byte minimum. Of course,\n" + 719 " - that's exactly what you're reading right now.\n" + 720 " -->\n").toCharArray(); 721 } 722 } 723 | Popular Tags |