/**
  * 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);
   }
 }
  @Override
  public void handle(
      String path,
      Request request,
      HttpServletRequest httpRequest,
      HttpServletResponse httpResponse)
      throws IOException, ServletException {

    activeDispatches.inc();

    final long start;
    final HttpChannelState state = request.getHttpChannelState();
    if (state.isInitial()) {
      // new request
      activeRequests.inc();
      start = request.getTimeStamp();
    } else {
      // resumed request
      start = System.currentTimeMillis();
      activeSuspended.dec();
      if (state.getState() == State.DISPATCHED) {
        asyncDispatches.mark();
      }
    }

    try {
      super.handle(path, request, httpRequest, httpResponse);
    } finally {
      final long now = System.currentTimeMillis();
      final long dispatched = now - start;

      activeDispatches.dec();
      dispatches.update(dispatched, TimeUnit.MILLISECONDS);

      if (state.isSuspended()) {
        if (state.isInitial()) {
          state.addListener(listener);
        }
        activeSuspended.inc();
      } else if (state.isInitial()) {
        requests.update(dispatched, TimeUnit.MILLISECONDS);
        updateResponses(request);
      }
      // else onCompletion will handle it.
    }
  }