Package org.jboss.aerogear.io.netty.handler.codec.sockjs.protocol

Source Code of org.jboss.aerogear.io.netty.handler.codec.sockjs.protocol.SockJsProtocolTest

/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License, version
* 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.jboss.aerogear.io.netty.handler.codec.sockjs.protocol;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandlerInvoker;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.http.ClientCookieEncoder;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
import io.netty.util.CharsetUtil;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.AbstractSockJsServiceFactory;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.CloseService;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.EchoService;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.SockJsConfig;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.SockJsService;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.SockJsServiceFactory;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.SockJsSessionContext;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.SockJsTestUtil;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.handler.CorsInboundHandler;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.handler.CorsOutboundHandler;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.handler.SockJsHandler;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.transport.EventSourceTransport;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.transport.Transports;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.util.ChannelUtil;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.util.HttpUtil;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.util.JsonUtil;
import org.jboss.aerogear.io.netty.handler.codec.sockjs.util.StubEmbeddedEventLoop;
import org.junit.Test;

import java.net.SocketAddress;
import java.util.UUID;
import java.util.regex.Pattern;

import static io.netty.handler.codec.http.HttpHeaders.Names.*;
import static io.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET;
import static io.netty.handler.codec.http.HttpHeaders.Values.KEEP_ALIVE;
import static io.netty.handler.codec.http.HttpMethod.*;
import static io.netty.handler.codec.http.HttpVersion.*;
import static io.netty.handler.codec.http.websocketx.WebSocketVersion.*;
import static io.netty.util.CharsetUtil.*;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class SockJsProtocolTest {

    private static final Pattern SEMICOLON = Pattern.compile(";");

    /*
     * Equivalent to BaseUrlGreeting.test_greeting in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void baseUrlGreetingTestGreeting() throws Exception {
        final SockJsServiceFactory factory = echoService();
        final EmbeddedChannel ch = channelForMockService(factory.config());
        ch.writeInbound(httpRequest(factory.config().prefix()));
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus().code(), is(HttpResponseStatus.OK.code()));
        assertThat(response.headers().get(CONTENT_TYPE), equalTo("text/plain; charset=UTF-8"));
        assertThat(response.content().toString(UTF_8), equalTo("Welcome to SockJS!\n"));
        verifyNoSET_COOKIE(response);
    }

    /*
     * Equivalent to BaseUrlGreeting.test_notFound in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void baseUrlGreetingTestNotFound() throws Exception {
        assertNotFoundResponse("/echo", "/a");
        assertNotFoundResponse("/echo", "/a.html");
        assertNotFoundResponse("/echo", "//");
        assertNotFoundResponse("/echo", "///");
        assertNotFoundResponse("/echo", "//a");
        assertNotFoundResponse("/echo", "/a/a/");
        assertNotFoundResponse("/echo", "/a/");
    }

    /*
     * Equivalent to IframePage.test_simpleUrl in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void iframePageSimpleUrl() throws Exception {
        verifyIframe("/echo", "/iframe.html");
    }

    /*
     * Equivalent to IframePage.test_versionedUrl in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void iframePageTestVersionedUrl() {
        verifyIframe("/echo", "/iframe-a.html");
        verifyIframe("/echo", "/iframe-.html");
        verifyIframe("/echo", "/iframe-0.1.2.html");
        verifyIframe("/echo", "/iframe-0.1.2.abc-dirty.2144.html");
    }

    /*
     * Equivalent to IframePage.test_queriedUrl in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void iframePageTestQueriedUrl() {
        verifyIframe("/echo", "/iframe-a.html?t=1234");
        verifyIframe("/echo", "/iframe-0.1.2.html?t=123414");
        verifyIframe("/echo", "/iframe-0.1.2abc-dirty.2144.html?t=qweqweq123");
    }

    /*
     * Equivalent to IframePage.test_invalidUrl in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void iframePageTestInvalidUrl() {
        assertNotFoundResponse("/echo", "/iframe.htm");
        assertNotFoundResponse("/echo", "/iframe");
        assertNotFoundResponse("/echo", "/IFRAME.HTML");
        assertNotFoundResponse("/echo", "/IFRAME");
        assertNotFoundResponse("/echo", "/iframe.HTML");
        assertNotFoundResponse("/echo", "/iframe.xml");
        assertNotFoundResponse("/echo", "/iframe-/.html");
    }

    /*
     * Equivalent to IframePage.test_cacheability in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void iframeCachability() throws Exception {
        final SockJsServiceFactory factory = echoService();
        EmbeddedChannel ch = channelForMockService(factory.config());
        ch.writeInbound(httpRequest(factory.config().prefix() + "/iframe.html"));
        final String etag1 = getEtag((FullHttpResponse) ch.readOutbound());

        ch = channelForMockService(factory.config());
        ch.writeInbound(httpRequest(factory.config().prefix() + "/iframe.html"));
        final String etag2 = getEtag((FullHttpResponse) ch.readOutbound());
        assertThat(etag1, equalTo(etag2));

        final FullHttpRequest requestWithEtag = httpRequest(factory.config().prefix() + "/iframe.html");
        requestWithEtag.headers().set(IF_NONE_MATCH, etag1);
        ch = channelForMockService(factory.config());
        ch.writeInbound(requestWithEtag);
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.NOT_MODIFIED));
        assertThat(response.headers().get(CONTENT_TYPE), is(nullValue()));
        assertThat(response.content().isReadable(), is(false));
    }

    /*
     * Equivalent to InfoTest.test_basic in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void infoTestBasic() throws Exception {
        final SockJsServiceFactory factory = echoService();
        final FullHttpResponse response = infoForMockService(factory);
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        verifyContentType(response, "application/json; charset=UTF-8");
        verifyNoSET_COOKIE(response);
        verifyNotCached(response);
        SockJsTestUtil.assertCORSHeaders(response, "*");

        final JsonNode json = contentAsJson(response);
        assertThat(json.get("websocket").asBoolean(), is(true));
        assertThat(json.get("cookie_needed").asBoolean(), is(true));
        assertThat(json.get("origins").get(0).asText(), is("*:*"));
        assertThat(json.get("entropy").asLong(), is(notNullValue()));
    }

    /*
     * Equivalent to InfoTest.test_entropy in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void infoTestEntropy() throws Exception {
        final SockJsServiceFactory factory = echoService();
        final FullHttpResponse response1 = infoForMockService(factory);
        final FullHttpResponse response2 = infoForMockService(factory);
        assertThat(getEntropy(response1) != getEntropy(response2), is(true));
    }

    /*
     * Equivalent to InfoTest.test_options in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void infoTestOptions() throws Exception {
        final SockJsServiceFactory factory = echoService();
        final EmbeddedChannel ch = channelForMockService(factory.config());
        ch.writeInbound(httpRequest(factory.config().prefix(), OPTIONS));
        final HttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), is(HttpResponseStatus.NO_CONTENT));
        assertCORSPreflightResponseHeaders(response);
        SockJsTestUtil.assertCORSHeaders(response, "*");
    }

    /*
     * Equivalent to InfoTest.test_options_null_origin in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void infoTestOptionsNullOrigin() throws Exception {
        final SockJsServiceFactory factory = echoService();
        final EmbeddedChannel ch = channelForMockService(factory.config());
        final FullHttpRequest request = httpRequest(factory.config().prefix() + "/info", OPTIONS);
        request.headers().set(ORIGIN, "null");
        ch.writeInbound(request);
        final HttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), is(HttpResponseStatus.NO_CONTENT));
        assertCORSPreflightResponseHeaders(response);
        SockJsTestUtil.assertCORSHeaders(response, "*");
    }

    /*
     * Equivalent to InfoTest.test_disabled_websocket in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void infoTestDisabledWebsocket() throws Exception {
        final SockJsServiceFactory factory = echoService(SockJsConfig.withPrefix("echo").disableWebSocket().build());
        final EmbeddedChannel ch = channelForMockService(factory.config());
        ch.writeInbound(httpRequest(factory.config().prefix() + "/info"));
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(contentAsJson(response).get("websocket").asBoolean(), is(false));
    }

    /*
     * Equivalent to SessionURLs.test_anyValue in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void sessionUrlsTestAnyValue() throws Exception {
        assertOKResponse("/a/a");
        assertOKResponse("/_/_");
        assertOKResponse("/1/1");
        assertOKResponse("/abcdefgh_i-j%20/abcdefg_i-j%20");
    }

    /*
     * Equivalent to SessionURLs.test_invalidPaths in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void sessionUrlsTestInvalidPaths() throws Exception {
        assertNotFoundResponse("echo", "//");
        assertNotFoundResponse("echo", "/a./a");
        assertNotFoundResponse("echo", "/a/a.");
        assertNotFoundResponse("echo", "/./.");
        assertNotFoundResponse("echo", "/");
        assertNotFoundResponse("echo", "///");
    }

    /*
     * Equivalent to SessionURLs.test_ignoringServerId in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void sessionUrlsTestIgnoringServerId() throws Exception {
        final SockJsServiceFactory factory = echoService();
        final String sessionId = UUID.randomUUID().toString();
        final String sessionUrl = factory.config().prefix() + "/000/" + sessionId;

        final FullHttpResponse openSessionResponse = xhrRequest(sessionUrl, factory);
        assertOpenFrameResponse(openSessionResponse);

        final FullHttpResponse sendResponse = xhrSendRequest(sessionUrl, "[\"a\"]", factory);
        assertNoContent(sendResponse);

        final FullHttpResponse pollResponse = xhrRequest(factory.config().prefix() + "/999/" + sessionId, factory);
        assertMessageFrameContent(pollResponse, "a");
    }

    /*
     * Equivalent to Protocol.test_simpleSession in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void protocolTestSimpleSession() throws Exception {
        final SockJsServiceFactory factory = echoService();
        final String sessionUrl = factory.config().prefix() + "/111/" + UUID.randomUUID().toString();

        final FullHttpResponse openSessionResponse = xhrRequest(sessionUrl, factory);
        assertOpenFrameResponse(openSessionResponse);

        final FullHttpResponse sendResponse = xhrSendRequest(sessionUrl, "[\"a\"]", factory);
        assertNoContent(sendResponse);

        final FullHttpResponse pollResponse = xhrRequest(sessionUrl, factory);
        assertMessageFrameContent(pollResponse, "a");

        final FullHttpResponse badSessionResponse = xhrSendRequest("/echo/111/badsession", "[\"a\"]", factory);
        assertThat(badSessionResponse.getStatus(), is(HttpResponseStatus.NOT_FOUND));
    }

    /*
     * Equivalent to Protocol.test_closeSession in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void protocolTestCloseSession() throws Exception {
        final SockJsServiceFactory closefactory = closeService();
        final String sessionUrl = closefactory.config().prefix() + "/222/" + UUID.randomUUID().toString();

        final FullHttpResponse openSessionResponse = xhrRequest(sessionUrl, closefactory);
        assertOpenFrameResponse(openSessionResponse);
        assertGoAwayResponse(xhrRequest(sessionUrl, closefactory));
        assertGoAwayResponse(xhrRequest(sessionUrl, closefactory));
    }

    /*
     * Equivalent to WebSocketHttpErrors.test_httpMethod in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHttpErrorsTestHttpMethod() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch);

        final FullHttpRequest request = httpRequest(sessionUrl + "/websocket");
        ch.writeInbound(request);
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.BAD_REQUEST));
        assertThat(response.content().toString(UTF_8), equalTo("Can \"Upgrade\" only to \"WebSocket\"."));
    }

    /*
     * Equivalent to WebSocketHttpErrors.test_invalidConnectionHeader in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHttpErrorsTestInvalidConnectionHeader() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch);

        final FullHttpRequest request = webSocketUpgradeRequest(sessionUrl + "/websocket");
        request.headers().set(UPGRADE, "websocket");
        request.headers().set(CONNECTION, "close");
        ch.writeInbound(request);
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.BAD_REQUEST));
        assertThat(response.content().toString(UTF_8), equalTo("\"Connection\" must be \"Upgrade\"."));
    }

    /*
     * Equivalent to WebsocketHttpErrors.test_invalidMethod in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHttpErrorsTestInvalidMethod() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch);

        final FullHttpRequest request = webSocketUpgradeRequest(sessionUrl + "/websocket");
        request.setMethod(POST);
        ch.writeInbound(request);
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.METHOD_NOT_ALLOWED));
    }

    /*
     * Equivalent to WebsocketHixie76.test_transport in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHixie76TestTransport() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = wsChannelForService(echoFactory);

        final FullHttpRequest request = webSocketUpgradeRequest(sessionUrl + "/websocket", WebSocketVersion.V00);
        ch.writeInbound(request);

        final FullHttpResponse upgradeResponse = HttpUtil.decodeFullHttpResponse(ch);
        assertThat(upgradeResponse.content().toString(UTF_8), equalTo("8jKS'y:G*Co,Wxa-"));

        final TextWebSocketFrame openFrame = ch.readOutbound();
        assertThat(openFrame.content().toString(UTF_8), equalTo("o"));
        ch.readOutbound();

        final TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame("\"a\"");
        ch.writeInbound(textWebSocketFrame);

        final TextWebSocketFrame textFrame = ch.readOutbound();
        assertThat(textFrame.content().toString(UTF_8), equalTo("a[\"a\"]"));
    }

    /*
     * Equivalent to WebsocketHixie76.test_close in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHixie76TestClose() throws Exception {
        final String serviceName = "/close";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).build();
        final SockJsServiceFactory service = closeService(config);
        final EmbeddedChannel ch = wsChannelForService(service);

        final FullHttpRequest request = webSocketUpgradeRequest(sessionUrl + "/websocket", WebSocketVersion.V00);
        ch.writeInbound(request);

        final FullHttpResponse upgradeResponse = HttpUtil.decodeFullHttpResponse(ch);
        assertThat(upgradeResponse.content().toString(UTF_8), equalTo("8jKS'y:G*Co,Wxa-"));

        final TextWebSocketFrame openFrame = ch.readOutbound();
        assertThat(openFrame.content().toString(UTF_8), equalTo("o"));

        final TextWebSocketFrame closeFrame = ch.readOutbound();
        assertThat(closeFrame.content().toString(UTF_8), equalTo("c[3000,\"Go away!\"]"));
        assertThat(ch.isActive(), is(false));
        webSocketTestClose(V13);
    }

    /*
     * Equivalent to WebsocketHixie76.test_empty_frame in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHixie76TestEmptyFrame() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = wsChannelForService(echoFactory);

        final FullHttpRequest request = webSocketUpgradeRequest(sessionUrl + "/websocket", WebSocketVersion.V00);
        ch.writeInbound(request);

        final FullHttpResponse upgradeResponse = HttpUtil.decodeFullHttpResponse(ch);
        assertThat(upgradeResponse.content().toString(UTF_8), equalTo("8jKS'y:G*Co,Wxa-"));

        final TextWebSocketFrame openFrame = ch.readOutbound();
        assertThat(openFrame.content().toString(UTF_8), equalTo("o"));

        final TextWebSocketFrame emptyWebSocketFrame = new TextWebSocketFrame("");
        ch.writeInbound(emptyWebSocketFrame);

        final TextWebSocketFrame webSocketFrame = new TextWebSocketFrame("\"a\"");
        ch.writeInbound(webSocketFrame);

        final TextWebSocketFrame textFrame = ch.readOutbound();
        assertThat(textFrame.content().toString(UTF_8), equalTo("a[\"a\"]"));
    }

    /*
     * Equivalent to WebsocketHixie76.test_reuseSessionId in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHixie76TestReuseSessionId() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch1 = wsChannelForService(echoFactory);
        final EmbeddedChannel ch2 = wsChannelForService(echoFactory);

        ch1.writeInbound(webSocketUpgradeRequest(sessionUrl + "/websocket", WebSocketVersion.V00));
        final FullHttpResponse upgradeResponse = HttpUtil.decodeFullHttpResponse(ch1);
        assertThat(upgradeResponse.content().toString(UTF_8), equalTo("8jKS'y:G*Co,Wxa-"));

        ch2.writeInbound(webSocketUpgradeRequest(sessionUrl + "/websocket", WebSocketVersion.V00));
        final FullHttpResponse upgradeResponse2 = HttpUtil.decodeFullHttpResponse(ch2);
        assertThat(upgradeResponse2.content().toString(UTF_8), equalTo("8jKS'y:G*Co,Wxa-"));

        assertThat(((ByteBufHolder) ch1.readOutbound()).content().toString(UTF_8), equalTo("o"));
        assertThat(((ByteBufHolder) ch2.readOutbound()).content().toString(UTF_8), equalTo("o"));

        ch1.writeInbound(new TextWebSocketFrame("\"a\""));
        assertThat(((ByteBufHolder) ch1.readOutbound()).content().toString(UTF_8), equalTo("a[\"a\"]"));

        ch2.writeInbound(new TextWebSocketFrame("\"b\""));
        assertThat(((ByteBufHolder) ch2.readOutbound()).content().toString(UTF_8), equalTo("a[\"b\"]"));

        ch1.close();
        ch2.close();

        final EmbeddedChannel newCh = wsChannelForService(echoFactory);

        newCh.writeInbound(webSocketUpgradeRequest(sessionUrl + "/websocket"));
        final FullHttpResponse upgradeResponse3 = HttpUtil.decodeFullHttpResponse(newCh);
        assertThat(upgradeResponse3.getStatus(), equalTo(HttpResponseStatus.SWITCHING_PROTOCOLS));

        assertThat(((ByteBufHolder) readOutboundDiscardEmpty(newCh)).content().toString(UTF_8), equalTo("o"));
        newCh.writeInbound(new TextWebSocketFrame("\"a\""));
        assertThat(((ByteBufHolder) newCh.readOutbound()).content().toString(UTF_8), equalTo("a[\"a\"]"));
        newCh.close();
    }

    /*
     * Equivalent to WebsocketHixie76.test_haproxy in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHixie76TestHAProxy() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = wsChannelForService(echoFactory);

        final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, sessionUrl + "/websocket");
        request.headers().set(HOST, "server.test.com");
        request.headers().set(UPGRADE, WEBSOCKET.toString());
        request.headers().set(CONNECTION, "Upgrade");
        request.headers().set(CONNECTION, "Upgrade");
        request.headers().set(SEC_WEBSOCKET_KEY1, "4 @1  46546xW%0l 1 5");
        request.headers().set(SEC_WEBSOCKET_KEY2, "12998 5 Y3 1  .P00");
        request.headers().set(ORIGIN, "http://example.com");
        ch.writeInbound(request);

        final HttpResponse upgradeResponse = HttpUtil.decode(ch);
        assertThat(upgradeResponse.getStatus(), equalTo(HttpResponseStatus.SWITCHING_PROTOCOLS));

        ch.writeInbound(Unpooled.copiedBuffer("^n:ds[4U", CharsetUtil.US_ASCII));

        final ByteBuf key = (ByteBuf) readOutboundDiscardEmpty(ch);
        assertThat(key.toString(CharsetUtil.US_ASCII), equalTo("8jKS'y:G*Co,Wxa-"));

        ch.readOutbound();
        final ByteBuf openFrame = ch.readOutbound();
        assertThat(openFrame.toString(UTF_8), equalTo("o"));

        final TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame("\"a\"");
        ch.writeInbound(textWebSocketFrame);

        ch.readOutbound();
        ch.readOutbound();
        final ByteBuf textFrame = ch.readOutbound();
        assertThat(textFrame.toString(UTF_8), equalTo("a[\"a\"]"));
    }

    /*
     * Equivalent to WebsocketHixie76.test_broken_json in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHixie76TestBrokenJSON() throws Exception {
        final String serviceName = "/close";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).build();
        final EmbeddedChannel ch = wsChannelForService(echoService(config));

        final FullHttpRequest request = webSocketUpgradeRequest(sessionUrl + "/websocket", WebSocketVersion.V00);
        ch.writeInbound(request);
        final FullHttpResponse upgradeResponse = HttpUtil.decodeFullHttpResponse(ch);
        assertThat(upgradeResponse.getStatus(), equalTo(HttpResponseStatus.SWITCHING_PROTOCOLS));
        assertThat(upgradeResponse.content().toString(UTF_8), equalTo("8jKS'y:G*Co,Wxa-"));
        assertThat(((ByteBufHolder) ch.readOutbound()).content().toString(UTF_8), equalTo("o"));

        final TextWebSocketFrame webSocketFrame = new TextWebSocketFrame("[\"a\"");
        ch.writeInbound(webSocketFrame);
        assertThat(ch.isActive(), is(false));
        webSocketTestBrokenJSON(V13);
    }

    /*
     * Equivalent to WebsocketHybi10.test_transport in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHybi10TestTransport() throws Exception {
        webSocketTestTransport(WebSocketVersion.V08);
    }

    /*
     * Equivalent to WebsocketHybi10.test_close in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHybi10TestClose() throws Exception {
        webSocketTestClose(WebSocketVersion.V08);
    }

    /*
     * Equivalent to WebsocketHybi10.test_headersSantity in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHybi10TestHeadersSanity() throws Exception {
        verifyHeaders(WebSocketVersion.V07);
        verifyHeaders(WebSocketVersion.V08);
        verifyHeaders(V13);
    }

    /*
     * Equivalent to WebsocketHybi10.test_broken_json in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHybi10TestBrokenJSON() throws Exception {
        webSocketTestBrokenJSON(WebSocketVersion.V08);
    }

    /*
     * Equivalent to WebsocketHybi10.test_transport, but for Hybi17, in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHybi17TestTransport() throws Exception {
        webSocketTestTransport(WebSocketVersion.V13);
    }

    /*
     * Equivalent to WebsocketHybi10.test_close, but for Hybi17, in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHybi17TestClose() throws Exception {
        webSocketTestClose(WebSocketVersion.V13);
    }

    /*
     * Equivalent to WebsocketHybi10.test_broken_json, but for Hybi17, in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHybi17TestBrokenJSON() throws Exception {
        webSocketTestBrokenJSON(WebSocketVersion.V13);
    }

    /*
     * Equivalent to WebsocketHybi10.test_firefox_602_connection_header in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void webSocketHybi10Firefox602ConnectionHeader() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final EmbeddedChannel ch = ChannelUtil.webSocketChannel(echoFactory.config());
        final FullHttpRequest request = HttpUtil.webSocketUpgradeRequest("/websocket", WebSocketVersion.V08);
        request.headers().set(CONNECTION, "keep-alive, Upgrade");
        ch.writeInbound(request);
        final HttpResponse response = HttpUtil.decode(ch);
        assertThat(response.getStatus(), is(HttpResponseStatus.SWITCHING_PROTOCOLS));
        assertThat(response.headers().get(CONNECTION), equalTo("Upgrade"));
    }

    /*
     * Equivalent to XhrPolling.test_options in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void xhrPollingTestOptions() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();
        final FullHttpRequest xhrRequest = httpRequest(sessionUrl + "/xhr", OPTIONS);
        final HttpResponse xhrOptionsResponse = xhrRequest(xhrRequest, echoFactory);
        assertCORSPreflightResponseHeaders(xhrOptionsResponse);

        final FullHttpRequest xhrSendRequest = httpRequest(sessionUrl + "/xhr_send", OPTIONS);
        final HttpResponse xhrSendOptionsResponse = xhrRequest(xhrSendRequest, echoFactory);
        assertCORSPreflightResponseHeaders(xhrSendOptionsResponse);
    }

    /*
     * Equivalent to XhrPolling.test_transport in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void xhrPollingTestTransport() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();

        final FullHttpResponse response = xhrRequest(sessionUrl, echoFactory);
        assertOpenFrameResponse(response);
        assertThat(response.headers().get(CONTENT_TYPE), equalTo(Transports.CONTENT_TYPE_JAVASCRIPT));
        SockJsTestUtil.assertCORSHeaders(response, "*");
        verifyNotCached(response);

        final FullHttpResponse xhrSendResponse = xhrSendRequest(sessionUrl, "[\"x\"]", echoFactory);
        assertNoContent(xhrSendResponse);
        assertThat(xhrSendResponse.headers().get(CONTENT_TYPE), equalTo(Transports.CONTENT_TYPE_PLAIN));
        SockJsTestUtil.assertCORSHeaders(response, "*");
        verifyNotCached(xhrSendResponse);

        final FullHttpResponse pollResponse = xhrRequest(sessionUrl, echoFactory);
        assertMessageFrameContent(pollResponse, "x");
    }

    @Test
    public void xhrPollingSessionReuse() throws Exception {
        final SockJsServiceFactory echoFactory = singletonEchoService();
        final String sessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();
        assertOpenFrameResponse(xhrRequest(sessionUrl, echoFactory));
        assertNoContent(xhrSendRequest(sessionUrl, "[\"x\"]", echoFactory));
        assertMessageFrameContent(xhrRequest(sessionUrl, echoFactory), "x");
        xhrRequest(sessionUrl, echoFactory);
        assertNoContent(xhrSendRequest(sessionUrl, "[\"x\"]", echoFactory));
        assertMessageFrameContent(xhrRequest(sessionUrl, echoFactory), "x");
    }

    /*
     * Equivalent to XhrPolling.test_invalid_session in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void xhrPollingTestInvalidSession() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();
        final FullHttpResponse xhrSendResponse = xhrSendRequest(sessionUrl, "[\"x\"]", echoFactory);
        assertThat(xhrSendResponse.getStatus(), is(HttpResponseStatus.NOT_FOUND));
    }

    /*
     * Equivalent to XhrPolling.test_invalid_json sockjs-protocol-0.3.3.py.
     */
    @Test
    public void xhrPollingTestInvalidJson() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();

        assertOpenFrameResponse(xhrRequest(sessionUrl, echoFactory));

        final FullHttpResponse xhrSendResponse = xhrSendRequest(sessionUrl, "[\"x\"", echoFactory);
        assertThat(xhrSendResponse.getStatus(), is(HttpResponseStatus.INTERNAL_SERVER_ERROR));
        assertThat(xhrSendResponse.content().toString(UTF_8), equalTo("Broken JSON encoding."));

        final FullHttpResponse noPayloadResponse = xhrSendRequest(sessionUrl, "", echoFactory);
        assertThat(noPayloadResponse.getStatus(), is(HttpResponseStatus.INTERNAL_SERVER_ERROR));
        assertThat(noPayloadResponse.content().toString(UTF_8), equalTo("Payload expected."));

        final FullHttpResponse validSend = xhrSendRequest(sessionUrl, "[\"a\"]", echoFactory);
        assertThat(validSend.getStatus(), is(HttpResponseStatus.NO_CONTENT));

        final FullHttpResponse pollResponse = xhrRequest(sessionUrl, echoFactory);
        assertMessageFrameContent(pollResponse, "a");
    }

    /*
     * Equivalent to XhrPolling.test_content_types sockjs-protocol-0.3.3.py.
     */
    @Test
    public void xhrPollingTestContentTypes() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();

        final FullHttpResponse response = xhrRequest(sessionUrl, echoFactory);
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.content().toString(UTF_8), equalTo("o\n"));

        final FullHttpResponse textPlain = xhrSendRequest(sessionUrl, "[\"a\"]", "text/plain", echoFactory);
        assertThat(textPlain.getStatus(), is(HttpResponseStatus.NO_CONTENT));

        final FullHttpResponse json = xhrSendRequest(sessionUrl, "[\"b\"]", "application/json", echoFactory);
        assertThat(json.getStatus(), is(HttpResponseStatus.NO_CONTENT));

        final FullHttpResponse json2 = xhrSendRequest(sessionUrl, "[\"c\"]", "application/json;charset=utf-8",
                echoFactory);
        assertThat(json2.getStatus(), is(HttpResponseStatus.NO_CONTENT));
        final FullHttpResponse xml = xhrSendRequest(sessionUrl, "[\"d\"]", "application/xml", echoFactory);
        assertThat(xml.getStatus(), is(HttpResponseStatus.NO_CONTENT));
        final FullHttpResponse xml2 = xhrSendRequest(sessionUrl, "[\"e\"]", "text/xml", echoFactory);
        assertThat(xml2.getStatus(), is(HttpResponseStatus.NO_CONTENT));
        final FullHttpResponse xml3 = xhrSendRequest(sessionUrl, "[\"f\"]", "text/xml; charset=utf-8", echoFactory);
        assertThat(xml3.getStatus(), is(HttpResponseStatus.NO_CONTENT));
        final FullHttpResponse empty = xhrSendRequest(sessionUrl, "[\"g\"]", "", echoFactory);
        assertThat(empty.getStatus(), is(HttpResponseStatus.NO_CONTENT));

        final FullHttpResponse pollRequest = xhrRequest(sessionUrl, echoFactory);
        assertThat(pollRequest.getStatus(), is(HttpResponseStatus.OK));
        assertThat(pollRequest.content().toString(UTF_8), equalTo("a[\"a\",\"b\",\"c\",\"d\",\"e\",\"f\",\"g\"]\n"));
    }

    /*
     * Equivalent to XhrPolling.test_request_headers_cors sockjs-protocol-0.3.3.py.
     */
    @Test
    public void xhrPollingTestRequestHeadersCors() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();

        final FullHttpRequest okRequest = httpRequest(sessionUrl + "/xhr", POST);
        okRequest.headers().set(ACCESS_CONTROL_REQUEST_HEADERS, "a, b, c");
        final HttpResponse response = xhrRequest(okRequest, echoFactory);
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        SockJsTestUtil.assertCORSHeaders(response, "*");
        assertThat(response.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), equalTo("a, b, c"));

        final String emptySessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();
        final FullHttpRequest emptyHeaderRequest = httpRequest(emptySessionUrl + "/xhr", POST);
        emptyHeaderRequest.headers().set(ACCESS_CONTROL_REQUEST_HEADERS, "");
        final HttpResponse emptyHeaderResponse = xhrRequest(emptyHeaderRequest, echoFactory);
        assertThat(emptyHeaderResponse.getStatus(), is(HttpResponseStatus.OK));
        SockJsTestUtil.assertCORSHeaders(response, "*");
        assertThat(emptyHeaderResponse.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), is(nullValue()));

        final String noHeaderSessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();
        final FullHttpRequest noHeaderRequest = httpRequest(noHeaderSessionUrl + "/xhr", POST);
        final HttpResponse noHeaderResponse = xhrRequest(noHeaderRequest, echoFactory);
        assertThat(noHeaderResponse.getStatus(), is(HttpResponseStatus.OK));
        SockJsTestUtil.assertCORSHeaders(response, "*");
        assertThat(noHeaderResponse.headers().get(ACCESS_CONTROL_ALLOW_HEADERS), is(nullValue()));
    }

    /*
     * Equivalent to XhrStreaming.test_options in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void xhrStreamingTestOptions() throws Exception {
        final SockJsServiceFactory serviceFactory = echoService();
        final String sessionUrl = serviceFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(serviceFactory);
        removeLastInboundMessageHandlers(ch);

        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), GET);
        ch.writeInbound(request);

        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));
        assertThat(response.headers().get(CONTENT_TYPE), equalTo(Transports.CONTENT_TYPE_JAVASCRIPT));
        SockJsTestUtil.assertCORSHeaders(response, "*");
        SockJsTestUtil.verifyNoCacheHeaders(response);

        final DefaultHttpContent prelude = ch.readOutbound();
        assertThat(prelude.content().readableBytes(), is(PreludeFrame.CONTENT_SIZE + 1));
        final ByteBuf buffer = Unpooled.buffer(PreludeFrame.CONTENT_SIZE + 1);
        prelude.content().readBytes(buffer);
        buffer.release();

        final DefaultHttpContent openResponse = ch.readOutbound();
        assertThat(openResponse.content().toString(UTF_8), equalTo("o\n"));

        final FullHttpResponse validSend = xhrSendRequest(sessionUrl, "[\"x\"]", serviceFactory);
        assertThat(validSend.getStatus(), is(HttpResponseStatus.NO_CONTENT));

        final DefaultHttpContent chunk = ch.readOutbound();
        assertThat(chunk.content().toString(UTF_8), equalTo("a[\"x\"]\n"));
    }

    /*
     * Equivalent to XhrStreaming.test_response_limit in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void xhrStreamingTestResponseLimit() throws Exception {
        final SockJsConfig config = SockJsConfig.withPrefix("/echo").maxStreamingBytesSize(4096).build();
        final SockJsServiceFactory echoFactory = echoService(config);
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch);

        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), GET);
        ch.writeInbound(request);
        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));
        assertThat(response.headers().get(CONTENT_TYPE), equalTo(Transports.CONTENT_TYPE_JAVASCRIPT));
        SockJsTestUtil.assertCORSHeaders(response, "*");
        SockJsTestUtil.verifyNoCacheHeaders(response);

        final DefaultHttpContent prelude = ch.readOutbound();
        assertThat(prelude.content().readableBytes(), is(PreludeFrame.CONTENT_SIZE + 1));
        final ByteBuf buf = Unpooled.buffer(PreludeFrame.CONTENT_SIZE + 1);
        prelude.content().readBytes(buf);
        buf.release();

        final DefaultHttpContent openResponse = ch.readOutbound();
        assertThat(openResponse.content().toString(UTF_8), equalTo("o\n"));

        final String msg = generateMessage(128);
        for (int i = 0; i < 31; i++) {
            final FullHttpResponse validSend = xhrSendRequest(sessionUrl, "[\"" + msg + "\"]", echoFactory);
            assertThat(validSend.getStatus(), is(HttpResponseStatus.NO_CONTENT));
            final DefaultHttpContent chunk = ch.readOutbound();
            assertThat(chunk.content().toString(UTF_8), equalTo("a[\"" + msg + "\"]\n"));
        }
        final LastHttpContent lastChunk = ch.readOutbound();
        assertThat(lastChunk.content().readableBytes(), is(0));
        assertThat(ch.isOpen(), is(false));
    }

    /*
     * Equivalent to EventSource.test_response_limit in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void eventSourceTestResponseLimit() throws Exception {
        final SockJsConfig config = SockJsConfig.withPrefix("/echo").maxStreamingBytesSize(4096).build();
        final SockJsServiceFactory echoFactory = echoService(config);
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch);

        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.EVENTSOURCE.path(), GET);
        ch.writeInbound(request);
        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));
        assertThat(response.headers().get(CONTENT_TYPE), equalTo(EventSourceTransport.CONTENT_TYPE_EVENT_STREAM));

        final DefaultHttpContent newLinePrelude = ch.readOutbound();
        assertThat(newLinePrelude.content().toString(UTF_8), equalTo("\r\n"));

        final DefaultHttpContent data = ch.readOutbound();
        assertThat(data.content().toString(UTF_8), equalTo("data: o\r\n\r\n"));

        final String msg = generateMessage(4096);
        final FullHttpResponse validSend = xhrSendRequest(sessionUrl, "[\"" + msg + "\"]", echoFactory);
        assertThat(validSend.getStatus(), is(HttpResponseStatus.NO_CONTENT));
        final DefaultHttpContent chunk = ch.readOutbound();
        assertThat(chunk.content().toString(UTF_8), equalTo("data: a[\"" + msg + "\"]\r\n\r\n"));

        assertThat(ch.isOpen(), is(false));
    }

    /*
     * Equivalent to EventSource.test_transport in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void eventSourceTestTransport() throws Exception {
        final SockJsConfig config = SockJsConfig.withPrefix("/echo").maxStreamingBytesSize(4096).build();
        final SockJsServiceFactory echoFactory = echoService(config);
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch);

        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.EVENTSOURCE.path(), GET);
        ch.writeInbound(request);
        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));
        assertThat(response.headers().get(CONTENT_TYPE), equalTo(EventSourceTransport.CONTENT_TYPE_EVENT_STREAM));

        final DefaultHttpContent newLinePrelude = ch.readOutbound();
        assertThat(newLinePrelude.content().toString(UTF_8), equalTo("\r\n"));

        final DefaultHttpContent data = ch.readOutbound();
        assertThat(data.content().toString(UTF_8), equalTo("data: o\r\n\r\n"));

        final String msg = "[\"  \\u0000\\n\\r \"]";
        final FullHttpResponse validSend = xhrSendRequest(sessionUrl, msg, echoFactory);
        assertThat(validSend.getStatus(), is(HttpResponseStatus.NO_CONTENT));

        final DefaultHttpContent chunk = ch.readOutbound();
        assertThat(chunk.content().toString(UTF_8), equalTo("data: a[\"  \\u0000\\n\\r \"]\r\n\r\n"));
    }

    /*
     * Equivalent to HtmlFile.test_transport in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void htmlFileTestTransport() throws Exception {
        final String serviceName = "/echo";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).maxStreamingBytesSize(4096).build();
        final SockJsServiceFactory service = echoService(config);
        final EmbeddedChannel ch = channelForService(service);
        removeLastInboundMessageHandlers(ch);

        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.HTMLFILE.path() + "?c=callback", GET);
        ch.writeInbound(request);
        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));
        assertThat(response.headers().get(CONTENT_TYPE), equalTo(Transports.CONTENT_TYPE_HTML));

        final HttpContent headerChunk = ch.readOutbound();
        assertThat(headerChunk.content().readableBytes(), is(greaterThan(1024)));
        final String header = headerChunk.content().toString(UTF_8);
        assertThat(header, containsString("var c = parent.callback"));
        final HttpContent openChunk = ch.readOutbound();
        assertThat(openChunk.content().toString(UTF_8), equalTo("<script>\np(\"o\");\n</script>\r\n"));

        final FullHttpResponse validSend = xhrSendRequest(sessionUrl, "[\"x\"]", service);
        assertThat(validSend.getStatus(), is(HttpResponseStatus.NO_CONTENT));

        final DefaultHttpContent messageChunk = ch.readOutbound();
        assertThat(messageChunk.content().toString(UTF_8), equalTo("<script>\np(\"a[\\\"x\\\"]\");\n</script>\r\n"));
        ch.finish();
    }

    /*
     * Equivalent to HtmlFile.test_no_callback in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void htmlFileTestNoCallback() throws Exception {
        final String serviceName = "/echo";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).maxStreamingBytesSize(4096).build();
        final SockJsServiceFactory service = echoService(config);
        final EmbeddedChannel ch = channelForService(service);
        removeLastInboundMessageHandlers(ch);

        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.HTMLFILE.path() + "?c=", GET);
        ch.writeInbound(request);
        final FullHttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.INTERNAL_SERVER_ERROR));
        assertThat(response.content().toString(UTF_8), equalTo("\"callback\" parameter required"));
    }

    /*
     * Equivalent to HtmlFile.test_response_limit in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void htmlFileTestResponseLimit() throws Exception {
        final String serviceName = "/echo";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).maxStreamingBytesSize(4096).build();
        final SockJsServiceFactory service = echoService(config);
        final EmbeddedChannel ch = channelForService(service);
        removeLastInboundMessageHandlers(ch);

        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.HTMLFILE.path() + "?c=callback", GET);
        ch.writeInbound(request);
        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));

        // read and discard header chunk
        ch.readOutbound();
        // read and discard open frame
        ch.readOutbound();

        final String msg = generateMessage(4096);
        final FullHttpResponse validSend = xhrSendRequest(sessionUrl, "[\"" + msg + "\"]", service);
        assertThat(validSend.getStatus(), is(HttpResponseStatus.NO_CONTENT));
        final DefaultHttpContent chunk = ch.readOutbound();
        assertThat(chunk.content().toString(UTF_8), equalTo("<script>\np(\"a[\\\"" + msg + "\\\"]\");\n</script>\r\n"));

        assertThat(ch.isOpen(), is(false));
    }

    /*
     * Equivalent to JsonPolling.test_transport in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsonpPollingTestTransport() throws Exception {
        final String serviceName = "/echo";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsServiceFactory echoService = echoService();

        final FullHttpResponse response = jsonpRequest(sessionUrl + "/jsonp?c=%63allback", echoService);
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.content().toString(UTF_8), equalTo("callback(\"o\");\r\n"));
        assertThat(response.headers().get(CONTENT_TYPE), equalTo(Transports.CONTENT_TYPE_JAVASCRIPT));
        verifyNotCached(response);

        final String data = "d=%5B%22x%22%5D";
        final FullHttpResponse sendResponse = jsonpSend(sessionUrl + "/jsonp_send", data, echoService);
        assertThat(sendResponse.getStatus(), is(HttpResponseStatus.OK));
        assertThat(sendResponse.content().toString(UTF_8), equalTo("ok"));
        assertThat(sendResponse.headers().get(CONTENT_TYPE), equalTo(Transports.CONTENT_TYPE_PLAIN));
        verifyNotCached(response);

        final FullHttpResponse pollResponse = jsonpRequest(sessionUrl + "/jsonp?c=callback", echoService);
        assertThat(pollResponse.getStatus(), is(HttpResponseStatus.OK));
        assertThat(pollResponse.headers().get(CONTENT_TYPE), equalTo(Transports.CONTENT_TYPE_JAVASCRIPT));
        assertThat(pollResponse.content().toString(UTF_8), equalTo("callback(\"a[\\\"x\\\"]\");\r\n"));
        verifyNotCached(pollResponse);
    }

    /*
     * Equivalent to JsonPolling.test_no_callback in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsonpPollingTestNoCallback() throws Exception {
        final SockJsServiceFactory echoService = echoService();
        final FullHttpResponse response = jsonpRequest("/echo/a/a/jsonp", echoService);
        assertThat(response.getStatus(), is(HttpResponseStatus.INTERNAL_SERVER_ERROR));
        assertThat(response.content().toString(UTF_8), equalTo("\"callback\" parameter required"));
    }

    /*
     * Equivalent to JsonPolling.test_invalid_json in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsonpPollingTestInvalidJson() throws Exception {
        final String serviceName = "/echo";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsServiceFactory echoService = echoService();

        final FullHttpResponse response = jsonpRequest(sessionUrl + "/jsonp?c=x", echoService);
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.content().toString(UTF_8), equalTo("x(\"o\");\r\n"));

        assertBrokenJSONEncoding(jsonpSend(sessionUrl + "/jsonp_send", "d=%5B%22x", echoService));
        assertPayloadExpected(jsonpSend(sessionUrl + "/jsonp_send", "", echoService));
        assertPayloadExpected(jsonpSend(sessionUrl + "/jsonp_send", "d=", echoService));
        assertPayloadExpected(jsonpSend(sessionUrl + "/jsonp_send", "p=p", echoService));

        final FullHttpResponse sendResponse = jsonpSend(sessionUrl + "/jsonp_send", "d=%5B%22b%22%5D", echoService);
        assertThat(sendResponse.getStatus(), is(HttpResponseStatus.OK));

        final FullHttpResponse pollResponse = jsonpRequest(sessionUrl + "/jsonp?c=x", echoService);
        assertThat(pollResponse.getStatus(), is(HttpResponseStatus.OK));
        assertThat(pollResponse.content().toString(UTF_8), equalTo("x(\"a[\\\"b\\\"]\");\r\n"));
    }

    /*
     * Equivalent to JsonPolling.test_content_types in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsonpPollingTestContentTypes() throws Exception {
        final String serviceName = "/echo";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsServiceFactory echoService = echoService();
        final FullHttpResponse response = jsonpRequest(sessionUrl + "/jsonp?c=x", echoService);
        assertThat(response.content().toString(UTF_8), equalTo("x(\"o\");\r\n"));

        final String data = "d=%5B%22abc%22%5D";
        final FullHttpResponse sendResponse = jsonpSend(sessionUrl + "/jsonp_send", data, echoService);
        assertThat(sendResponse.getStatus(), is(HttpResponseStatus.OK));

        final FullHttpRequest plainRequest = httpRequest(sessionUrl + "/jsonp_send", POST);
        plainRequest.headers().set(CONTENT_TYPE, "text/plain");
        final ByteBuf byteBuf = Unpooled.copiedBuffer("[\"%61bc\"]", UTF_8);
        plainRequest.content().writeBytes(byteBuf);
        byteBuf.release();
        final FullHttpResponse plainResponse = jsonpSend(plainRequest, echoService);
        assertThat(plainResponse.getStatus(), is(HttpResponseStatus.OK));

        final FullHttpResponse pollResponse = jsonpRequest(sessionUrl + "/jsonp?c=x", echoService);
        assertThat(pollResponse.getStatus(), is(HttpResponseStatus.OK));
        assertThat(pollResponse.content().toString(UTF_8), equalTo("x(\"a[\\\"abc\\\",\\\"%61bc\\\"]\");\r\n"));
    }

    /*
     * Equivalent to JsonPolling.test_close in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsonpPollingTestClose() throws Exception {
        final String serviceName = "/close";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsServiceFactory service = closeService();
        final FullHttpResponse response = jsonpRequest(sessionUrl + "/jsonp?c=x", service);
        assertThat(response.content().toString(UTF_8), equalTo("x(\"o\");\r\n"));

        final FullHttpResponse firstResponse = jsonpRequest(sessionUrl + "/jsonp?c=x", service);
        assertThat(firstResponse.content().toString(UTF_8), equalTo("x(\"c[3000,\\\"Go away!\\\"]\");\r\n"));

        final FullHttpResponse secondResponse = jsonpRequest(sessionUrl + "/jsonp?c=x", service);
        assertThat(secondResponse.content().toString(UTF_8), equalTo("x(\"c[3000,\\\"Go away!\\\"]\");\r\n"));
    }

    /*
     * Equivalent to JsessionIdCookie.test_basic in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsessionIdCookieTestBasic() throws Exception {
        final SockJsConfig config = SockJsConfig.withPrefix("/cookie_needed_echo").cookiesNeeded().build();
        SockJsServiceFactory serviceFactory = echoService(config);
        final FullHttpResponse response = infoRequest(config.prefix(), serviceFactory);
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        verifyNoSET_COOKIE(response);
        assertThat(infoAsJson(response).get("cookie_needed").asBoolean(), is(true));
    }

    /*
     * Equivalent to JsessionIdCookie.test_xhr in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsessionIdCookieTestXhr() throws Exception {
        assertSetCookie(Transports.Type.XHR.path());

        final String serviceName = "/cookie_needed_echo";
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).cookiesNeeded().build();
        final EmbeddedChannel ch = channelForService(echoService(config));
        removeLastInboundMessageHandlers(ch);
        final String sessionUrl = serviceName + "/abc/" + UUID.randomUUID().toString();
        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.XHR.path(), GET);
        request.headers().set("Cookie", ClientCookieEncoder.encode("JSESSIONID", "abcdef"));
        ch.writeInbound(request);
        final FullHttpResponse response2 = ch.readOutbound();
        assertThat(response2.getStatus(), is(HttpResponseStatus.OK));
        assertSetCookie("abcdef", response2);
    }

    /*
     * Equivalent to JsessionIdCookie.test_xhr_streaming in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsessionIdCookieTestXhrStreaming() throws Exception {
        assertSetCookie(Transports.Type.XHR_STREAMING.path());
    }

    /*
     * Equivalent to JsessionIdCookie.test_eventsource in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsessionIdCookieTestEventSource() throws Exception {
        assertSetCookie(Transports.Type.EVENTSOURCE.path());
    }

    /*
     * Equivalent to JsessionIdCookie.test_htmlfile in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsessionIdCookieTestHtmlFile() throws Exception {
        assertSetCookie(Transports.Type.HTMLFILE.path() + "?c=callback");
    }

    /*
     * Equivalent to JsessionIdCookie.test_jsonp in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsessionIdCookieTestJsonp() throws Exception {
        assertSetCookie(Transports.Type.JSONP.path() + "?c=callback");
    }

    /*
     * Equivalent to RawWebsocket.test_transport in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void rawWebsocketTestTransport() throws Exception {
        final String serviceName = "/echo";
        final SockJsServiceFactory echoServiceFactory = echoService();
        final EmbeddedChannel ch = wsChannelForService(echoServiceFactory);

        ch.writeInbound(webSocketUpgradeRequest(serviceName + "/websocket"));
        // Discard Switching Protocols response
        ch.readOutbound();
        ch.writeInbound(new TextWebSocketFrame("Hello world!\uffff"));
        final TextWebSocketFrame textFrame = (TextWebSocketFrame) readOutboundDiscardEmpty(ch);
        assertThat(textFrame.text(), equalTo("Hello world!\uffff"));
        ch.finish();
    }

    /*
     * Equivalent to RawWebsocket.test_close in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void rawWebsocketTestClose() throws Exception {
        final SockJsServiceFactory closeFactory = closeService();
        final EmbeddedChannel ch = wsChannelForService(closeFactory);
        ch.writeInbound(webSocketUpgradeRequest(closeFactory.config().prefix() + "/websocket"));
        assertThat(ch.isActive(), is(false));
        ch.finish();
    }

    @Test
    public void webSocketCloseSession() throws Exception {
        final String serviceName = "/closesession";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).build();
        final SockJsService sockJsService = mock(SockJsService.class);
        final EmbeddedChannel ch = wsChannelForService(factoryFor(sockJsService, config));

        final FullHttpRequest request = webSocketUpgradeRequest(sessionUrl + "/websocket", V13.toHttpHeaderValue());
        ch.writeInbound(request);

        // read and discard the HTTP Response (this will be a ByteBuf and not an object
        // as we have a HttpEncoder in the pipeline to start with.
        ch.readOutbound();

        assertThat(((TextWebSocketFrame) readOutboundDiscardEmpty(ch)).content().toString(UTF_8), equalTo("o"));

        ch.writeInbound(new CloseWebSocketFrame(1000, "Normal close"));
        final CloseWebSocketFrame closeFrame = ch.readOutbound();
        assertThat(closeFrame.statusCode(), is(1000));
        assertThat(closeFrame.reasonText(), equalTo("Normal close"));
        verify(sockJsService).onOpen(any(SockJsSessionContext.class));
        verify(sockJsService).onClose();
    }

    /*
     * Equivalent to JSONEncoding.test_xhr_server_encodes in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsonEncodingTestXhrServerEncodes() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();

        final FullHttpResponse response = xhrRequest(sessionUrl, echoFactory);
        assertOpenFrameResponse(response);
        final String content = Transports.escapeCharacters(serverKillerStringEsc().toCharArray());
        final FullHttpResponse xhrSendResponse = xhrSendRequest(sessionUrl, "[\"" + content + "\"]", echoFactory);
        assertThat(xhrSendResponse.getStatus(), is(HttpResponseStatus.NO_CONTENT));
        final FullHttpResponse pollResponse = xhrRequest(sessionUrl, echoFactory);
        assertThat(pollResponse.getStatus(), is(HttpResponseStatus.OK));
        assertThat(pollResponse.content().toString(UTF_8), equalTo("a[\"" + content + "\"]\n"));
    }

    /*
     * Equivalent to JSONEncoding.test_xhr_server_decodes in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void jsonEncodingTestXhrServerDecodes() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/abc/" + UUID.randomUUID().toString();

        final FullHttpResponse response = xhrRequest(sessionUrl, echoFactory);
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.content().toString(UTF_8), equalTo("o\n"));

        final String content = "[\"" + clientKillerStringEsc() + "\"]";
        final FullHttpResponse xhrSendResponse = xhrSendRequest(sessionUrl, content, echoFactory);
        assertThat(xhrSendResponse.getStatus(), is(HttpResponseStatus.NO_CONTENT));

        final FullHttpResponse pollResponse = xhrRequest(sessionUrl, echoFactory);
        assertThat(pollResponse.getStatus(), is(HttpResponseStatus.OK));

        // Let the content go through the MessageFrame to match what the response will go through.
        final MessageFrame messageFrame = new MessageFrame(JsonUtil.decode(content)[0]);
        String expectedContent = JsonUtil.encode(messageFrame.content().toString(UTF_8) + '\n');
        String responseContent = JsonUtil.encode(pollResponse.content().toString(UTF_8));
        assertThat(responseContent, equalTo(expectedContent));
    }

    /*
     * Equivalent to HandlingClose.test_close_frame in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void handlingCloseTestCloseFrame() throws Exception {
        final SockJsServiceFactory serviceFactory = closeService();
        final String sessionUrl = serviceFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(serviceFactory);
        removeLastInboundMessageHandlers(ch);
        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), GET);
        ch.writeInbound(request);

        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));
        //Read and discard prelude
        ch.readOutbound();
        // Read and discard of the open frame
        ch.readOutbound();

        final DefaultHttpContent closeResponse = ch.readOutbound();
        assertThat(closeResponse.content().toString(UTF_8), equalTo("c[3000,\"Go away!\"]\n"));

        final EmbeddedChannel ch2 = channelForService(serviceFactory);
        removeLastInboundMessageHandlers(ch2);
        final FullHttpRequest request2 = httpRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), GET);
        ch2.writeInbound(request2);

        final HttpResponse response2 =  ch2.readOutbound();
        assertThat(response2.getStatus(), equalTo(HttpResponseStatus.OK));
        //Read and discard prelude
        ch2.readOutbound();

        final DefaultHttpContent closeResponse2 = ch2.readOutbound();
        assertThat(closeResponse2.content().toString(UTF_8), equalTo("c[3000,\"Go away!\"]\n"));

        assertThat(ch.isActive(), is(false));
        assertThat(ch2.isActive(), is(false));
    }

    /*
     * Equivalent to HandlingClose.test_close_request in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void handlingCloseTestCloseRequest() throws Exception {
        final SockJsServiceFactory serviceFactory = echoService();
        final String sessionUrl = serviceFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(serviceFactory);
        removeLastInboundMessageHandlers(ch);
        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), POST);
        ch.writeInbound(request);

        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));
        //Read and discard prelude
        ch.readOutbound();
        final DefaultHttpContent openResponse = ch.readOutbound();
        assertThat(openResponse.content().toString(UTF_8), equalTo("o\n"));

        final EmbeddedChannel ch2 = channelForService(serviceFactory);
        removeLastInboundMessageHandlers(ch2);
        final FullHttpRequest request2 = httpRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), POST);
        ch2.writeInbound(request2);

        final HttpResponse response2 =  ch2.readOutbound();
        assertThat(response2.getStatus(), equalTo(HttpResponseStatus.OK));
        //Read and discard prelude
        ch2.readOutbound();

        final DefaultHttpContent closeResponse2 = ch2.readOutbound();
        assertThat(closeResponse2.content().toString(UTF_8), equalTo("c[2010,\"Another connection still open\"]\n"));

        assertThat(ch2.isActive(), is(false));
    }

    /*
     * Equivalent to HandlingClose.test_abort_xhr_streaming in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void handlingCloseTestAbortXhrStreaming() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch);
        final FullHttpRequest request = httpRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), GET);
        ch.writeInbound(request);

        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));
        //Read and discard prelude
        ch.readOutbound();
        final DefaultHttpContent openResponse = ch.readOutbound();
        assertThat(openResponse.content().toString(UTF_8), equalTo("o\n"));

        final EmbeddedChannel ch2 = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch2);
        final FullHttpRequest request2 = httpRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), GET);
        ch2.writeInbound(request2);

        final HttpResponse response2 =  ch2.readOutbound();
        assertThat(response2.getStatus(), equalTo(HttpResponseStatus.OK));
        //Read and discard prelude
        ch2.readOutbound();

        final DefaultHttpContent closeResponse2 = ch2.readOutbound();
        assertThat(closeResponse2.content().toString(UTF_8), equalTo("c[2010,\"Another connection still open\"]\n"));

        assertThat(ch2.isActive(), is(false));
        ch.close();

        final EmbeddedChannel ch3 = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch3);
        final FullHttpRequest request3 = httpRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), POST);
        ch3.writeInbound(request3);

        final HttpResponse response3 =  ch3.readOutbound();
        assertThat(response3.getStatus(), equalTo(HttpResponseStatus.OK));
        //Read and discard prelude
        ch3.readOutbound();

        final DefaultHttpContent closeResponse3 = ch3.readOutbound();
        assertThat(closeResponse3.content().toString(UTF_8), equalTo("c[1002,\"Connection interrupted\"]\n"));

        assertThat(ch3.isActive(), is(false));
        ch.close();
    }

    /*
     * Equivalent to HandlingClose.test_abort_xhr_polling in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void handlingCloseTestAbortXhrPolling() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();
        final String sessionUrl = echoFactory.config().prefix() + "/000/" + UUID.randomUUID().toString();

        final EmbeddedChannel ch = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch);
        ch.writeInbound(httpRequest(sessionUrl + Transports.Type.XHR.path(), GET));

        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.content().toString(UTF_8), equalTo("o\n"));

        final EmbeddedChannel ch2 = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch2);
        ch2.writeInbound(httpRequest(sessionUrl + Transports.Type.XHR.path(), GET));
        final FullHttpResponse response2 = ch2.readOutbound();
        assertThat(response2.content().toString(UTF_8), equalTo("c[2010,\"Another connection still open\"]\n"));

        final EmbeddedChannel ch3 = channelForService(echoFactory);
        removeLastInboundMessageHandlers(ch3);
        ch3.writeInbound(httpRequest(sessionUrl + Transports.Type.XHR.path(), GET));

        final FullHttpResponse response3 = ch3.readOutbound();
        assertThat(response3.content().toString(UTF_8), equalTo("c[1002,\"Connection interrupted\"]\n"));
    }

    /*
     * Equivalent to Http10.test_synchronous in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void http10TestSynchronous() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();

        final EmbeddedChannel ch = channelForService(echoFactory);
        final FullHttpRequest request = httpGetRequest(echoFactory.config().prefix(), HTTP_1_0);
        request.headers().set(CONNECTION, KEEP_ALIVE);
        ch.writeInbound(request);
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.getProtocolVersion(), is(HTTP_1_0));
        assertThat(response.headers().get(TRANSFER_ENCODING), is(nullValue()));
        if (response.headers().get(CONTENT_LENGTH) == null) {
            assertThat(response.headers().get(CONNECTION), equalTo("close"));
            assertThat(response.content().toString(UTF_8), equalTo("Welcome to SockJS!\n"));
            assertThat(ch.isActive(), is(false));
        } else {
            assertThat(response.headers().get(CONTENT_LENGTH), is("19"));
            assertThat(response.content().toString(UTF_8), equalTo("Welcome to SockJS!\n"));
            final String connectionHeader = response.headers().get(CONNECTION);
            if (connectionHeader.contains("close") || connectionHeader.isEmpty()) {
                assertThat(ch.isActive(), is(false));
            } else {
                assertThat(connectionHeader, equalTo("keep-alive"));
                ch.writeInbound(httpGetRequest(echoFactory.config().prefix(), HTTP_1_0));
                final HttpResponse newResponse = ch.readOutbound();
                assertThat(newResponse.getStatus(), is(HttpResponseStatus.OK));
            }
        }
    }

    /*
     * Equivalent to Http10.test_streaming in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void http10TestStreaming() throws Exception {
        final SockJsServiceFactory closeFactory = closeService();
        final String sessionUrl = closeFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(closeFactory);
        removeLastInboundMessageHandlers(ch);
        final FullHttpRequest request = httpPostRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), HTTP_1_0);
        request.headers().set(CONNECTION, KEEP_ALIVE);
        ch.writeInbound(request);

        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));
        assertThat(response.getProtocolVersion(), is(HTTP_1_0));
        assertThat(response.headers().get(TRANSFER_ENCODING), is(nullValue()));
        assertThat(response.headers().get(CONTENT_LENGTH), is(nullValue()));
        final HttpContent httpContent = ch.readOutbound();
        assertThat(httpContent.content().readableBytes(), is(PreludeFrame.CONTENT_SIZE + 1));
        assertThat(getContent(httpContent.content()), equalTo(expectedContent(PreludeFrame.CONTENT_SIZE)));

        final HttpContent open = ch.readOutbound();
        assertThat(open.content().toString(UTF_8), equalTo("o\n"));

        final HttpContent goAway = ch.readOutbound();
        assertThat(goAway.content().toString(UTF_8), equalTo("c[3000,\"Go away!\"]\n"));

        final HttpContent lastChunk = ch.readOutbound();
        assertThat(lastChunk.content().toString(UTF_8), equalTo(""));
    }

    /*
     * Equivalent to Http11.test_synchronous in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void http11TestSynchronous() throws Exception {
        final SockJsServiceFactory echoFactory = echoService();

        final EmbeddedChannel ch = channelForService(echoFactory);
        final FullHttpRequest request = httpGetRequest(echoFactory.config().prefix(), HTTP_1_1);
        request.headers().set(CONNECTION, KEEP_ALIVE);
        ch.writeInbound(request);

        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.getProtocolVersion(), is(HTTP_1_1));
        String connectionHeader = response.headers().get(CONNECTION);
        if (connectionHeader != null) {
            assertThat(connectionHeader, equalTo("keep-alive"));
        }

        if (response.headers().get(CONTENT_LENGTH) != null) {
            assertThat(response.headers().get(CONTENT_LENGTH), is("19"));
            assertThat(response.content().toString(UTF_8), equalTo("Welcome to SockJS!\n"));
            assertThat(response.headers().get(TRANSFER_ENCODING), is(nullValue()));
        } else {
            assertThat(response.headers().get(TRANSFER_ENCODING), is("chunked"));
            assertThat(response.content().toString(UTF_8), equalTo("Welcome to SockJS!\n"));
        }

        ch.writeInbound(httpGetRequest(echoFactory.config().prefix(), HTTP_1_0));
        final HttpResponse newResponse = ch.readOutbound();
        assertThat(newResponse.getStatus(), is(HttpResponseStatus.OK));
    }

    /*
     * Equivalent to Http11.test_streaming in sockjs-protocol-0.3.3.py.
     */
    @Test
    public void http11TestStreaming() throws Exception {
        final SockJsServiceFactory closeFactory = closeService();
        final String sessionUrl = closeFactory.config().prefix() + "/222/" + UUID.randomUUID().toString();
        final EmbeddedChannel ch = channelForService(closeFactory);
        removeLastInboundMessageHandlers(ch);
        final FullHttpRequest request = httpPostRequest(sessionUrl + Transports.Type.XHR_STREAMING.path(), HTTP_1_1);
        request.headers().set(CONNECTION, KEEP_ALIVE);
        ch.writeInbound(request);

        final HttpResponse response =  ch.readOutbound();
        assertThat(response.getStatus(), equalTo(HttpResponseStatus.OK));
        assertThat(response.getProtocolVersion(), is(HTTP_1_1));
        assertThat(response.headers().get(TRANSFER_ENCODING), equalTo("chunked"));
        assertThat(response.headers().get(CONTENT_LENGTH), is(nullValue()));

        final HttpContent httpContent = ch.readOutbound();
        assertThat(httpContent.content().readableBytes(), is(PreludeFrame.CONTENT_SIZE + 1));
        assertThat(getContent(httpContent.content()), equalTo(expectedContent(PreludeFrame.CONTENT_SIZE)));

        final HttpContent open = ch.readOutbound();
        assertThat(open.content().toString(UTF_8), equalTo("o\n"));

        final HttpContent goAway = ch.readOutbound();
        assertThat(goAway.content().toString(UTF_8), equalTo("c[3000,\"Go away!\"]\n"));
        final HttpContent lastChunk = ch.readOutbound();
        assertThat(lastChunk.content().toString(UTF_8), equalTo(""));
    }

    @Test
    public void prefixNotFound() throws Exception {
        final SockJsConfig config = SockJsConfig.withPrefix("/simplepush").cookiesNeeded().build();
        final EmbeddedChannel ch = channelForMockService(config);
        ch.writeInbound(httpRequest("/missing"));
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus().code(), is(HttpResponseStatus.NOT_FOUND.code()));
    }

    private static void assertGoAwayResponse(final FullHttpResponse response) {
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.content().toString(UTF_8), equalTo("c[3000,\"Go away!\"]\n"));
    }

    private static void assertNoContent(final FullHttpResponse response) {
        assertThat(response.getStatus(), is(HttpResponseStatus.NO_CONTENT));
        assertThat(response.content().isReadable(), is(false));
    }

    private static void assertMessageFrameContent(final FullHttpResponse response, final String expected) {
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.content().toString(UTF_8), equalTo("a[\"" + expected + "\"]\n"));
    }

    private static byte[] getContent(final ByteBuf buf) {
        final byte[] actualContent = new byte[PreludeFrame.CONTENT_SIZE + 1];
        buf.readBytes(actualContent);
        return actualContent;
    }

    private static byte[] expectedContent(final int size) {
        final byte[] content = new byte[size + 1];
        for (int i = 0; i < content.length; i++) {
            content[i] = 'h';
        }
        content[size] = '\n';
        return content;
    }

    private static String clientKillerStringEsc() {
        return "\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\u0008\\u0009\\u000a\\u000b\\u000c\\u000d" +
               "\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b" +
               "\\u001c\\u001d\\u001e\\u001f\\u0022\\u007f\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" +
               "\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095" +
               "\\u0096\\u0097\\u0098\\u0099\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f\\u00ad\\u0300\\u0301\\u0302" +
               "\\u0303\\u0304\\u0305\\u0306\\u0307\\u0308\\u0309\\u030a\\u030b\\u030c\\u030d\\u030e\\u030f\\u0310" +
               "\\u0311\\u0312\\u0313\\u0314\\u0315\\u0316\\u0317\\u0318\\u0319\\u031a\\u031b\\u031c\\u031d\\u031e" +
               "\\u031f\\u0320\\u0321\\u0322\\u0323\\u0324\\u0325\\u0326\\u0327\\u0328\\u0329\\u032a\\u032b\\u032c" +
               "\\u032d\\u032e\\u032f\\u0330\\u0331\\u0332\\u0333\\u033d\\u033e\\u033f\\u0340\\u0341\\u0342\\u0343" +
               "\\u0344\\u0345\\u0346\\u034a\\u034b\\u034c\\u0350\\u0351\\u0352\\u0357\\u0358\\u035c\\u035d\\u035e" +
               "\\u035f\\u0360\\u0361\\u0362\\u0374\\u037e\\u0387\\u0591\\u0592\\u0593\\u0594\\u0595\\u0596\\u0597" +
               "\\u0598\\u0599\\u059a\\u059b\\u059c\\u059d\\u059e\\u059f\\u05a0\\u05a1\\u05a2\\u05a3\\u05a4\\u05a5" +
               "\\u05a6\\u05a7\\u05a8\\u05a9\\u05aa\\u05ab\\u05ac\\u05ad\\u05ae\\u05af\\u05c4\\u0600\\u0601\\u0602" +
               "\\u0603\\u0604\\u0610\\u0611\\u0612\\u0613\\u0614\\u0615\\u0616\\u0617\\u0653\\u0654\\u0657\\u0658" +
               "\\u0659\\u065a\\u065b\\u065d\\u065e\\u06df\\u06e0\\u06e1\\u06e2\\u06eb\\u06ec\\u070f\\u0730\\u0732" +
               "\\u0733\\u0735\\u0736\\u073a\\u073d\\u073f\\u0740\\u0741\\u0743\\u0745\\u0747\\u07eb\\u07ec\\u07ed" +
               "\\u07ee\\u07ef\\u07f0\\u07f1\\u0951\\u0958\\u0959\\u095a\\u095b\\u095c\\u095d\\u095e\\u095f\\u09dc" +
               "\\u09dd\\u09df\\u0a33\\u0a36\\u0a59\\u0a5a\\u0a5b\\u0a5e\\u0b5c\\u0b5d\\u0e38\\u0e39\\u0f43\\u0f4d" +
               "\\u0f52\\u0f57\\u0f5c\\u0f69\\u0f72\\u0f73\\u0f74\\u0f75\\u0f76\\u0f78\\u0f80\\u0f81\\u0f82\\u0f83" +
               "\\u0f93\\u0f9d\\u0fa2\\u0fa7\\u0fac\\u0fb9\\u17b4\\u17b5\\u1939\\u193a\\u1a17\\u1b6b\\u1cda\\u1cdb" +
               "\\u1dc0\\u1dc1\\u1dc2\\u1dc3\\u1dc4\\u1dc5\\u1dc6\\u1dc7\\u1dc8\\u1dc9\\u1dca\\u1dcb\\u1dcc\\u1dcd" +
               "\\u1dce\\u1dcf\\u1dfc\\u1dfe\\u1f71\\u1f73\\u1f75\\u1f77\\u1f79\\u1f7b\\u1f7d\\u1fbb\\u1fbe\\u1fc9" +
               "\\u1fcb\\u1fd3\\u1fdb\\u1fe3\\u1feb\\u1fee\\u1fef\\u1ff9\\u1ffb\\u1ffd\\u2000\\u2001\\u2002\\u2003" +
               "\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u200b\\u200c\\u200d\\u200e\\u200f\\u2010\\u2011" +
               "\\u2012\\u2013\\u2014\\u2015\\u2016\\u2017\\u2018\\u2019\\u201a\\u201b\\u201c\\u201d\\u201e\\u201f" +
               "\\u2020\\u2021\\u2022\\u2023\\u2024\\u2025\\u2026\\u2027\\u2028\\u2029\\u202a\\u202b\\u202c\\u202d" +
               "\\u202e\\u202f\\u2030\\u2031\\u2032\\u2033\\u2034\\u2035\\u2036\\u2037\\u2038\\u2039\\u203a\\u203b" +
               "\\u203c\\u203d\\u203e\\u203f\\u2040\\u2041\\u2042\\u2043\\u2044\\u2045\\u2046\\u2047\\u2048\\u2049" +
               "\\u204a\\u204b\\u204c\\u204d\\u204e\\u204f\\u2050\\u2051\\u2052\\u2053\\u2054\\u2055\\u2056\\u2057" +
               "\\u2058\\u2059\\u205a\\u205b\\u205c\\u205d\\u205e\\u205f\\u2060\\u2061\\u2062\\u2063\\u2064\\u2065" +
               "\\u2066\\u2067\\u2068\\u2069\\u206a\\u206b\\u206c\\u206d\\u206e\\u206f\\u2070\\u2071\\u2072\\u2073" +
               "\\u2074\\u2075\\u2076\\u2077\\u2078\\u2079\\u207a\\u207b\\u207c\\u207d\\u207e\\u207f\\u2080\\u2081" +
               "\\u2082\\u2083\\u2084\\u2085\\u2086\\u2087\\u2088\\u2089\\u208a\\u208b\\u208c\\u208d\\u208e\\u208f" +
               "\\u2090\\u2091\\u2092\\u2093\\u2094\\u2095\\u2096\\u2097\\u2098\\u2099\\u209a\\u209b\\u209c\\u209d" +
               "\\u209e\\u209f\\u20a0\\u20a1\\u20a2\\u20a3\\u20a4\\u20a5\\u20a6\\u20a7\\u20a8\\u20a9\\u20aa\\u20ab" +
               "\\u20ac\\u20ad\\u20ae\\u20af\\u20b0\\u20b1\\u20b2\\u20b3\\u20b4\\u20b5\\u20b6\\u20b7\\u20b8\\u20b9" +
               "\\u20ba\\u20bb\\u20bc\\u20bd\\u20be\\u20bf\\u20c0\\u20c1\\u20c2\\u20c3\\u20c4\\u20c5\\u20c6\\u20c7" +
               "\\u20c8\\u20c9\\u20ca\\u20cb\\u20cc\\u20cd\\u20ce\\u20cf\\u20d0\\u20d1\\u20d2\\u20d3\\u20d4\\u20d5" +
               "\\u20d6\\u20d7\\u20d8\\u20d9\\u20da\\u20db\\u20dc\\u20dd\\u20de\\u20df\\u20e0\\u20e1\\u20e2\\u20e3" +
               "\\u20e4\\u20e5\\u20e6\\u20e7\\u20e8\\u20e9\\u20ea\\u20eb\\u20ec\\u20ed\\u20ee\\u20ef\\u20f0\\u20f1" +
               "\\u20f2\\u20f3\\u20f4\\u20f5\\u20f6\\u20f7\\u20f8\\u20f9\\u20fa\\u20fb\\u20fc\\u20fd\\u20fe\\u20ff" +
               "\\u2126\\u212a\\u212b\\u2329\\u232a\\u2adc\\u302b\\u302c\\uaab2\\uaab3\\uf900\\uf901\\uf902\\uf903" +
               "\\uf904\\uf905\\uf906\\uf907\\uf908\\uf909\\uf90a\\uf90b\\uf90c\\uf90d\\uf90e\\uf90f\\uf910\\uf911" +
               "\\uf912\\uf913\\uf914\\uf915\\uf916\\uf917\\uf918\\uf919\\uf91a\\uf91b\\uf91c\\uf91d\\uf91e\\uf91f" +
               "\\uf920\\uf921\\uf922\\uf923\\uf924\\uf925\\uf926\\uf927\\uf928\\uf929\\uf92a\\uf92b\\uf92c\\uf92d" +
               "\\uf92e\\uf92f\\uf930\\uf931\\uf932\\uf933\\uf934\\uf935\\uf936\\uf937\\uf938\\uf939\\uf93a\\uf93b" +
               "\\uf93c\\uf93d\\uf93e\\uf93f\\uf940\\uf941\\uf942\\uf943\\uf944\\uf945\\uf946\\uf947\\uf948\\uf949" +
               "\\uf94a\\uf94b\\uf94c\\uf94d\\uf94e\\uf94f\\uf950\\uf951\\uf952\\uf953\\uf954\\uf955\\uf956\\uf957" +
               "\\uf958\\uf959\\uf95a\\uf95b\\uf95c\\uf95d\\uf95e\\uf95f\\uf960\\uf961\\uf962\\uf963\\uf964\\uf965" +
               "\\uf966\\uf967\\uf968\\uf969\\uf96a\\uf96b\\uf96c\\uf96d\\uf96e\\uf96f\\uf970\\uf971\\uf972\\uf973" +
               "\\uf974\\uf975\\uf976\\uf977\\uf978\\uf979\\uf97a\\uf97b\\uf97c\\uf97d\\uf97e\\uf97f\\uf980\\uf981" +
               "\\uf982\\uf983\\uf984\\uf985\\uf986\\uf987\\uf988\\uf989\\uf98a\\uf98b\\uf98c\\uf98d\\uf98e\\uf98f" +
               "\\uf990\\uf991\\uf992\\uf993\\uf994\\uf995\\uf996\\uf997\\uf998\\uf999\\uf99a\\uf99b\\uf99c\\uf99d" +
               "\\uf99e\\uf99f\\uf9a0\\uf9a1\\uf9a2\\uf9a3\\uf9a4\\uf9a5\\uf9a6\\uf9a7\\uf9a8\\uf9a9\\uf9aa\\uf9ab" +
               "\\uf9ac\\uf9ad\\uf9ae\\uf9af\\uf9b0\\uf9b1\\uf9b2\\uf9b3\\uf9b4\\uf9b5\\uf9b6\\uf9b7\\uf9b8\\uf9b9" +
               "\\uf9ba\\uf9bb\\uf9bc\\uf9bd\\uf9be\\uf9bf\\uf9c0\\uf9c1\\uf9c2\\uf9c3\\uf9c4\\uf9c5\\uf9c6\\uf9c7" +
               "\\uf9c8\\uf9c9\\uf9ca\\uf9cb\\uf9cc\\uf9cd\\uf9ce\\uf9cf\\uf9d0\\uf9d1\\uf9d2\\uf9d3\\uf9d4\\uf9d5" +
               "\\uf9d6\\uf9d7\\uf9d8\\uf9d9\\uf9da\\uf9db\\uf9dc\\uf9dd\\uf9de\\uf9df\\uf9e0\\uf9e1\\uf9e2\\uf9e3" +
               "\\uf9e4\\uf9e5\\uf9e6\\uf9e7\\uf9e8\\uf9e9\\uf9ea\\uf9eb\\uf9ec\\uf9ed\\uf9ee\\uf9ef\\uf9f0\\uf9f1" +
               "\\uf9f2\\uf9f3\\uf9f4\\uf9f5\\uf9f6\\uf9f7\\uf9f8\\uf9f9\\uf9fa\\uf9fb\\uf9fc\\uf9fd\\uf9fe\\uf9ff" +
               "\\ufa00\\ufa01\\ufa02\\ufa03\\ufa04\\ufa05\\ufa06\\ufa07\\ufa08\\ufa09\\ufa0a\\ufa0b\\ufa0c\\ufa0d" +
               "\\ufa10\\ufa12\\ufa15\\ufa16\\ufa17\\ufa18\\ufa19\\ufa1a\\ufa1b\\ufa1c\\ufa1d\\ufa1e\\ufa20\\ufa22" +
               "\\ufa25\\ufa26\\ufa2a\\ufa2b\\ufa2c\\ufa2d\\ufa30\\ufa31\\ufa32\\ufa33\\ufa34\\ufa35\\ufa36\\ufa37" +
               "\\ufa38\\ufa39\\ufa3a\\ufa3b\\ufa3c\\ufa3d\\ufa3e\\ufa3f\\ufa40\\ufa41\\ufa42\\ufa43\\ufa44\\ufa45" +
               "\\ufa46\\ufa47\\ufa48\\ufa49\\ufa4a\\ufa4b\\ufa4c\\ufa4d\\ufa4e\\ufa4f\\ufa50\\ufa51\\ufa52\\ufa53" +
               "\\ufa54\\ufa55\\ufa56\\ufa57\\ufa58\\ufa59\\ufa5a\\ufa5b\\ufa5c\\ufa5d\\ufa5e\\ufa5f\\ufa60\\ufa61" +
               "\\ufa62\\ufa63\\ufa64\\ufa65\\ufa66\\ufa67\\ufa68\\ufa69\\ufa6a\\ufa6b\\ufa6c\\ufa6d\\ufa70\\ufa71" +
               "\\ufa72\\ufa73\\ufa74\\ufa75\\ufa76\\ufa77\\ufa78\\ufa79\\ufa7a\\ufa7b\\ufa7c\\ufa7d\\ufa7e\\ufa7f" +
               "\\ufa80\\ufa81\\ufa82\\ufa83\\ufa84\\ufa85\\ufa86\\ufa87\\ufa88\\ufa89\\ufa8a\\ufa8b\\ufa8c\\ufa8d" +
               "\\ufa8e\\ufa8f\\ufa90\\ufa91\\ufa92\\ufa93\\ufa94\\ufa95\\ufa96\\ufa97\\ufa98\\ufa99\\ufa9a\\ufa9b" +
               "\\ufa9c\\ufa9d\\ufa9e\\ufa9f\\ufaa0\\ufaa1\\ufaa2\\ufaa3\\ufaa4\\ufaa5\\ufaa6\\ufaa7\\ufaa8\\ufaa9" +
               "\\ufaaa\\ufaab\\ufaac\\ufaad\\ufaae\\ufaaf\\ufab0\\ufab1\\ufab2\\ufab3\\ufab4\\ufab5\\ufab6\\ufab7" +
               "\\ufab8\\ufab9\\ufaba\\ufabb\\ufabc\\ufabd\\ufabe\\ufabf\\ufac0\\ufac1\\ufac2\\ufac3\\ufac4\\ufac5" +
               "\\ufac6\\ufac7\\ufac8\\ufac9\\ufaca\\ufacb\\ufacc\\ufacd\\uface\\ufacf\\ufad0\\ufad1\\ufad2\\ufad3" +
               "\\ufad4\\ufad5\\ufad6\\ufad7\\ufad8\\ufad9\\ufb1d\\ufb1f\\ufb2a\\ufb2b\\ufb2c\\ufb2d\\ufb2e\\ufb2f" +
               "\\ufb30\\ufb31\\ufb32\\ufb33\\ufb34\\ufb35\\ufb36\\ufb38\\ufb39\\ufb3a\\ufb3b\\ufb3c\\ufb3e\\ufb40" +
               "\\ufb41\\ufb43\\ufb44\\ufb46\\ufb47\\ufb48\\ufb49\\ufb4a\\ufb4b\\ufb4c\\ufb4d\\ufb4e\\ufeff\\ufff0" +
               "\\ufff1\\ufff2\\ufff3\\ufff4\\ufff5\\ufff6\\ufff7\\ufff8\\ufff9\\ufffa\\ufffb\\ufffc\\ufffd\\ufffe" +
               "\\uffff";
    }

    private static String serverKillerStringEsc() {
        return "\\u200c\\u200d\\u200e\\u200f\\u2028\\u2029\\u202a\\u202b\\u202c\\u202d\\u202e\\u202f\\u2060\\u2061" +
               "\\u2062\\u2063\\u2064\\u2065\\u2066\\u2067\\u2068\\u2069\\u206a\\u206b\\u206c\\u206d\\u206e\\u206f" +
               "\\ufff0\\ufff1\\ufff2\\ufff3\\ufff4\\ufff5\\ufff6\\ufff7\\ufff8\\ufff9\\ufffa\\ufffb\\ufffc\\ufffd" +
               "\\ufffe\\uffff";
    }

    private static void assertSetCookie(final String transportPath) {
        final String serviceName = "/cookie_needed_echo";
        final String sessionUrl = serviceName + "/abc/" + UUID.randomUUID().toString();
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).cookiesNeeded().build();
        final EmbeddedChannel ch = channelForService(echoService(config));
        removeLastInboundMessageHandlers(ch);
        final FullHttpRequest request = httpRequest(sessionUrl + transportPath, GET);
        ch.writeInbound(request);
        final HttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertSetCookie("dummy", response);
    }

    private static void assertSetCookie(final String sessionId, final HttpResponse response) {
        final String setCookie = response.headers().get(SET_COOKIE);
        final String[] split = SEMICOLON.split(setCookie);
        assertThat(split[0], equalTo("JSESSIONID=" + sessionId));
        assertThat(split[1].trim().toLowerCase(), equalTo("path=/"));
    }

    private static JsonNode infoAsJson(final FullHttpResponse response) throws Exception {
        final ObjectMapper om = new ObjectMapper();
        return om.readTree(response.content().toString(UTF_8));
    }

    private static void assertOpenFrameResponse(final FullHttpResponse response) {
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.content().toString(UTF_8), equalTo("o\n"));
    }

    private static void verifyHeaders(final WebSocketVersion version) throws Exception {
        final SockJsConfig config = SockJsConfig.withPrefix("/echo").build();
        final EmbeddedChannel ch = ChannelUtil.webSocketChannel(config);
        final FullHttpRequest request = HttpUtil.webSocketUpgradeRequest("/websocket", version);
        ch.writeInbound(request);
        final HttpResponse response = HttpUtil.decode(ch);
        assertThat(response.getStatus(), is(HttpResponseStatus.SWITCHING_PROTOCOLS));
        assertThat(response.headers().get(CONNECTION), equalTo("Upgrade"));
        assertThat(response.headers().get(UPGRADE), equalTo("websocket"));
        assertThat(response.headers().get(CONTENT_LENGTH), is(nullValue()));
    }

    private static FullHttpRequest webSocketUpgradeRequest(final String path, final WebSocketVersion version) {
        final FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, GET, path);
        req.headers().set(HOST, "server.test.com");
        req.headers().set(UPGRADE, WEBSOCKET.toString());
        req.headers().set(CONNECTION, "Upgrade");

        if (version == WebSocketVersion.V00) {
            req.headers().set(CONNECTION, "Upgrade");
            req.headers().set(SEC_WEBSOCKET_KEY1, "4 @1  46546xW%0l 1 5");
            req.headers().set(SEC_WEBSOCKET_KEY2, "12998 5 Y3 1  .P00");
            req.headers().set(ORIGIN, "http://example.com");
            final ByteBuf byteBuf = Unpooled.copiedBuffer("^n:ds[4U", CharsetUtil.US_ASCII);
            req.content().writeBytes(byteBuf);
            byteBuf.release();
        } else {
            req.headers().set(SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
            req.headers().set(SEC_WEBSOCKET_ORIGIN, "http://test.com");
            req.headers().set(SEC_WEBSOCKET_VERSION, version.toHttpHeaderValue());
        }
        return req;
    }

    private static SockJsServiceFactory closeService() {
        final SockJsConfig config = SockJsConfig.withPrefix("/close").build();
        return new AbstractSockJsServiceFactory(config) {
            @Override
            public SockJsService create() {
                return new CloseService(config);
            }
        };
    }

    private static SockJsServiceFactory closeService(final SockJsConfig config) {
        return new AbstractSockJsServiceFactory(config) {
            @Override
            public SockJsService create() {
                return new CloseService(config);
            }
        };
    }

    private static SockJsServiceFactory echoService() {
        final SockJsConfig config = SockJsConfig.withPrefix("/echo").cookiesNeeded().build();
        return new AbstractSockJsServiceFactory(config) {
            @Override
            public SockJsService create() {
                return new EchoService(config);
            }
        };
    }

    private static final EchoService singletonEchoService =
            new EchoService(SockJsConfig.withPrefix("/echo").cookiesNeeded().build());

    private static SockJsServiceFactory singletonEchoService() {
        return new AbstractSockJsServiceFactory(singletonEchoService.config()) {
            @Override
            public SockJsService create() {
                return singletonEchoService;
            }
        };
    }

    private static SockJsServiceFactory echoService(final SockJsConfig config) {
        return new AbstractSockJsServiceFactory(config) {
            @Override
            public SockJsService create() {
                return new EchoService(config);
            }
        };
    }

    private static void webSocketTestTransport(final WebSocketVersion version) {
        final String serviceName = "/echo";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).build();
        final SockJsServiceFactory service = echoService(config);
        final EmbeddedChannel ch = wsChannelForService(service);

        final FullHttpRequest request = webSocketUpgradeRequest(sessionUrl + "/websocket", version);
        ch.writeInbound(request);
        // Discard the HTTP Response (this will be a ByteBuf and not an object
        // as we have a HttpEncoder is in the pipeline to start with.
        ch.readOutbound();

        final TextWebSocketFrame openFrame = (TextWebSocketFrame) readOutboundDiscardEmpty(ch);
        assertThat(openFrame.content().toString(UTF_8), equalTo("o"));
        ch.readOutbound();

        final TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame("\"a\"");
        ch.writeInbound(textWebSocketFrame);

        final TextWebSocketFrame textFrame = ch.readOutbound();
        assertThat(textFrame.content().toString(UTF_8), equalTo("a[\"a\"]"));
    }

    private static void webSocketTestClose(final WebSocketVersion version) {
        final String serviceName = "/close";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).build();
        final SockJsServiceFactory service = closeService(config);
        final EmbeddedChannel ch = wsChannelForService(service);

        final FullHttpRequest request = webSocketUpgradeRequest(sessionUrl + "/websocket", version.toHttpHeaderValue());
        ch.writeInbound(request);

        // read and discard the HTTP Response (this will be a ByteBuf and not an object
        // as we have a HttpEncoder in the pipeline to start with.
        ch.readOutbound();

        final TextWebSocketFrame openFrame = (TextWebSocketFrame) readOutboundDiscardEmpty(ch);
        assertThat(openFrame.content().toString(UTF_8), equalTo("o"));

        final TextWebSocketFrame closeFrame = ch.readOutbound();
        assertThat(closeFrame.content().toString(UTF_8), equalTo("c[3000,\"Go away!\"]"));
        assertThat(ch.isActive(), is(false));
    }

    private static void webSocketTestBrokenJSON(final WebSocketVersion version) {
        final String serviceName = "/close";
        final String sessionUrl = serviceName + "/222/" + UUID.randomUUID().toString();
        final SockJsConfig config = SockJsConfig.withPrefix(serviceName).build();
        final EmbeddedChannel ch = wsChannelForService(echoService(config));

        final FullHttpRequest request = webSocketUpgradeRequest(sessionUrl + "/websocket", version.toHttpHeaderValue());
        ch.writeInbound(request);

        // read and discard the HTTP Response (this will be a ByteBuf and not an object
        // as we have a HttpEncoder in the pipeline to start with.
        ch.readOutbound();

        assertThat(((ByteBufHolder) readOutboundDiscardEmpty(ch)).content().toString(UTF_8), equalTo("o"));

        final TextWebSocketFrame webSocketFrame = new TextWebSocketFrame("[\"a\"");
        ch.writeInbound(webSocketFrame);
        assertThat(ch.isActive(), is(false));
    }

    private static FullHttpRequest webSocketUpgradeRequest(final String path) {
        return webSocketUpgradeRequest(path, "13");
    }

    private static FullHttpRequest webSocketUpgradeRequest(final String path, final String version) {
        final FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, GET, path);
        req.headers().set(HOST, "server.test.com");
        req.headers().set(UPGRADE, WEBSOCKET.toString());
        req.headers().set(CONNECTION, "Upgrade");
        req.headers().set(SEC_WEBSOCKET_KEY, "dGhlIHNhbXBsZSBub25jZQ==");
        req.headers().set(SEC_WEBSOCKET_ORIGIN, "http://test.com");
        req.headers().set(SEC_WEBSOCKET_VERSION, version);
        return req;
    }

    private static String generateMessage(final int characters) {
        final StringBuilder sb = new StringBuilder();
        for (int i = 0; i < characters; i++) {
            sb.append('x');
        }
        return sb.toString();
    }

    private static void assertBrokenJSONEncoding(final FullHttpResponse response) {
        assertThat(response.getStatus(), is(HttpResponseStatus.INTERNAL_SERVER_ERROR));
        assertThat(response.content().toString(UTF_8), equalTo("Broken JSON encoding."));
    }

    private static void assertPayloadExpected(final FullHttpResponse response) {
        assertThat(response.getStatus(), is(HttpResponseStatus.INTERNAL_SERVER_ERROR));
        assertThat(response.content().toString(UTF_8), equalTo("Payload expected."));
    }

    private static FullHttpResponse jsonpRequest(final String url, final SockJsServiceFactory service) {
        final FullHttpRequest request = httpRequest(url, GET);
        final EmbeddedChannel ch = jsonpChannelForService(service);
        removeLastInboundMessageHandlers(ch);
        ch.writeInbound(request);
        final FullHttpResponse response = ch.readOutbound();
        ch.finish();
        return response;
    }

    private static FullHttpResponse jsonpSend(final FullHttpRequest request, final SockJsServiceFactory service) {
        final EmbeddedChannel ch = jsonpChannelForService(service);
        removeLastInboundMessageHandlers(ch);
        ch.writeInbound(request);
        Object out;
        try {
            while ((out = ch.readOutbound()) != null) {
                if (out instanceof FullHttpResponse) {
                    return (FullHttpResponse) out;
                }
            }
        } finally {
            ch.finish();
        }
        throw new IllegalStateException("No outbound FullHttpResponse was written");
    }

    private static FullHttpResponse jsonpSend(final String url, final String content,
                                              final SockJsServiceFactory service) {
        final FullHttpRequest request = httpRequest(url, POST);
        request.headers().set(CONTENT_TYPE, Transports.CONTENT_TYPE_FORM);
        final ByteBuf buf = Unpooled.copiedBuffer(content, UTF_8);
        request.content().writeBytes(buf);
        buf.release();
        return jsonpSend(request, service);
    }

    private static FullHttpResponse xhrSendRequest(final String path, final String content,
                                                   final SockJsServiceFactory service) {
        final EmbeddedChannel ch = channelForService(service);
        removeLastInboundMessageHandlers(ch);
        final FullHttpRequest sendRequest = httpRequest(path + "/xhr_send", POST);
        final ByteBuf byteBuf = Unpooled.copiedBuffer(content, UTF_8);
        sendRequest.content().writeBytes(byteBuf);
        byteBuf.release();
        ch.writeInbound(sendRequest);
        final FullHttpResponse response = ch.readOutbound();
        ch.finish();
        return response;
    }

    private static FullHttpResponse xhrSendRequest(final String path,
                                                   final String content,
                                                   final String contentType,
                                                   final SockJsServiceFactory service) {
        final EmbeddedChannel ch = channelForService(service);
        removeLastInboundMessageHandlers(ch);
        final FullHttpRequest request = httpRequest(path + Transports.Type.XHR_SEND.path(), POST);
        request.headers().set(CONTENT_TYPE, contentType);
        final ByteBuf byteBuf = Unpooled.copiedBuffer(content, UTF_8);
        request.content().writeBytes(byteBuf);
        byteBuf.release();
        ch.writeInbound(request);
        Object out;
        try {
            while ((out = ch.readOutbound()) != null) {
                if (out instanceof FullHttpResponse) {
                    return (FullHttpResponse) out;
                }
            }
        } finally {
            ch.finish();
        }
        throw new IllegalStateException("No outbound FullHttpResponse was written");
    }

    private static FullHttpResponse xhrRequest(final String url, final SockJsServiceFactory service) {
        final EmbeddedChannel ch = channelForService(service);
        removeLastInboundMessageHandlers(ch);
        final FullHttpRequest request = httpRequest(url + Transports.Type.XHR.path(), GET);
        ch.writeInbound(request);
        final FullHttpResponse response = ch.readOutbound();
        ch.finish();
        return response;
    }

    private static FullHttpResponse infoRequest(final String url, final SockJsServiceFactory service) {
        final EmbeddedChannel ch = channelForService(service);
        final FullHttpRequest request = httpRequest(url + "/info", GET);
        ch.writeInbound(request);
        final FullHttpResponse response = ch.readOutbound();
        ch.finish();
        return response;
    }

    private static HttpResponse xhrRequest(final FullHttpRequest request, final SockJsServiceFactory service) {
        final EmbeddedChannel ch = channelForService(service);
        removeLastInboundMessageHandlers(ch);
        ch.writeInbound(request);
        final HttpResponse response = ch.readOutbound();
        ch.finish();
        return response;
    }

    /*
     * This is needed as otherwise the pipeline chain will stop, and since we
     * add handler to the end of the pipeline our handler would otherwise
     * not get called.
     */
    private static void removeLastInboundMessageHandlers(final EmbeddedChannel ch) {
        ch.pipeline().remove("EmbeddedChannel$LastInboundHandler#0");
    }

    private static void assertOKResponse(final String sessionPart) {
        final SockJsConfig config = SockJsConfig.withPrefix("/echo").cookiesNeeded().build();
        final EmbeddedChannel ch = channelForMockService(config);
        removeLastInboundMessageHandlers(ch);
        ch.writeInbound(httpRequest("/echo" + sessionPart + Transports.Type.XHR.path()));
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), is(HttpResponseStatus.OK));
        assertThat(response.content().toString(UTF_8), equalTo("o\n"));
    }

    private static void assertCORSPreflightResponseHeaders(final HttpResponse response, HttpMethod... methods) {
        final HttpHeaders headers = response.headers();
        assertThat(headers.get(CONTENT_TYPE), is("text/plain; charset=UTF-8"));
        assertThat(headers.get(CACHE_CONTROL), containsString("public"));
        assertThat(headers.get(CACHE_CONTROL), containsString("max-age=31536000"));
        assertThat(headers.get(ACCESS_CONTROL_ALLOW_CREDENTIALS), is("true"));
        assertThat(headers.get(ACCESS_CONTROL_MAX_AGE), is("31536000"));
        for (HttpMethod method : methods) {
            assertThat(headers.get(ACCESS_CONTROL_ALLOW_METHODS), containsString(method.toString()));
        }
        assertThat(headers.get(ACCESS_CONTROL_ALLOW_HEADERS), is("Content-Type"));
        assertThat(headers.get(ACCESS_CONTROL_ALLOW_CREDENTIALS), is("true"));
        assertThat(headers.get(EXPIRES), is(notNullValue()));
        assertThat(headers.get(SET_COOKIE), is("JSESSIONID=dummy;path=/"));
    }

    private static void verifyIframe(final String service, final String path) {
        final SockJsConfig config = SockJsConfig.withPrefix(service).cookiesNeeded().build();
        final EmbeddedChannel ch = channelForMockService(config);
        ch.writeInbound(httpRequest(config.prefix() + path));
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus().code(), is(HttpResponseStatus.OK.code()));
        assertThat(response.headers().get(CONTENT_TYPE), equalTo("text/html; charset=UTF-8"));
        assertThat(response.headers().get(CACHE_CONTROL), equalTo("max-age=31536000, public"));
        assertThat(response.headers().get(EXPIRES), is(notNullValue()));
        verifyNoSET_COOKIE(response);
        assertThat(response.headers().get(ETAG), is(notNullValue()));
        assertThat(response.content().toString(UTF_8), equalTo(iframeHtml(config.sockJsUrl())));
    }

    private static void verifyContentType(final HttpResponse response, final String contentType) {
        assertThat(response.headers().get(CONTENT_TYPE), equalTo(contentType));
    }

    private static void verifyNoSET_COOKIE(final HttpResponse response) {
        assertThat(response.headers().get(SET_COOKIE), is(nullValue()));
    }

    private static void verifyNotCached(final HttpResponse response) {
        assertThat(response.headers().get(CACHE_CONTROL), containsString("no-store"));
        assertThat(response.headers().get(CACHE_CONTROL), containsString("no-cache"));
        assertThat(response.headers().get(CACHE_CONTROL), containsString("must-revalidate"));
        assertThat(response.headers().get(CACHE_CONTROL), containsString("max-age=0"));
    }

    private static long getEntropy(final FullHttpResponse response) throws Exception {
        return contentAsJson(response).get("entropy").asLong();
    }

    private static void assertNotFoundResponse(final String service, final String path) {
        final SockJsConfig config = SockJsConfig.withPrefix(service).cookiesNeeded().build();
        final EmbeddedChannel ch = channelForMockService(config);
        ch.writeInbound(httpRequest('/' + service + path));
        final FullHttpResponse response = ch.readOutbound();
        assertThat(response.getStatus(), is(HttpResponseStatus.NOT_FOUND));
    }

    private static String getEtag(final FullHttpResponse response) {
        return response.headers().get(ETAG);
    }

    private static JsonNode contentAsJson(final FullHttpResponse response) throws Exception {
        final ObjectMapper om = new ObjectMapper();
        return om.readTree(response.content().toString(UTF_8));
    }

    private static FullHttpRequest httpRequest(final String path) {
        return new DefaultFullHttpRequest(HTTP_1_1, GET, path);
    }

    private static FullHttpRequest httpRequest(final String path, HttpMethod method) {
        return new DefaultFullHttpRequest(HTTP_1_1, method, path);
    }

    private static FullHttpRequest httpPostRequest(final String path, HttpVersion version) {
        return new DefaultFullHttpRequest(version, POST, path);
    }

    private static FullHttpRequest httpGetRequest(final String path, final HttpVersion version) {
        return new DefaultFullHttpRequest(version, GET, path);
    }

    private static EmbeddedChannel channelForMockService(final SockJsConfig config) {
        final SockJsService service = mock(SockJsService.class);
        return channelForService(factoryFor(service, config));
    }

    private static SockJsServiceFactory factoryFor(final SockJsService service, final SockJsConfig config) {
        final SockJsServiceFactory factory = mock(SockJsServiceFactory.class);
        when(service.config()).thenReturn(config);
        when(factory.config()).thenReturn(config);
        when(factory.create()).thenReturn(service);
        return factory;
    }

    private static EmbeddedChannel channelForService(final SockJsServiceFactory service) {
        return new TestEmbeddedChannel(
                new CorsInboundHandler(),
                new SockJsHandler(service),
                new CorsOutboundHandler());
    }

    private static EmbeddedChannel wsChannelForService(final SockJsServiceFactory service) {
        final EmbeddedChannel ch = new TestEmbeddedChannel(
                        new HttpRequestDecoder(),
                        new HttpResponseEncoder(),
                        new CorsInboundHandler(),
                        new SockJsHandler(service),
                        new CorsOutboundHandler(),
                        new WsCodecRemover());
        removeLastInboundMessageHandlers(ch);
        return ch;
    }

    private static FullHttpResponse infoForMockService(final SockJsServiceFactory factory) {
        final EmbeddedChannel ch = channelForMockService(factory.config());
        ch.writeInbound(httpRequest(factory.config().prefix() + "/info"));
        final FullHttpResponse response = ch.readOutbound();
        ch.close();
        return response;
    }

    private static EmbeddedChannel jsonpChannelForService(final SockJsServiceFactory service) {
        return new TestEmbeddedChannel(new CorsInboundHandler(),
                new SockJsHandler(service),
                new WsCodecRemover());
    }

    private static Object readOutboundDiscardEmpty(final EmbeddedChannel ch) {
        final Object obj = ch.readOutbound();
        if (obj instanceof ByteBuf) {
            final ByteBuf buf = (ByteBuf) obj;
            if (buf.capacity() == 0) {
                return ch.readOutbound();
            }
        }
        return obj;
    }

    private static class WsCodecRemover extends ChannelHandlerAdapter {

        @Override
        public void write(final ChannelHandlerContext ctx, final Object msg,
                final ChannelPromise channelPromise) throws Exception {
            // Remove WebSocket encoder so that we can assert the plain WebSocketFrame
            if (ctx.pipeline().get("wsencoder") != null) {
                ctx.pipeline().remove("wsencoder");
            }
            // Remove WebSocket encoder so that we can assert the plain WebSocketFrame
            if (ctx.pipeline().get("wsdecoder") != null) {
                ctx.pipeline().remove("wsdecoder");
            }
            ctx.writeAndFlush(msg, channelPromise);
        }
    }

    private static String iframeHtml(final String sockJSUrl) {
        return "<!DOCTYPE html>\n" + "<html>\n" + "<head>\n"
                + "  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n"
                + "  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n" + "  <script>\n"
                + "    document.domain = document.domain;\n"
                + "    _sockjs_onload = function(){SockJS.bootstrap_iframe();};\n" + "  </script>\n"
                + "  <script src=\"" + sockJSUrl + "\"></script>\n" + "</head>\n" + "<body>\n"
                + "  <h2>Don't panic!</h2>\n"
                + "  <p>This is a SockJS hidden iframe. It's used for cross domain magic.</p>\n" + "</body>\n"
                + "</html>";
    }

    private static class TestEmbeddedChannel extends EmbeddedChannel {

        public TestEmbeddedChannel(final ChannelHandler... handlers) {
            super(handlers);
        }

        @Override
        public Unsafe unsafe() {
            final AbstractUnsafe delegate = super.newUnsafe();
            return new TestUnsafe(delegate, new StubEmbeddedEventLoop(super.eventLoop()));
        }

        private class TestUnsafe implements Unsafe {

            private final Unsafe delegate;
            private final ChannelHandlerInvoker invoker;

            public TestUnsafe(final Unsafe delegate, final ChannelHandlerInvoker invoker) {
                this.delegate = delegate;
                this.invoker = invoker;
            }

            @Override
            public ChannelHandlerInvoker invoker() {
                return invoker;
            }

            @Override
            public SocketAddress localAddress() {
                return delegate.localAddress();
            }

            @Override
            public SocketAddress remoteAddress() {
                return delegate.remoteAddress();
            }

            @Override
            public void register(ChannelPromise promise) {
                delegate.register(promise);
            }

            @Override
            public void bind(SocketAddress localAddress, ChannelPromise promise) {
                delegate.bind(localAddress, promise);
            }

            @Override
            public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
                delegate.connect(remoteAddress, localAddress, promise);
            }

            @Override
            public void disconnect(ChannelPromise promise) {
                delegate.disconnect(promise);
            }

            @Override
            public void close(ChannelPromise promise) {
                delegate.close(promise);
            }

            @Override
            public void closeForcibly() {
                delegate.closeForcibly();
            }

            @Override
            public void beginRead() {
                delegate.beginRead();
            }

            @Override
            public void write(Object msg, ChannelPromise promise) {
                delegate.write(msg, promise);
            }

            @Override
            public void flush() {
                delegate.flush();
            }

            @Override
            public ChannelPromise voidPromise() {
                return delegate.voidPromise();
            }

            @Override
            public ChannelOutboundBuffer outboundBuffer() {
                return delegate.outboundBuffer();
            }
        }

    }


}
TOP

Related Classes of org.jboss.aerogear.io.netty.handler.codec.sockjs.protocol.SockJsProtocolTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.