@Override
  protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
    ByteBuf buf = null;
    if (msg instanceof HttpMessage) {
      if (state != ST_INIT) {
        throw new IllegalStateException(
            "unexpected message type: " + StringUtil.simpleClassName(msg));
      }

      @SuppressWarnings({"unchecked", "CastConflictsWithInstanceof"})
      H m = (H) msg;

      buf = ctx.alloc().buffer();
      // Encode the message.
      encodeInitialLine(buf, m);
      HttpHeaders.encode(m.headers(), buf);
      buf.writeBytes(CRLF);
      state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;
    }
    if (msg instanceof HttpContent || msg instanceof ByteBuf || msg instanceof FileRegion) {
      if (state == ST_INIT) {
        throw new IllegalStateException(
            "unexpected message type: " + StringUtil.simpleClassName(msg));
      }

      int contentLength = contentLength(msg);
      if (state == ST_CONTENT_NON_CHUNK) {
        if (contentLength > 0) {
          if (buf != null && buf.writableBytes() >= contentLength && msg instanceof HttpContent) {
            // merge into other buffer for performance reasons
            buf.writeBytes(((HttpContent) msg).content());
            out.add(buf);
          } else {
            if (buf != null) {
              out.add(buf);
            }
            out.add(encodeAndRetain(msg));
          }
        } else {
          if (buf != null) {
            out.add(buf);
          } else {
            // Need to produce some output otherwise an
            // IllegalStateException will be thrown
            out.add(EMPTY_BUFFER);
          }
        }

        if (msg instanceof LastHttpContent) {
          state = ST_INIT;
        }
      } else if (state == ST_CONTENT_CHUNK) {
        if (buf != null) {
          out.add(buf);
        }
        encodeChunkedContent(ctx, msg, contentLength, out);
      } else {
        throw new Error();
      }
    } else {
      if (buf != null) {
        out.add(buf);
      }
    }
  }
  @Override
  protected void encode(ChannelHandlerContext ctx, HttpObject msg, ByteBuf out) throws Exception {
    if (msg instanceof HttpMessage) {
      if (state != ST_INIT) {
        throw new IllegalStateException(
            "unexpected message type: " + msg.getClass().getSimpleName());
      }

      @SuppressWarnings({"unchecked", "CastConflictsWithInstanceof"})
      H m = (H) msg;

      // Encode the message.
      encodeInitialLine(out, m);
      encodeHeaders(out, m);
      out.writeByte(CR);
      out.writeByte(LF);

      state = HttpHeaders.isTransferEncodingChunked(m) ? ST_CONTENT_CHUNK : ST_CONTENT_NON_CHUNK;
    }

    if (msg instanceof HttpContent) {
      if (state == ST_INIT) {
        throw new IllegalStateException(
            "unexpected message type: " + msg.getClass().getSimpleName());
      }

      HttpContent chunk = (HttpContent) msg;
      ByteBuf content = chunk.content();
      int contentLength = content.readableBytes();

      if (state == ST_CONTENT_NON_CHUNK) {
        if (contentLength > 0) {
          out.writeBytes(content, content.readerIndex(), content.readableBytes());
        }

        if (chunk instanceof LastHttpContent) {
          state = ST_INIT;
        }
      } else if (state == ST_CONTENT_CHUNK) {
        if (contentLength > 0) {
          out.writeBytes(copiedBuffer(Integer.toHexString(contentLength), CharsetUtil.US_ASCII));
          out.writeByte(CR);
          out.writeByte(LF);
          out.writeBytes(content, content.readerIndex(), contentLength);
          out.writeByte(CR);
          out.writeByte(LF);
        }

        if (chunk instanceof LastHttpContent) {
          out.writeByte((byte) '0');
          out.writeByte(CR);
          out.writeByte(LF);
          encodeTrailingHeaders(out, (LastHttpContent) chunk);
          out.writeByte(CR);
          out.writeByte(LF);
          state = ST_INIT;
        }
      } else {
        throw new Error();
      }
    }
  }