/**
   * Handle an HTTP status code or Java exception by forwarding control to the location included in
   * the specified errorPage object. It is assumed that the caller has already recorded any request
   * attributes that are to be forwarded to this page. Return <code>true</code> if we successfully
   * utilized the specified error page location, or <code>false</code> if the default error report
   * should be rendered.
   *
   * @param request The request being processed
   * @param response The response being generated
   * @param errorPage The errorPage directive we are obeying
   */
  private boolean custom(Request request, Response response, ErrorPage errorPage) {

    if (container.getLogger().isDebugEnabled())
      container.getLogger().debug("Processing " + errorPage);

    try {
      // Forward control to the specified location
      ServletContext servletContext = request.getContext().getServletContext();
      RequestDispatcher rd = servletContext.getRequestDispatcher(errorPage.getLocation());

      if (response.isCommitted()) {
        // Response is committed - including the error page is the
        // best we can do
        rd.include(request.getRequest(), response.getResponse());
      } else {
        // Reset the response (keeping the real error code and message)
        response.resetBuffer(true);
        response.setContentLength(-1);

        rd.forward(request.getRequest(), response.getResponse());

        // If we forward, the response is suspended again
        response.setSuspended(false);
      }

      // Indicate that we have successfully processed this custom page
      return (true);

    } catch (Throwable t) {
      ExceptionUtils.handleThrowable(t);
      // Report our failure to process this custom page
      container.getLogger().error("Exception Processing " + errorPage, t);
      return (false);
    }
  }
示例#2
0
  /**
   * Internal method that allows a redirect to be sent with a status other than {@link
   * HttpServletResponse#SC_FOUND} (302). No attempt is made to validate the status code.
   */
  public void sendRedirect(String location, int status) throws IOException {
    if (isCommitted()) {
      throw new IllegalStateException(sm.getString("coyoteResponse.sendRedirect.ise"));
    }

    // Ignore any call from an included servlet
    if (included) {
      return;
    }

    // Clear any data content that has been buffered
    resetBuffer(true);

    // Generate a temporary redirect to the specified location
    try {
      String absolute = toAbsolute(location);
      setStatus(status);
      setHeader("Location", absolute);
      if (getContext().getSendRedirectBody()) {
        PrintWriter writer = getWriter();
        writer.print(
            sm.getString("coyoteResponse.sendRedirect.note", RequestUtil.filter(absolute)));
        flushBuffer();
      }
    } catch (IllegalArgumentException e) {
      setStatus(SC_NOT_FOUND);
    }

    // Cause the response to be finished (from the application perspective)
    setSuspended(true);
  }
  public void finish() {

    if (response == null) {
      throw new IllegalStateException(sm.getString("responseFacade.nullResponse"));
    }

    response.setSuspended(true);
  }
  @Override
  public PrintWriter getWriter() throws IOException {

    //        if (isFinished())
    //            throw new IllegalStateException
    //                (/*sm.getString("responseFacade.finished")*/);

    PrintWriter writer = response.getWriter();
    if (isFinished()) response.setSuspended(true);
    return (writer);
  }
  @Override
  public ServletOutputStream getOutputStream() throws IOException {

    //        if (isFinished())
    //            throw new IllegalStateException
    //                (/*sm.getString("responseFacade.finished")*/);

    ServletOutputStream sos = response.getOutputStream();
    if (isFinished()) response.setSuspended(true);
    return (sos);
  }
示例#6
0
  /**
   * Invoke the next Valve in the sequence. When the invoke returns, check the response state, and
   * output an error report is necessary.
   *
   * @param request The servlet request to be processed
   * @param response The servlet response to be created
   * @exception IOException if an input/output error occurs
   * @exception ServletException if a servlet error occurs
   */
  @Override
  public void invoke(Request request, Response response) throws IOException, ServletException {

    // Perform the request
    getNext().invoke(request, response);

    if (response.isCommitted()) {
      return;
    }

    Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

    if (request.isAsyncStarted() && response.getStatus() < 400 && throwable == null) {
      return;
    }

    if (throwable != null) {

      // The response is an error
      response.setError();

      // Reset the response (if possible)
      try {
        response.reset();
      } catch (IllegalStateException e) {
        // Ignore
      }

      response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }

    response.setSuspended(false);

    try {
      report(request, response, throwable);
    } catch (Throwable tt) {
      ExceptionUtils.handleThrowable(tt);
    }

    if (request.isAsyncStarted()) {
      request.getAsyncContext().complete();
    }
  }
示例#7
0
  /**
   * Send an error response with the specified status and message.
   *
   * @param status HTTP status code to send
   * @param message Corresponding message to send
   * @exception IllegalStateException if this response has already been committed
   * @exception IOException if an input/output error occurs
   */
  @Override
  public void sendError(int status, String message) throws IOException {

    if (isCommitted()) {
      throw new IllegalStateException(sm.getString("coyoteResponse.sendError.ise"));
    }

    // Ignore any call from an included servlet
    if (included) {
      return;
    }

    setError();

    coyoteResponse.setStatus(status);
    coyoteResponse.setMessage(message);

    // Clear any data content that has been buffered
    resetBuffer();

    // Cause the response to be finished (from the application perspective)
    setSuspended(true);
  }
  /**
   * Process Comet event.
   *
   * @param request Request to be processed
   * @param response Response to be produced
   * @param event the event
   * @exception IOException if an input/output error occurred
   * @exception ServletException if a servlet error occurred
   */
  @Override
  public final void event(Request request, Response response, CometEvent event)
      throws IOException, ServletException {

    // Select the Context to be used for this Request
    Context context = request.getContext();

    // Bind the context CL to the current thread
    if (context.getLoader() != null) {
      // Not started - it should check for availability first
      // This should eventually move to Engine, it's generic.
      Thread.currentThread().setContextClassLoader(context.getLoader().getClassLoader());
    }

    // Ask this Context to process this request
    context.getPipeline().getFirst().event(request, response, event);

    // Error page processing
    response.setSuspended(false);

    Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

    if (t != null) {
      throwable(request, response, t);
    } else {
      status(request, response);
    }

    // Access a session (if present) to update last accessed time, based on a
    // strict interpretation of the specification
    if (ACCESS_SESSION) {
      request.getSession(false);
    }

    // Restore the context classloader
    Thread.currentThread().setContextClassLoader(StandardHostValve.class.getClassLoader());
  }
示例#9
0
  @Override
  public boolean asyncDispatch(
      org.apache.coyote.Request req, org.apache.coyote.Response res, SocketStatus status)
      throws Exception {
    Request request = (Request) req.getNote(ADAPTER_NOTES);
    Response response = (Response) res.getNote(ADAPTER_NOTES);

    if (request == null) {
      throw new IllegalStateException("Dispatch may only happen on an existing request.");
    }
    boolean comet = false;
    boolean success = true;
    AsyncContextImpl asyncConImpl = (AsyncContextImpl) request.getAsyncContext();
    req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
    try {
      if (!request.isAsync() && !comet) {
        // Error or timeout - need to tell listeners the request is over
        // Have to test this first since state may change while in this
        // method and this is only required if entering this method in
        // this state
        Context ctxt = (Context) request.getMappingData().context;
        if (ctxt != null) {
          ctxt.fireRequestDestroyEvent(request);
        }
        // Lift any suspension (e.g. if sendError() was used by an async
        // request) to allow the response to be written to the client
        response.setSuspended(false);
      }

      if (status == SocketStatus.TIMEOUT) {
        if (!asyncConImpl.timeout()) {
          asyncConImpl.setErrorState(null, false);
        }
      }
      // Has an error occurred during async processing that needs to be
      // processed by the application's error page mechanism (or Tomcat's
      // if the application doesn't define one)?
      if (!request.isAsyncDispatching() && request.isAsync() && response.isErrorReportRequired()) {
        connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
      }

      if (request.isAsyncDispatching()) {
        connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
        Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
        if (t != null) {
          asyncConImpl.setErrorState(t, true);
        }
      }

      if (request.isComet()) {
        if (!response.isClosed() && !response.isError()) {
          if (request.getAvailable()
              || (request.getContentLength() > 0 && (!request.isParametersParsed()))) {
            // Invoke a read event right away if there are available bytes
            if (event(req, res, SocketStatus.OPEN_READ)) {
              comet = true;
              res.action(ActionCode.COMET_BEGIN, null);
            }
          } else {
            comet = true;
            res.action(ActionCode.COMET_BEGIN, null);
          }
        } else {
          // Clear the filter chain, as otherwise it will not be reset elsewhere
          // since this is a Comet request
          request.setFilterChain(null);
        }
      }
      if (!request.isAsync() && !comet) {
        request.finishRequest();
        response.finishResponse();
        req.action(ActionCode.POST_REQUEST, null);
        ((Context) request.getMappingData().context)
            .logAccess(request, response, System.currentTimeMillis() - req.getStartTime(), false);
      }

      // Check to see if the processor is in an error state. If it is,
      // bail out now.
      AtomicBoolean error = new AtomicBoolean(false);
      res.action(ActionCode.IS_ERROR, error);
      if (error.get()) {
        success = false;
      }
    } catch (IOException e) {
      success = false;
      // Ignore
    } catch (Throwable t) {
      ExceptionUtils.handleThrowable(t);
      success = false;
      log.error(sm.getString("coyoteAdapter.service"), t);
    } finally {
      req.getRequestProcessor().setWorkerThreadName(null);
      // Recycle the wrapper request and response
      if (!success || (!comet && !request.isAsync())) {
        request.recycle();
        response.recycle();
      } else {
        // Clear converters so that the minimum amount of memory
        // is used by this processor
        request.clearEncoders();
        response.clearEncoders();
      }
    }
    return success;
  }
  /**
   * Select the appropriate child Context to process this request, based on the specified request
   * URI. If no matching Context can be found, return an appropriate HTTP error.
   *
   * @param request Request to be processed
   * @param response Response to be produced
   * @exception IOException if an input/output error occurred
   * @exception ServletException if a servlet error occurred
   */
  @Override
  public final void invoke(Request request, Response response)
      throws IOException, ServletException {

    /*
     * xujb:
     * 和Engine一样,调用下层的context,让它来做一些处理
     * 自身也处理了一些的逻辑
     *
     * */

    // Select the Context to be used for this Request
    Context context = request.getContext();
    if (context == null) {
      response.sendError(
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString("standardHost.noContext"));
      return;
    }

    // Bind the context CL to the current thread
    if (context.getLoader() != null) {
      // Not started - it should check for availability first
      // This should eventually move to Engine, it's generic.
      if (Globals.IS_SECURITY_ENABLED) {
        PrivilegedAction<Void> pa = new PrivilegedSetTccl(context.getLoader().getClassLoader());
        AccessController.doPrivileged(pa);
      } else {
        Thread.currentThread().setContextClassLoader(context.getLoader().getClassLoader());
      }
    }
    if (request.isAsyncSupported()) {
      request.setAsyncSupported(context.getPipeline().isAsyncSupported());
    }

    boolean asyncAtStart = request.isAsync();
    boolean asyncDispatching = request.isAsyncDispatching();
    if (asyncAtStart || context.fireRequestInitEvent(request)) {

      // Ask this Context to process this request. Requests that are in
      // async mode and are not being dispatched to this resource must be
      // in error and have been routed here to check for application
      // defined error pages.
      try {
        if (!asyncAtStart || asyncDispatching) {
          context.getPipeline().getFirst().invoke(request, response);
        } else {
          // Make sure this request/response is here because an error
          // report is required.
          if (!response.isErrorReportRequired()) {
            throw new IllegalStateException(sm.getString("standardHost.asyncStateError"));
          }
        }
      } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        // If a new error occurred while trying to report a previous
        // error simply log the new error and allow the original error
        // to be reported.
        if (response.isErrorReportRequired()) {
          container.getLogger().error("Exception Processing " + request.getRequestURI(), t);
        } else {
          request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
          throwable(request, response, t);
        }
      }

      // Now that the request/response pair is back under container
      // control lift the suspension so that the error handling can
      // complete and/or the container can flush any remaining data
      response.setSuspended(false);

      Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

      // Protect against NPEs if the context was destroyed during a
      // long running request.
      if (!context.getState().isAvailable()) {
        return;
      }

      // Look for (and render if found) an application level error page
      if (response.isErrorReportRequired()) {
        if (t != null) {
          throwable(request, response, t);
        } else {
          status(request, response);
        }
      }

      if (!request.isAsync() && (!asyncAtStart || !response.isErrorReportRequired())) {
        context.fireRequestDestroyEvent(request);
      }
    }

    // Access a session (if present) to update last accessed time, based on a
    // strict interpretation of the specification
    if (ACCESS_SESSION) {
      request.getSession(false);
    }

    // Restore the context classloader
    if (Globals.IS_SECURITY_ENABLED) {
      PrivilegedAction<Void> pa = new PrivilegedSetTccl(StandardHostValve.class.getClassLoader());
      AccessController.doPrivileged(pa);
    } else {
      Thread.currentThread().setContextClassLoader(StandardHostValve.class.getClassLoader());
    }
  }