@Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (msg instanceof Http2Settings) {
      // Expected
    } else if (msg instanceof FullHttpResponse) {
      FullHttpResponse response = (FullHttpResponse) msg;

      final Invocation invocation = waitsHolder.poll(response);

      if (invocation != null) {
        final ServiceInvocationContext iCtx = invocation.invocationContext();
        final SerializationFormat serializationFormat = iCtx.scheme().serializationFormat();
        try {
          final Promise<FullHttpResponse> resultPromise = invocation.resultPromise();
          if (HttpStatusClass.SUCCESS == response.status().codeClass()
              // No serialization indicates a raw HTTP protocol which should
              // have error responses returned.
              || serializationFormat == SerializationFormat.NONE) {
            iCtx.resolvePromise(resultPromise, response.retain());
          } else {
            iCtx.rejectPromise(
                resultPromise,
                new InvalidResponseException("HTTP Response code: " + response.status()));
          }
        } finally {
          ReferenceCountUtil.release(msg);
        }
      } else {
        // if invocation not found, we just bypass message to next
        ctx.fireChannelRead(msg);
      }

      if (!isMultiplex
          && HttpHeaderValues.CLOSE.contentEqualsIgnoreCase(
              response.headers().get(HttpHeaderNames.CONNECTION))) {
        ctx.close();
      }
    } else {
      try {
        throw new IllegalStateException("unexpected message type: " + msg);
      } finally {
        ReferenceCountUtil.release(msg);
      }
    }
  }
  /** Write the response */
  private void writeResponse(ChannelHandlerContext ctx) {
    // Convert the response content to a ByteBuf.
    ByteBuf buf = Unpooled.copiedBuffer(responseContent.toString(), WaarpStringUtils.UTF8);
    responseContent.setLength(0);

    // Decide whether to close the connection or not.
    boolean keepAlive = HttpUtil.isKeepAlive(request);
    boolean close =
        HttpHeaderValues.CLOSE.contentEqualsIgnoreCase(
                request.headers().get(HttpHeaderNames.CONNECTION))
            || (!keepAlive)
            || forceClose;

    // Build the response object.
    FullHttpResponse response =
        new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf);
    response.headers().add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
    response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html");
    if (keepAlive) {
      response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
    }
    if (!close) {
      // There's no need to add 'Content-Length' header
      // if this is the last response.
      response.headers().set(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
    }

    handleCookies(response);

    // Write the response.
    ChannelFuture future = ctx.writeAndFlush(response);
    // Close the connection after the write operation is done if necessary.
    if (close) {
      future.addListener(WaarpSslUtility.SSLCLOSE);
    }
    if (shutdown) {
      ChannelUtils.startShutdown();
    }
  }