/**
   * All proprietary Comet based {@link Servlet} must invoke the timedout method when the underlying
   * WebServer time out the {@link AtmosphereResponse}. The returned value, of type {@link Action},
   * tells the proprietary Comet {@link Servlet} to resume (again), suspended or do nothing with the
   * current {@link AtmosphereResponse}.
   *
   * @param request the {@link AtmosphereRequest}
   * @param response the {@link AtmosphereResponse}
   * @return action the Action operation.
   * @throws java.io.IOException
   * @throws javax.servlet.ServletException
   */
  public Action timedout(AtmosphereRequest request, AtmosphereResponse response)
      throws IOException, ServletException {

    AtmosphereResourceImpl r = null;
    try {
      if (trackActiveRequest) {
        long l = (Long) request.getAttribute(MAX_INACTIVE);
        if (l == -1) {
          // The closedDetector closed the connection.
          return timedoutAction;
        }
        request.setAttribute(MAX_INACTIVE, (long) -1);
      }

      logger.debug("Timing out the connection for request {}", request);

      // Something went wrong.
      if (request == null || response == null) {
        logger.warn("Invalid Request/Response: {}/{}", request, response);
        return timedoutAction;
      }

      r = (AtmosphereResourceImpl) request.getAttribute(FrameworkConfig.ATMOSPHERE_RESOURCE);

      if (r != null && r.getAtmosphereResourceEvent().isSuspended()) {
        r.getAtmosphereResourceEvent().setIsResumedOnTimeout(true);

        Broadcaster b = r.getBroadcaster();
        if (b instanceof DefaultBroadcaster) {
          ((DefaultBroadcaster) b).broadcastOnResume(r);
        }

        if (request.getAttribute(ApplicationConfig.RESUMED_ON_TIMEOUT) != null) {
          r.getAtmosphereResourceEvent()
              .setIsResumedOnTimeout(
                  (Boolean) request.getAttribute(ApplicationConfig.RESUMED_ON_TIMEOUT));
        }

        invokeAtmosphereHandler(r);
      }
    } catch (Throwable t) {
      logger.error("failed to timeout resource {}", r, t);
    } finally {
      try {
        if (r != null) {
          r.notifyListeners();
          r.setIsInScope(false);
          r.cancel();
        }
      } catch (Throwable t) {
        logger.trace("timedout", t);
      } finally {

        try {
          response.getOutputStream().close();
        } catch (Throwable t) {
          try {
            response.getWriter().close();
          } catch (Throwable t2) {
          }
        }

        if (r != null) {
          destroyResource(r);
        }
      }
    }

    return timedoutAction;
  }