/** * Sends an error 500, performing a special logic to detect whether the request is suspended, to * avoid concurrent writes from the application. * * <p>It may happen that the application suspends, and then throws an exception, while an * application spawned thread writes the response content; in such case, we attempt to commit the * error directly bypassing the {@link ErrorHandler} mechanisms and the response OutputStream. * * @param x the Throwable that caused the problem */ protected void handleException(Throwable x) { try { _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, x); _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE, x.getClass()); if (_state.isSuspended()) { HttpFields fields = new HttpFields(); fields.add(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE); ResponseInfo info = new ResponseInfo( _request.getHttpVersion(), fields, 0, HttpStatus.INTERNAL_SERVER_ERROR_500, null, _request.isHead()); boolean committed = sendResponse(info, null, true); if (!committed) LOG.warn("Could not send response error 500: " + x); _request.getAsyncContext().complete(); } else if (isCommitted()) { if (!(x instanceof EofException)) LOG.warn("Could not send response error 500: " + x); } else { _response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString()); _response.sendError(500, x.getMessage()); } } catch (IOException e) { // We tried our best, just log LOG.debug("Could not commit response error 500", e); } }
/* ------------------------------------------------------------ */ protected void handleRequest() throws IOException { boolean error = false; String threadName = null; try { if (LOG.isDebugEnabled()) { threadName = Thread.currentThread().getName(); Thread.currentThread().setName(threadName + " - " + _uri); } // Loop here to handle async request redispatches. // The loop is controlled by the call to async.unhandle in the // finally block below. If call is from a non-blocking connector, // then the unhandle will return false only if an async dispatch has // already happened when unhandle is called. For a blocking connector, // the wait for the asynchronous dispatch or timeout actually happens // within the call to unhandle(). final Server server = _server; boolean handling = _request._async.handling() && server != null && server.isRunning(); while (handling) { _request.setHandled(false); String info = null; try { _uri.getPort(); info = URIUtil.canonicalPath(_uri.getDecodedPath()); if (info == null && !_request.getMethod().equals(HttpMethods.CONNECT)) throw new HttpException(400); _request.setPathInfo(info); if (_out != null) _out.reopen(); if (_request._async.isInitial()) { _request.setDispatcherType(DispatcherType.REQUEST); _connector.customize(_endp, _request); server.handle(this); } else { _request.setDispatcherType(DispatcherType.ASYNC); server.handleAsync(this); } } catch (ContinuationThrowable e) { LOG.ignore(e); } catch (EofException e) { LOG.debug(e); error = true; _request.setHandled(true); } catch (RuntimeIOException e) { LOG.debug(e); error = true; _request.setHandled(true); } catch (HttpException e) { LOG.debug(e); error = true; _request.setHandled(true); _response.sendError(e.getStatus(), e.getReason()); } catch (Throwable e) { if (e instanceof ThreadDeath) throw (ThreadDeath) e; LOG.warn(String.valueOf(_uri), e); error = true; _request.setHandled(true); _generator.sendError(info == null ? 400 : 500, null, null, true); } finally { handling = !_request._async.unhandle() && server.isRunning() && _server != null; } } } finally { if (threadName != null) Thread.currentThread().setName(threadName); if (_request._async.isUncompleted()) { _request._async.doComplete(); if (_expect100Continue) { LOG.debug("100 continues not sent"); // We didn't send 100 continues, but the latest interpretation // of the spec (see httpbis) is that the client will either // send the body anyway, or close. So we no longer need to // do anything special here other than make the connection not persistent _expect100Continue = false; if (!_response.isCommitted()) _generator.setPersistent(false); } if (_endp.isOpen()) { if (error) { _endp.shutdownOutput(); _generator.setPersistent(false); if (!_generator.isComplete()) _response.complete(); } else { if (!_response.isCommitted() && !_request.isHandled()) _response.sendError(HttpServletResponse.SC_NOT_FOUND); _response.complete(); if (_generator.isPersistent()) _connector.persist(_endp); } } else { _response.complete(); } _request.setHandled(true); } } }
@Override public void sendError(int sc) throws IOException { if (sc == 102) sendProcessing(); else sendError(sc, null); }
/** @return True if the channel is ready to continue handling (ie it is not suspended) */ public boolean handle() { LOG.debug("{} handle enter", this); setCurrentHttpChannel(this); String threadName = null; if (LOG.isDebugEnabled()) { threadName = Thread.currentThread().getName(); Thread.currentThread().setName(threadName + " - " + _uri); } // Loop here to handle async request redispatches. // The loop is controlled by the call to async.unhandle in the // finally block below. Unhandle will return false only if an async dispatch has // already happened when unhandle is called. HttpChannelState.Next next = _state.handling(); while (next == Next.CONTINUE && getServer().isRunning()) { boolean error = false; try { _request.setHandled(false); _response.getHttpOutput().reopen(); if (_state.isInitial()) { _request.setTimeStamp(System.currentTimeMillis()); _request.setDispatcherType(DispatcherType.REQUEST); for (HttpConfiguration.Customizer customizer : _configuration.getCustomizers()) customizer.customize(getConnector(), _configuration, _request); getServer().handle(this); } else { if (_request.getHttpChannelState().isExpired()) { _request.setDispatcherType(DispatcherType.ERROR); Throwable ex = _state.getAsyncContextEvent().getThrowable(); String reason = "Async Timeout"; if (ex != null) { reason = "Async Exception"; _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ex); } _request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, new Integer(500)); _request.setAttribute(RequestDispatcher.ERROR_MESSAGE, reason); _request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, _request.getRequestURI()); _response.setStatusWithReason(500, reason); ErrorHandler eh = _state.getContextHandler().getErrorHandler(); if (eh instanceof ErrorHandler.ErrorPageMapper) { String error_page = ((ErrorHandler.ErrorPageMapper) eh) .getErrorPage( (HttpServletRequest) _state.getAsyncContextEvent().getSuppliedRequest()); if (error_page != null) _state.getAsyncContextEvent().setDispatchPath(error_page); } } else _request.setDispatcherType(DispatcherType.ASYNC); getServer().handleAsync(this); } } catch (Error e) { if ("ContinuationThrowable".equals(e.getClass().getSimpleName())) LOG.ignore(e); else { error = true; throw e; } } catch (Exception e) { error = true; if (e instanceof EofException) LOG.debug(e); else LOG.warn(String.valueOf(_uri), e); _state.error(e); _request.setHandled(true); handleException(e); } finally { if (error && _state.isAsyncStarted()) _state.errorComplete(); next = _state.unhandle(); } } if (threadName != null && LOG.isDebugEnabled()) Thread.currentThread().setName(threadName); setCurrentHttpChannel(null); if (next == Next.COMPLETE) { try { _state.completed(); if (!_response.isCommitted() && !_request.isHandled()) _response.sendError(404); else // Complete generating the response _response.closeOutput(); } catch (EofException | ClosedChannelException e) { LOG.debug(e); } catch (Exception e) { LOG.warn("complete failed", e); } finally { _request.setHandled(true); _transport.completed(); } } LOG.debug("{} handle exit, result {}", this, next); return next != Next.WAIT; }