// Decode a 12-byte text frame and a 15 byte text frame written in one go (one network packet)
  private void sizeLimitDecodeDoubleTextFrame(int maxSize) throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, maxSize);

    IoBufferEx in =
        allocator
            .wrap(allocator.allocate(12 + 2 + 15 + 2))
            .put((byte) 0x81)
            .put((byte) 12)
            .putString("123456789012", UTF_8.newEncoder())
            .put((byte) 0x81)
            .put((byte) 15)
            .putString("123456789012345", UTF_8.newEncoder())
            .flip();

    decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());

    WsMessage out = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsTextMessage(allocator.wrap(ByteBuffer.wrap("123456789012".getBytes(UTF_8)))), out);

    WsMessage out2 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsTextMessage(allocator.wrap(ByteBuffer.wrap("123456789012345".getBytes(UTF_8)))),
        out2);

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    decoder.finishDecode(session, session.getDecoderOutput());

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    assertFalse(in.hasRemaining());
  }
  @Test
  public void decodeTextContinuationFrame() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 200);

    int firstPayload = 125;
    String first = createString('a', firstPayload);

    int secondPayload = 2;
    String second = createString('b', secondPayload);

    int thirdPayload = 4;
    String thrid = createString('c', thirdPayload);

    IoBufferEx in =
        allocator
            .wrap(allocator.allocate(firstPayload + secondPayload + thirdPayload + 6))
            // text frame
            .put((byte) 0x01)
            .put((byte) firstPayload)
            .putString(first, UTF_8.newEncoder())
            // continuation frame with FIN
            .put((byte) 0x80)
            .put((byte) secondPayload)
            .putString(second, UTF_8.newEncoder())
            // text frame with FIN
            .put((byte) 0x81)
            .put((byte) thirdPayload)
            .putString(thrid, UTF_8.newEncoder())
            .flip();

    decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());

    WsMessage out1 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsTextMessage(allocator.wrap(ByteBuffer.wrap(first.getBytes(UTF_8))), false), out1);

    WsMessage out2 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsContinuationMessage(allocator.wrap(ByteBuffer.wrap(second.getBytes(UTF_8)))), out2);

    WsMessage out3 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsTextMessage(allocator.wrap(ByteBuffer.wrap(thrid.getBytes(UTF_8)))), out3);

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    decoder.finishDecode(session, session.getDecoderOutput());

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    assertFalse(in.hasRemaining());
  }
  // Decode a 200-byte binary frame
  private void sizeLimitDecodeBinaryFrame(int maxSize) throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, maxSize);

    IoBufferEx in =
        allocator
            .wrap(allocator.allocate(204))
            .put((byte) 0x82)
            .put((byte) 126)
            .put((byte) 0x00)
            .put((byte) 0xC8)
            .fill(200)
            .flip();

    decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());

    WsMessage out = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsBinaryMessage(allocator.wrap(allocator.allocate(200)).fill(200).flip()), out);

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    decoder.finishDecode(session, session.getDecoderOutput());

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    assertFalse(in.hasRemaining());
  }
  @Test
  public void decodeTextFrame() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 0);

    IoBufferEx in =
        allocator
            .wrap(allocator.allocate(14))
            .put((byte) 0x81)
            .put((byte) 0x0C)
            .putString("Hello, world", UTF_8.newEncoder())
            .flip();

    decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());

    WsMessage out = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsTextMessage(allocator.wrap(ByteBuffer.wrap("Hello, world".getBytes(UTF_8)))), out);

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    decoder.finishDecode(session, session.getDecoderOutput());

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    assertFalse(in.hasRemaining());
  }
  @Test
  public void decodeZeroLengthMaskedPongFrame() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 0);

    IoBufferEx in =
        allocator
            .wrap(allocator.allocate(6))
            .put((byte) 0x8A)
            .put((byte) 0x80)
            .fill(4) // mask
            .flip();

    decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());

    WsMessage out = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsPongMessage(allocator.wrap(allocator.allocate(0)).flip()), out);

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    decoder.finishDecode(session, session.getDecoderOutput());

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    assertFalse(in.hasRemaining());
  }
  @Test
  public void decodeBinaryContinuationFrame() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 200);

    int firstPayload = 125;
    byte[] first = createString('a', firstPayload).getBytes("UTF-8");

    int secondPayload = 2;
    byte[] second = createString('b', secondPayload).getBytes("UTF-8");

    int thirdPayload = 4;
    byte[] thrid = createString('c', thirdPayload).getBytes("UTF-8");

    IoBufferEx in =
        allocator
            .wrap(allocator.allocate(firstPayload + secondPayload + thirdPayload + 6))
            // binary frame
            .put((byte) 0x02)
            .put((byte) firstPayload)
            .put(first)
            // continuation frame with FIN
            .put((byte) 0x80)
            .put((byte) secondPayload)
            .put(second)
            // binary frame with FIN
            .put((byte) 0x82)
            .put((byte) thirdPayload)
            .put(thrid)
            .flip();

    decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());

    WsMessage out1 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsBinaryMessage(allocator.wrap(ByteBuffer.wrap(first)), false), out1);

    WsMessage out2 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsContinuationMessage(allocator.wrap(ByteBuffer.wrap(second))), out2);

    WsMessage out3 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsBinaryMessage(allocator.wrap(ByteBuffer.wrap(thrid))), out3);

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    decoder.finishDecode(session, session.getDecoderOutput());

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    assertFalse(in.hasRemaining());
  }
  // Make sure we fail early for case of large text messages: we should fail as soon as we process
  // a network packet that exceeds the limit (first packet in this case)
  @Test(expected = ProtocolDecoderException.class)
  public void sizeLimitDecodeTextFrameFailEarly1() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 20);

    int dataSize = 30;
    StringBuilder data = new StringBuilder(dataSize);
    for (int i = 0; i < (dataSize); i++) {
      data.append((i % 10));
    }

    IoBufferEx in =
        allocator
            .wrap(allocator.allocate(dataSize + 2))
            .put((byte) 0x81)
            .put((byte) 30)
            .putString(data.toString(), UTF_8.newEncoder())
            .flip();
    // As soon as we sent part of a message that exceeds the limit it should throw the exception
    decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());
  }
  @Test
  public void testMaxMessageSize() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();

    try {
      IoBuffer in = (IoBuffer) getMaxMessageSizeBuffer(allocator);
      ProtocolDecoder decoder = new WsFrameDecoder(allocator, 253);
      decoder.decode(session, in, session.getDecoderOutput());
      fail("Expected throw exception as the message size > 253");
    } catch (ProtocolDecoderException e) {
      // expected exception as message size exceeds 255
    }

    IoBuffer in = (IoBuffer) getMaxMessageSizeBuffer(allocator);
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 254);
    decoder.decode(session, in, session.getDecoderOutput());

    in = (IoBuffer) getMaxMessageSizeBuffer(allocator);
    decoder = new WsFrameDecoder(allocator, 255);
    decoder.decode(session, in, session.getDecoderOutput());
  }
  // Make sure we fail early for case of large text messages: we should fail as soon as we process
  // a network packet that exceeds the limit (2nd packet in this case)
  @Test(expected = ProtocolDecoderException.class)
  public void sizeLimitDecodeTextFrameFailEarly2() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 20);

    int dataSize = 30;
    StringBuilder data = new StringBuilder(dataSize);
    for (int i = 0; i < (dataSize); i++) {
      data.append((i % 10));
    }
    IoBufferEx in =
        allocator
            .wrap(allocator.allocate(dataSize + 2))
            .put((byte) 0x81)
            .put((byte) 30)
            .putString(data.toString(), UTF_8.newEncoder())
            .flip();
    decoder.decode(session, (IoBuffer) in.getSlice(10), session.getDecoderOutput());
    // Now if we send the next 12 bytes that should exceed the limit (first byte is control byte,
    // doesn't count)
    decoder.decode(session, (IoBuffer) in.getSlice(12), session.getDecoderOutput());
  }
  @Test(expected = ProtocolDecoderException.class)
  public void decodeFragmentedContinuationFrameExceedingMaxMessageSize() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 150);

    String textFramePayload = createString('a', 100);

    IoBufferEx textFrameBuffer =
        allocator
            .wrap(allocator.allocate(102))
            // text frame
            .put((byte) 0x01)
            .put((byte) 100)
            .putString(textFramePayload, UTF_8.newEncoder())
            .flip();

    // the decoder should fail fast when message size exceeds the max message size without waiting
    // for
    // payload
    IoBufferEx continuationFrameBuffer =
        allocator
            .wrap(allocator.allocate(2))
            // continuation frame fragment (opcode and payload length)
            .put((byte) 0x80)
            .put((byte) 0x64)
            .flip();

    decoder.decode(session, (IoBuffer) textFrameBuffer, session.getDecoderOutput());

    // since the maximum message size is 150, the following statement will
    // cause the message size(200) to exceed maximum message size
    // The decoder should fail fast once the payload length is decoded regardless of the
    // availability of payload
    decoder.decode(session, (IoBuffer) continuationFrameBuffer, session.getDecoderOutput());
  }
  @Test
  public void decodeTextContinuationFrameWithFragmentedPayload() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 250);

    String textFramePayload = createString('a', 100);
    String continuationFramePayloadFirstFragment = createString('b', 50);
    String continuationFramePayloadSecondFragment = createString('b', 50);

    IoBufferEx in =
        allocator
            .wrap(allocator.allocate(102))
            // text frame
            .put((byte) 0x01)
            .put((byte) 100)
            .putString(textFramePayload, UTF_8.newEncoder())
            .flip();

    IoBufferEx[] array =
        new IoBufferEx[] {
          allocator
              .wrap(allocator.allocate(2))
              // continuation frame fragment (opcode and payload length)
              .put((byte) 0x80)
              .put((byte) 0x64)
              .flip(),
          allocator
              .wrap(allocator.allocate(50))
              // continuation frame payload first fragment
              .putString(continuationFramePayloadFirstFragment, UTF_8.newEncoder())
              .flip(),
          allocator
              .wrap(allocator.allocate(50))
              // continuation frame payload second fragment
              .putString(continuationFramePayloadSecondFragment, UTF_8.newEncoder())
              .flip()
        };
    decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());

    for (IoBufferEx buffer : array) {
      decoder.decode(session, (IoBuffer) buffer, session.getDecoderOutput());
    }

    WsMessage out1 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsTextMessage(allocator.wrap(ByteBuffer.wrap(textFramePayload.getBytes(UTF_8))), false),
        out1);

    WsMessage out2 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsContinuationMessage(
            allocator.wrap(ByteBuffer.wrap(createString('b', 100).getBytes(UTF_8)))),
        out2);
  }
  @Test
  public void decodeFragmentedBinaryFrame() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 0);

    IoBufferEx[] array =
        new IoBufferEx[] {
          allocator
              .wrap(allocator.allocate(104))
              .put((byte) 0x82)
              .put((byte) 126)
              .put((byte) 0x00)
              .put((byte) 0xC8)
              .fill(100)
              .flip(),
          allocator
              .wrap(allocator.allocate(102))
              .fill(100)
              .put((byte) 0x82)
              .put((byte) 0x00)
              .flip(),
        };

    for (IoBufferEx in : array) {
      decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());
    }

    WsMessage fragmented = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsBinaryMessage(allocator.wrap(allocator.allocate(200)).fill(200).flip()), fragmented);

    WsMessage empty = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsBinaryMessage(allocator.wrap(allocator.allocate(0))), empty);

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    decoder.finishDecode(session, session.getDecoderOutput());

    assertTrue(session.getDecoderOutputQueue().isEmpty());

    for (IoBufferEx in : array) {
      assertFalse(in.hasRemaining());
    }
  }
  @Test
  public void decodeFragmentedTextFrame() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 0);

    IoBufferEx[] array =
        new IoBufferEx[] {
          allocator
              .wrap(allocator.allocate(103))
              .put((byte) 0x81)
              .put((byte) 0x0C)
              .putString("Hello", UTF_8.newEncoder())
              .flip(),
          allocator
              .wrap(allocator.allocate(102))
              .putString(", world", UTF_8.newEncoder())
              .put((byte) 0x81)
              .put((byte) 0x00)
              .flip(),
        };

    for (IoBufferEx in : array) {
      decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());
    }

    WsMessage fragmented = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsTextMessage(allocator.wrap(ByteBuffer.wrap("Hello, world".getBytes(UTF_8)))),
        fragmented);

    WsMessage empty = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsTextMessage(allocator.wrap(allocator.allocate(0))), empty);

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    decoder.finishDecode(session, session.getDecoderOutput());

    assertTrue(session.getDecoderOutputQueue().isEmpty());

    for (IoBufferEx in : array) {
      assertFalse(in.hasRemaining());
    }
  }
  @Test
  public void decodeBinaryContinuationFrameWithFragmentedPayload() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 250);

    byte[] binaryFramePayload = createString('a', 100).getBytes();
    byte[] continuationFramePayload = createString('b', 100).getBytes();

    IoBufferEx in =
        allocator
            .wrap(allocator.allocate(102))
            // binary frame
            .put((byte) 0x02)
            .put((byte) 100)
            .put(binaryFramePayload)
            .flip();

    IoBufferEx[] array =
        new IoBufferEx[] {
          allocator
              .wrap(allocator.allocate(2))
              // continuation frame fragment (opcode and payload length)
              .put((byte) 0x80)
              .put((byte) 0x64)
              .flip(),
          allocator
              .wrap(allocator.allocate(50))
              // continuation frame first fragment
              .put(continuationFramePayload, 0, 50)
              .flip(),
          allocator
              .wrap(allocator.allocate(50))
              // continuation frame second fragment
              .put(continuationFramePayload, 50, 50)
              .flip()
        };
    decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());

    for (IoBufferEx buffer : array) {
      decoder.decode(session, (IoBuffer) buffer, session.getDecoderOutput());
    }

    WsMessage out1 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsBinaryMessage(allocator.wrap(ByteBuffer.wrap(binaryFramePayload)), false), out1);

    WsMessage out2 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsContinuationMessage(allocator.wrap(ByteBuffer.wrap(continuationFramePayload))), out2);
  }
  @Test
  public void pingInBinaryContinuationSequence() throws Exception {
    ProtocolCodecSessionEx session = new ProtocolCodecSessionEx();
    IoBufferAllocatorEx<?> allocator = session.getBufferAllocator();
    ProtocolDecoder decoder = new WsFrameDecoder(allocator, 400);

    int firstPayload = 125;
    byte[] first = createString('a', firstPayload).getBytes("UTF-8");

    int secondPayload = 125;
    byte[] second = createString('b', secondPayload).getBytes("UTF-8");

    int thirdPayload = 4;
    byte[] third = createString('c', thirdPayload).getBytes("UTF-8");

    int fourthPayload = 6;
    String fourth = createString('d', fourthPayload);

    IoBufferEx in =
        allocator
            .wrap(
                allocator.allocate(
                    firstPayload + secondPayload + 2 + thirdPayload + fourthPayload + 8))
            // text frame
            .put((byte) 0x01)
            .put((byte) firstPayload)
            .put(first)
            // continuation frame
            .put((byte) 0x00)
            .put((byte) secondPayload)
            .put(second)
            // ping frame
            .put((byte) 0x89)
            .put((byte) 0x00)
            // continuation frame with FIN
            .put((byte) 0x80)
            .put((byte) thirdPayload)
            .put(third)
            // binary frame
            .put((byte) 0x82)
            .put((byte) fourthPayload)
            .putString(fourth, UTF_8.newEncoder())
            .flip();

    decoder.decode(session, (IoBuffer) in, session.getDecoderOutput());

    WsMessage out1 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsTextMessage(allocator.wrap(ByteBuffer.wrap(first)), false), out1);

    WsMessage out2 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsContinuationMessage(allocator.wrap(ByteBuffer.wrap(second)), false), out2);

    WsMessage out = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsPingMessage(allocator.wrap(allocator.allocate(0))), out);

    WsMessage out3 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(new WsContinuationMessage(allocator.wrap(ByteBuffer.wrap(third))), out3);

    WsMessage out4 = (WsMessage) session.getDecoderOutputQueue().poll();
    assertEquals(
        new WsBinaryMessage(allocator.wrap(ByteBuffer.wrap(fourth.getBytes(UTF_8)))), out4);

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    decoder.finishDecode(session, session.getDecoderOutput());

    assertTrue(session.getDecoderOutputQueue().isEmpty());
    assertFalse(in.hasRemaining());
  }