@Override
  protected void flushCache() throws SockJsTransportFailureException {
    while (!getMessageCache().isEmpty()) {
      String message = getMessageCache().poll();
      SockJsMessageCodec messageCodec = getSockJsServiceConfig().getMessageCodec();
      SockJsFrame frame = SockJsFrame.messageFrame(messageCodec, message);
      writeFrame(frame);

      this.byteCount += frame.getContentBytes().length + 1;
      if (logger.isTraceEnabled()) {
        logger.trace(
            this.byteCount
                + " bytes written so far, "
                + getMessageCache().size()
                + " more messages not flushed");
      }
      if (this.byteCount >= getSockJsServiceConfig().getStreamBytesLimit()) {
        if (logger.isTraceEnabled()) {
          logger.trace("Streamed bytes limit reached. Recycling current request");
        }
        resetRequest();
        this.byteCount = 0;
        break;
      }
    }
    scheduleHeartbeat();
  }
  /**
   * Handle the first request for receiving messages on a SockJS HTTP transport based session.
   *
   * <p>Long polling-based transports (e.g. "xhr", "jsonp") complete the request after writing the
   * open frame. Streaming-based transports ("xhr_streaming", "eventsource", and "htmlfile") leave
   * the response open longer for further streaming of message frames but will also close it
   * eventually after some amount of data has been sent.
   *
   * @param request the current request
   * @param response the current response
   * @param frameFormat the transport-specific SocksJS frame format to use
   */
  public void handleInitialRequest(
      ServerHttpRequest request, ServerHttpResponse response, SockJsFrameFormat frameFormat)
      throws SockJsException {

    this.uri = request.getURI();
    this.handshakeHeaders = request.getHeaders();
    this.principal = request.getPrincipal();
    this.localAddress = request.getLocalAddress();
    this.remoteAddress = request.getRemoteAddress();

    this.response = response;
    this.frameFormat = frameFormat;
    this.asyncRequestControl = request.getAsyncRequestControl(response);

    synchronized (this.responseLock) {
      try {
        // Let "our" handler know before sending the open frame to the remote handler
        delegateConnectionEstablished();
        writePrelude(request, response);
        writeFrame(SockJsFrame.openFrame());
        if (isStreaming() && !isClosed()) {
          startAsyncRequest();
        }
      } catch (Throwable ex) {
        tryCloseWithSockJsTransportError(ex, CloseStatus.SERVER_ERROR);
        throw new SockJsTransportFailureException("Failed to open session", getId(), ex);
      }
    }
  }
  protected void handleRequestInternal(
      ServerHttpRequest request,
      ServerHttpResponse response,
      AbstractHttpSockJsSession sockJsSession)
      throws SockJsException {

    if (sockJsSession.isNew()) {
      if (logger.isDebugEnabled()) {
        logger.debug(request.getMethod() + " " + request.getURI());
      }
      sockJsSession.handleInitialRequest(request, response, getFrameFormat(request));
    } else if (sockJsSession.isClosed()) {
      if (logger.isDebugEnabled()) {
        logger.debug("Connection already closed (but not removed yet) for " + sockJsSession);
      }
      SockJsFrame frame = SockJsFrame.closeFrameGoAway();
      try {
        response.getBody().write(frame.getContentBytes());
      } catch (IOException ex) {
        throw new SockJsException("Failed to send " + frame, sockJsSession.getId(), ex);
      }
    } else if (!sockJsSession.isActive()) {
      if (logger.isTraceEnabled()) {
        logger.trace("Starting " + getTransportType() + " async request.");
      }
      sockJsSession.handleSuccessiveRequest(request, response, getFrameFormat(request));
    } else {
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Another " + getTransportType() + " connection still open for " + sockJsSession);
      }
      String formattedFrame =
          getFrameFormat(request).format(SockJsFrame.closeFrameAnotherConnectionOpen());
      try {
        response.getBody().write(formattedFrame.getBytes(SockJsFrame.CHARSET));
      } catch (IOException ex) {
        throw new SockJsException("Failed to send " + formattedFrame, sockJsSession.getId(), ex);
      }
    }
  }
  @Override
  protected void handleRequestInternal(
      ServerHttpRequest request, ServerHttpResponse response, boolean initialRequest)
      throws IOException {

    byte[] prelude = getPrelude(request);
    Assert.notNull(prelude);
    response.getBody().write(prelude);
    response.flush();

    if (initialRequest) {
      writeFrame(SockJsFrame.openFrame());
    }
    flushCache();
  }
  /**
   * Handle all requests, except the first one, to receive messages on a SockJS HTTP transport based
   * session.
   *
   * <p>Long polling-based transports (e.g. "xhr", "jsonp") complete the request after writing any
   * buffered message frames (or the next one). Streaming-based transports ("xhr_streaming",
   * "eventsource", and "htmlfile") leave the response open longer for further streaming of message
   * frames but will also close it eventually after some amount of data has been sent.
   *
   * @param request the current request
   * @param response the current response
   * @param frameFormat the transport-specific SocksJS frame format to use
   */
  public void handleSuccessiveRequest(
      ServerHttpRequest request, ServerHttpResponse response, SockJsFrameFormat frameFormat)
      throws SockJsException {

    synchronized (this.responseLock) {
      try {
        if (isClosed()) {
          response.getBody().write(SockJsFrame.closeFrameGoAway().getContentBytes());
        }
        this.response = response;
        this.frameFormat = frameFormat;
        this.asyncRequestControl = request.getAsyncRequestControl(response);
        writePrelude(request, response);
        startAsyncRequest();
      } catch (Throwable ex) {
        tryCloseWithSockJsTransportError(ex, CloseStatus.SERVER_ERROR);
        throw new SockJsTransportFailureException(
            "Failed to handle SockJS receive request", getId(), ex);
      }
    }
  }