深入刨析tomcat 之---第10篇 how tomcat works 第13章,Response 发送错误信息 sendError
writedby 张艳涛 在浏览器中发送一个错误应用url 那么tomcat是如何发送错误的呢?
基本上是发送http 的response协议,分为两部分一部分是response设置头信息, 那么先分析一下,tomcat是如花添加响应头的
当我发送:http://localhost:8080/app4/Primitive 的时候,我的应用名字为app1 那么tomcat 会将url 的app4/Primitve进行切分,将app4 做为一个host,如果有标准StandardHost 的时候, 那么看代码
public void invoke(Request request, Response response, ValveContext valveContext) throws IOException, ServletException { // Validate the request and response object types if (!(request.getRequest() instanceof HttpServletRequest) || !(response.getResponse() instanceof HttpServletResponse)) { return; // NOTE - Not much else we can do generically } // Select the Context to be used for this Request StandardHost host = (StandardHost) getContainer(); Context context = (Context) host.map(request, true); if (context == null) { ((HttpServletResponse) response.getResponse()).sendError (HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString("standardHost.noContext")); return; } // Bind the context CL to the current thread Thread.currentThread().setContextClassLoader (context.getLoader().getClassLoader()); // Update the session last access time for our session (if any) HttpServletRequest hreq = (HttpServletRequest) request.getRequest(); String sessionId = hreq.getRequestedSessionId(); if (sessionId != null) { Manager manager = context.getManager(); if (manager != null) { Session session = manager.findSession(sessionId); if ((session != null) && session.isValid()) session.access(); } } // Ask this Context to process this request context.invoke(request, response); }
结果是context找不到,就发送错误信息
public void sendError(int sc, String msg) throws IOException { if (isCommitted()) throw new IllegalStateException (/*sm.getString("responseBase.reset.ise")*/); resp.setAppCommitted(true); ((HttpServletResponse) response).sendError(sc, msg); }
-->
public void sendError(int status, String message) throws IOException { addHeader("Connection", "close"); super.sendError(status, message); }
-->
public void sendError(int status, String message) throws IOException { if (isCommitted()) throw new IllegalStateException (sm.getString("httpResponseBase.sendError.ise")); if (included) return; // Ignore any call from an included servlet setError(); // Record the status code and message. this.status = status; //500 this.message = message; //Cannot find message associated with key 'standardHost.noContext' // Clear any data content that has been buffered resetBuffer(); // Cause the response to be finished (from the application perspective) setSuspended(true); }
设置respose对象的header属性
进入 ErrorReportValve 错误页面处理类
protected void report(Request request, Response response, Throwable throwable) throws IOException { // Do nothing on non-HTTP responses if (!(response instanceof HttpResponse)) return; HttpResponse hresponse = (HttpResponse) response; if (!(response instanceof HttpServletResponse)) return; HttpServletResponse hres = (HttpServletResponse) response; int statusCode = hresponse.getStatus(); String message = RequestUtil.filter(hresponse.getMessage()); if (message == null) message = ""; // Do nothing on a 1xx and 2xx status if (statusCode < 300) return; // Do nothing on a NOT MODIFIED status if (statusCode == HttpServletResponse.SC_NOT_MODIFIED) return; // FIXME: Reset part of the request /* try { if (hresponse.isError()) hresponse.reset(statusCode, message); } catch (IllegalStateException e) { ; } */ Throwable rootCause = null; if (throwable != null) { if (throwable instanceof ServletException) rootCause = ((ServletException) throwable).getRootCause(); } // Do nothing if there is no report for the specified status code String report = null; try { report = sm.getString("http." + statusCode, message); } catch (Throwable t) { ; } if (report == null) return; StringBuffer sb = new StringBuffer(); sb.append("<html><head><title>"); sb.append(ServerInfo.getServerInfo()).append(" - "); sb.append(sm.getString("errorReportValve.errorReport")); sb.append("</title>"); sb.append("<STYLE><!--"); sb.append("H1{font-family : sans-serif,Arial,Tahoma;color : white;background-color : #0086b2;} "); sb.append("H3{font-family : sans-serif,Arial,Tahoma;color : white;background-color : #0086b2;} "); sb.append("BODY{font-family : sans-serif,Arial,Tahoma;color : black;background-color : white;} "); sb.append("B{color : white;background-color : #0086b2;} "); sb.append("HR{color : #0086b2;} "); sb.append("--></STYLE> "); sb.append("</head><body>"); sb.append("<h1>"); sb.append(sm.getString("errorReportValve.statusHeader", "" + statusCode, message)).append("</h1>"); sb.append("<HR size=\"1\" noshade>"); sb.append("<p><b>type</b> "); if (throwable != null) { sb.append(sm.getString("errorReportValve.exceptionReport")); } else { sb.append(sm.getString("errorReportValve.statusReport")); } sb.append("</p>"); sb.append("<p><b>"); sb.append(sm.getString("errorReportValve.message")); sb.append("</b> <u>"); sb.append(message).append("</u></p>"); sb.append("<p><b>"); sb.append(sm.getString("errorReportValve.description")); sb.append("</b> <u>"); sb.append(report); sb.append("</u></p>"); if (throwable != null) { StringWriter stackTrace = new StringWriter(); throwable.printStackTrace(new PrintWriter(stackTrace)); sb.append("<p><b>"); sb.append(sm.getString("errorReportValve.exception")); sb.append("</b> <pre>"); sb.append(stackTrace.toString()); sb.append("</pre></p>"); if (rootCause != null) { stackTrace = new StringWriter(); rootCause.printStackTrace(new PrintWriter(stackTrace)); sb.append("<p><b>"); sb.append(sm.getString("errorReportValve.rootCause")); sb.append("</b> <pre>"); sb.append(stackTrace.toString()); sb.append("</pre></p>"); } } sb.append("<HR size=\"1\" noshade>"); sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>"); sb.append("</body></html>"); try { Writer writer = response.getReporter(); if (writer != null) { Locale locale = Locale.getDefault(); try { hres.setContentType("text/html"); hres.setLocale(locale); } catch (Throwable t) { if (debug >= 1) log("status.setContentType", t); } // If writer is null, it's an indication that the response has // been hard committed already, which should never happen writer.write(sb.toString()); writer.flush(); } } catch (IOException e) { ; } catch (IllegalStateException e) { ; } }
获取
public PrintWriter getReporter() { if (isError()) { try { if (this.stream == null) this.stream = createOutputStream(); } catch (IOException e) { return null; } return (new PrintWriter(this.stream)); } else { if (this.stream != null) { return null; } else { try { return (new PrintWriter(getOutputStream())); } catch (IOException e) { return null; } } } }
获取的out流 底层是 ResponseStream ,这个流我write方法底层是写到response的buffer数组中, 在connector类中,完成了调用,接着就处理发送页面
发送头的方法是sendHeaders,接着发body