@Override
  public AtmosphereResource suspend(long timeout) {

    if (event.isSuspended() || disableSuspend) return this;

    if (config.isSupportSession()
        && req.getSession(false) != null
        && req.getSession().getMaxInactiveInterval() != -1
        && req.getSession().getMaxInactiveInterval() * 1000 < timeout) {
      throw new IllegalStateException(
          "Cannot suspend a "
              + "response longer than the session timeout. Increase the value of session-timeout in web.xml");
    }

    if (Utils.resumableTransport(transport())) {
      resumeOnBroadcast.set(true);
    }

    onPreSuspend(event);

    // Recheck based on preSuspend
    if (event.isSuspended() || disableSuspend) return this;

    if (!event.isResumedOnTimeout()) {

      Enumeration<String> connection = req.getHeaders("Connection");
      if (connection == null) {
        connection = req.getHeaders("connection");
      }

      if (connection != null && connection.hasMoreElements()) {
        String[] e = connection.nextElement().toString().split(",");
        for (String upgrade : e) {
          if (upgrade.trim().equalsIgnoreCase(WEBSOCKET_UPGRADE)) {
            if (!asyncSupport.supportWebSocket()) {
              response.addHeader(X_ATMOSPHERE_ERROR, "Websocket protocol not supported");
            } else {
              req.setAttribute(FrameworkConfig.TRANSPORT_IN_USE, HeaderConfig.WEBSOCKET_TRANSPORT);
            }
          }
        }
      }

      if (req.getHeader(X_ATMOSPHERE_TRANSPORT) == null) {
        req.setAttribute(FrameworkConfig.TRANSPORT_IN_USE, HeaderConfig.LONG_POLLING_TRANSPORT);
      }

      req.setAttribute(PRE_SUSPEND, "true");
      action.type(Action.TYPE.SUSPEND);
      action.timeout(timeout);

      // TODO: We can possibly optimize that call by avoiding creating a Broadcaster if we are sure
      // the Broadcaster
      // is unique.
      boolean isJersey = req.getAttribute(FrameworkConfig.CONTAINER_RESPONSE) != null;

      boolean skipCreation = false;
      if (req.getAttribute(SKIP_BROADCASTER_CREATION) != null) {
        skipCreation = true;
      }

      // Null means SCOPE=REQUEST set by a Meteor
      if (!skipCreation
          && (broadcaster == null || broadcaster.getScope() == Broadcaster.SCOPE.REQUEST)
          && !isJersey) {
        String id = broadcaster != null ? broadcaster.getID() : "/*";
        Class<? extends Broadcaster> clazz =
            broadcaster != null ? broadcaster.getClass() : DefaultBroadcaster.class;

        broadcaster = config.getBroadcasterFactory().lookup(clazz, id, false);
        if (broadcaster == null || broadcaster.getAtmosphereResources().size() > 0) {
          broadcaster =
              config.getBroadcasterFactory().lookup(clazz, id + "/" + UUID.randomUUID(), true);
        }
      }

      broadcaster.addAtmosphereResource(this);
      if (req.getAttribute(DefaultBroadcaster.CACHED) != null
          && transport() != null
          && Utils.resumableTransport(transport())) {
        action.type(Action.TYPE.CONTINUE);
        // Do nothing because we have found cached message which was written already, and the
        // handler resumed.
        logger.debug("Cached message found, not suspending {}", uuid());
        return this;
      }
      req.removeAttribute(PRE_SUSPEND);
      notifyListeners();
    }
    return this;
  }
  @Override
  public Action inspect(final AtmosphereResource ar) {
    final AtmosphereResourceImpl r = AtmosphereResourceImpl.class.cast(ar);
    final AtmosphereRequest request = r.getRequest(false);
    final AtmosphereResponse response = r.getResponse(false);

    String uuid = request.getHeader(HeaderConfig.X_ATMOSPHERE_TRACKING_ID);
    String handshakeUUID = request.getHeader(HeaderConfig.X_ATMO_PROTOCOL);
    if (uuid != null && uuid.equals("0") && handshakeUUID != null) {
      request.header(HeaderConfig.X_ATMO_PROTOCOL, null);
      // Since 1.0.10

      final StringBuffer message =
          new StringBuffer(r.uuid()).append(wsDelimiter).append(System.currentTimeMillis());

      // https://github.com/Atmosphere/atmosphere/issues/993
      boolean track = false;
      if (r.getBroadcaster().getBroadcasterConfig().hasFilters()) {
        for (BroadcastFilter bf : r.getBroadcaster().getBroadcasterConfig().filters()) {
          if (TrackMessageSizeFilter.class.isAssignableFrom(bf.getClass())) {
            track = true;
            break;
          }
        }
      }

      final AtomicReference<String> protocolMessage =
          new AtomicReference<String>(message.toString());
      if (track) {
        protocolMessage.set(
            (String) f.filter(r, protocolMessage.get(), protocolMessage.get()).message());
      }

      if (!Utils.resumableTransport(r.transport())) {
        OnSuspend a =
            new OnSuspend() {
              @Override
              public void onSuspend(AtmosphereResourceEvent event) {
                response.write(protocolMessage.get());
                try {
                  response.flushBuffer();
                } catch (IOException e) {
                  logger.trace("", e);
                }
              }
            };
        // Pass the information to Servlet Based Framework
        request.setAttribute(CALLBACK_JAVASCRIPT_PROTOCOL, a);
        r.addEventListener(a);
      } else {
        response.write(protocolMessage.get());
      }

      // We don't need to reconnect here
      if (r.transport() == AtmosphereResource.TRANSPORT.WEBSOCKET
          || r.transport() == AtmosphereResource.TRANSPORT.STREAMING
          || r.transport() == AtmosphereResource.TRANSPORT.SSE) {
        return Action.CONTINUE;
      } else {
        return Action.SKIP_ATMOSPHEREHANDLER;
      }
    }
    return Action.CONTINUE;
  }