private CompositeBuffer toCompositeInputContentBuffer() {
    if (!inputContentBuffer.isComposite()) {
      final CompositeBuffer compositeBuffer =
          CompositeBuffer.newBuffer(connection.getMemoryManager());

      compositeBuffer.allowBufferDispose(true);
      compositeBuffer.allowInternalBuffersDispose(true);

      int posAlign = 0;

      if (readAheadLimit > 0) { // the simple inputContentBuffer is marked
        // make the marked data still available
        inputContentBuffer.position(inputContentBuffer.position() - readCount);
        posAlign = readCount;

        markPos = 0; // for the CompositeBuffer markPos is 0
      }

      compositeBuffer.append(inputContentBuffer);
      compositeBuffer.position(posAlign);

      inputContentBuffer = compositeBuffer;
    }

    return (CompositeBuffer) inputContentBuffer;
  }
  private static Buffer appendContentChunkAndTrim(
      final MemoryManager memoryManager, final Buffer dstBuffer, final Buffer httpContentBuffer) {

    final boolean useDstBufferForHeaders =
        dstBuffer != null && dstBuffer.remaining() >= BODY_CHUNK_HEADER_SIZE;

    final Buffer headerBuffer;
    if (useDstBufferForHeaders) {
      headerBuffer = dstBuffer;
    } else {
      if (dstBuffer != null) {
        dstBuffer.trim();
      }
      headerBuffer = memoryManager.allocate(BODY_CHUNK_HEADER_SIZE);
      headerBuffer.allowBufferDispose(true);
    }

    headerBuffer.put((byte) 'A');
    headerBuffer.put((byte) 'B');
    headerBuffer.putShort((short) (4 + httpContentBuffer.remaining()));
    headerBuffer.put(AjpConstants.JK_AJP13_SEND_BODY_CHUNK);
    headerBuffer.putShort((short) httpContentBuffer.remaining());
    headerBuffer.trim();

    Buffer resultBuffer = Buffers.appendBuffers(memoryManager, headerBuffer, httpContentBuffer);

    // Add terminating \0
    final Buffer terminatingBuffer = memoryManager.allocate(1);
    terminatingBuffer.allowBufferDispose(true);

    resultBuffer = Buffers.appendBuffers(memoryManager, resultBuffer, terminatingBuffer);

    if (!useDstBufferForHeaders && dstBuffer != null) {
      resultBuffer = Buffers.appendBuffers(memoryManager, dstBuffer, resultBuffer);
    }

    if (resultBuffer.isComposite()) {
      // If during buffer appending - composite buffer was created -
      // allow buffer disposing
      ((CompositeBuffer) resultBuffer).allowInternalBuffersDispose(true);
    }

    return resultBuffer;
  }
  public void testBlockingReadWithRemainder() throws Exception {
    final String[] clientMsgs = {"YYYYY", "Hello", "from", "client"};

    Connection connection = null;
    int messageNum = 3;

    final StringFilter stringFilter = new StringFilter();

    final BlockingQueue<String> intermResultQueue = DataStructures.getLTQInstance(String.class);

    final PUFilter puFilter = new PUFilter();
    FilterChain subProtocolChain =
        puFilter
            .getPUFilterChainBuilder()
            .add(new MergeFilter(clientMsgs.length, intermResultQueue))
            .add(new EchoFilter())
            .build();

    puFilter.register(new SimpleProtocolFinder(clientMsgs[0]), subProtocolChain);

    FilterChainBuilder filterChainBuilder = FilterChainBuilder.newInstance();
    filterChainBuilder.add(new TransportFilter());
    filterChainBuilder.add(stringFilter);
    filterChainBuilder.add(puFilter);

    TCPNIOTransport transport = TCPNIOTransportBuilder.newInstance().build();
    transport.setFilterChain(filterChainBuilder.build());

    try {
      transport.bind(PORT);
      transport.start();

      final BlockingQueue<String> resultQueue = DataStructures.getLTQInstance(String.class);

      Future<Connection> future = transport.connect("localhost", PORT);
      connection = future.get(10, TimeUnit.SECONDS);
      assertTrue(connection != null);

      FilterChainBuilder clientFilterChainBuilder = FilterChainBuilder.newInstance();
      clientFilterChainBuilder.add(new TransportFilter());
      clientFilterChainBuilder.add(stringFilter);
      clientFilterChainBuilder.add(
          new BaseFilter() {

            @Override
            public NextAction handleRead(FilterChainContext ctx) throws IOException {
              resultQueue.add((String) ctx.getMessage());
              return ctx.getStopAction();
            }
          });
      final FilterChain clientFilterChain = clientFilterChainBuilder.build();

      connection.setFilterChain(clientFilterChain);

      final MemoryManager memoryManager = transport.getMemoryManager();
      for (int i = 0; i < messageNum; i++) {
        String clientMessage = "";

        CompositeBuffer bb = CompositeBuffer.newBuffer(memoryManager);

        for (int j = 0; j < clientMsgs.length; j++) {
          String msg = clientMsgs[j] + "-" + i;
          clientMessage += msg;
          Buffer buffer = stringFilter.encode(memoryManager, msg);
          bb.append(buffer);
        }

        Future<WriteResult<WritableMessage, SocketAddress>> writeFuture = connection.write(bb);

        assertTrue("Write timeout loop: " + i, writeFuture.get(10, TimeUnit.SECONDS) != null);

        for (int j = 0; j < clientMsgs.length; j++) {
          String msg = clientMsgs[j] + "-" + i;
          final String srvInterm = intermResultQueue.poll(10, TimeUnit.SECONDS);

          assertEquals("Unexpected interm. response (" + i + ", " + j + ")", msg, srvInterm);
        }

        final String message = resultQueue.poll(10, TimeUnit.SECONDS);

        assertEquals("Unexpected response (" + i + ")", clientMessage, message);
      }
    } finally {
      if (connection != null) {
        connection.closeSilently();
      }

      transport.shutdownNow();
    }
  }