@Override
  public NextAction handleWrite(FilterChainContext context) throws IOException {
    Connection<?> connection = context.getConnection();
    GrizzlyChannel channel = GrizzlyChannel.getOrAddChannel(connection, url, handler);
    try {
      UnsafeByteArrayOutputStream output = new UnsafeByteArrayOutputStream(1024); // 不需要关闭

      if (!(context.getMessage() instanceof Response)) {
        downstreamCodec.encode(channel, output, context.getMessage());
      } else {
        upstreamCodec.encode(channel, output, context.getMessage());
      }

      GrizzlyChannel.removeChannelIfDisconnectd(connection);
      byte[] bytes = output.toByteArray();
      Buffer buffer = connection.getTransport().getMemoryManager().allocate(bytes.length);
      buffer.put(bytes);
      buffer.flip();
      buffer.allowBufferDispose(true);
      context.setMessage(buffer);
    } finally {
      GrizzlyChannel.removeChannelIfDisconnectd(connection);
    }
    return context.getInvokeAction();
  }
  @Test
  public void testShutdownHandler() throws Exception {
    final FutureImpl<Boolean> shutdownFuture = SafeFutureImpl.create();
    final ShutdownHandler shutDownHandler =
        new ShutdownHandler() {

          @Override
          public void onShutdown(Connection initiator) {
            shutdownFuture.result(true);
          }
        };

    AjpAddOn myAjpAddon =
        new AjpAddOn() {

          @Override
          protected AjpHandlerFilter createAjpHandlerFilter() {
            final AjpHandlerFilter filter = new AjpHandlerFilter();
            filter.addShutdownHandler(shutDownHandler);
            return filter;
          }
        };

    final NetworkListener listener = httpServer.getListener(LISTENER_NAME);

    listener.deregisterAddOn(ajpAddon);
    listener.registerAddOn(myAjpAddon);

    startHttpServer(
        new HttpHandler() {

          @Override
          public void service(Request request, Response response) throws Exception {}
        },
        "/");

    final MemoryManager mm = listener.getTransport().getMemoryManager();
    final Buffer request = mm.allocate(512);
    request.put((byte) 0x12);
    request.put((byte) 0x34);
    request.putShort((short) 1);
    request.put(AjpConstants.JK_AJP13_SHUTDOWN);
    request.flip();

    send("localhost", PORT, request);
    final Boolean b = shutdownFuture.get(10, TimeUnit.SECONDS);
    assertTrue(b);
  }
  @Test
  public void testPingPong() throws Exception {
    startHttpServer(
        new HttpHandler() {

          @Override
          public void service(Request request, Response response) throws Exception {
            response.setStatus(200, "FINE");
          }
        },
        "/");

    final MemoryManager mm =
        httpServer.getListener(LISTENER_NAME).getTransport().getMemoryManager();
    final Buffer request = mm.allocate(512);
    request.put((byte) 0x12);
    request.put((byte) 0x34);
    request.putShort((short) 1);
    request.put(AjpConstants.JK_AJP13_CPING_REQUEST);
    request.flip();

    final Future<Buffer> responseFuture = send("localhost", PORT, request);
    Buffer response = responseFuture.get(10, TimeUnit.SECONDS);

    assertEquals('A', response.get());
    assertEquals('B', response.get());
    assertEquals((short) 1, response.getShort());
    assertEquals(AjpConstants.JK_AJP13_CPONG_REPLY, response.get());

    final AjpForwardRequestPacket headersPacket =
        new AjpForwardRequestPacket("GET", "/TestServlet/normal", 80, PORT);
    headersPacket.addHeader("Host", "localhost:80");
    send(headersPacket.toByteArray());

    AjpResponse ajpResponse = Utils.parseResponse(readAjpMessage());
    Assert.assertEquals("FINE", ajpResponse.getResponseMessage());
  }