/* Handle a request from a connection.
   * Called to handle a request on the connection when either the header has been received,
   * or after the entire request has been received (for short requests of known length), or
   * on the dispatch of an async request.
   */
  public void handleAsync(AbstractHttpConnection connection) throws IOException, ServletException {
    final AsyncContinuation async = connection.getRequest().getAsyncContinuation();
    final AsyncContinuation.AsyncEventState state = async.getAsyncEventState();

    final Request baseRequest = connection.getRequest();
    final String path = state.getPath();

    if (path != null) {
      // this is a dispatch with a path
      final String contextPath = state.getServletContext().getContextPath();
      HttpURI uri = new HttpURI(URIUtil.addPaths(contextPath, path));
      baseRequest.setUri(uri);
      baseRequest.setRequestURI(null);
      baseRequest.setPathInfo(baseRequest.getRequestURI());
      if (uri.getQuery() != null) baseRequest.mergeQueryString(uri.getQuery());
    }

    final String target = baseRequest.getPathInfo();
    final HttpServletRequest request = (HttpServletRequest) async.getRequest();
    final HttpServletResponse response = (HttpServletResponse) async.getResponse();

    if (LOG.isDebugEnabled()) {
      LOG.debug("REQUEST " + target + " on " + connection);
      handle(target, baseRequest, request, response);
      LOG.debug("RESPONSE " + target + "  " + connection.getResponse().getStatus());
    } else handle(target, baseRequest, request, response);
  }
  /* Handle a request from a connection.
   * Called to handle a request on the connection when either the header has been received,
   * or after the entire request has been received (for short requests of known length), or
   * on the dispatch of an async request.
   */
  public void handle(AbstractHttpConnection connection) throws IOException, ServletException {
    final String target = connection.getRequest().getPathInfo();
    final Request request = connection.getRequest();
    final Response response = connection.getResponse();

    if (LOG.isDebugEnabled()) {
      LOG.debug("REQUEST " + target + " on " + connection);
      handle(target, request, request, response);
      LOG.debug("RESPONSE " + target + "  " + connection.getResponse().getStatus());
    } else handle(target, request, request, response);
  }
  /* ------------------------------------------------------------ */
  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);
      }
    }
  }