@Override
  public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
    if (!ctx.channel().isOpen()) {
      logger.debug("Channel closed when write. Channel: {}", ctx.channel());
      promise.setFailure(new ChannelException("Channel has been closed when write."));
      ReferenceCountUtil.release(msg);
      return;
    }
    if (!(msg instanceof Send)) {
      ctx.write(msg, promise);
      return;
    }
    Send send = (Send) msg;

    Http2Headers http2Headers;
    if (forServer) {
      logger.trace("Write content to channel as server {}", ctx.channel());
      http2Headers = new DefaultHttp2Headers().status(HttpResponseStatus.OK.codeAsText());
    } else {
      logger.trace("Write content to channel as client {}", ctx.channel());
      http2Headers = new DefaultHttp2Headers().method(HttpMethod.POST.asciiName()).scheme("https").path("/");
    }
    DefaultHttp2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(http2Headers, false);
    ctx.write(headersFrame);

    // Referencing counting for derived {@link ByteBuf}: https://netty.io/wiki/reference-counted-objects.html#derived-buffers
    try {
      while (send.content().isReadable(maxFrameSize)) {
        ByteBuf slice = send.content().readSlice(maxFrameSize);
        slice.retain();
        DefaultHttp2DataFrame dataFrame = new DefaultHttp2DataFrame(slice, false);
        ctx.write(dataFrame);
      }
      // The last slice
      ByteBuf slice = send.content().readSlice(send.content().readableBytes());
      slice.retain();
      DefaultHttp2DataFrame dataFrame = new DefaultHttp2DataFrame(slice, true);
      ctx.write(dataFrame, promise);
    } catch (Exception e) {
      logger.error("Error while processing frames. Channel: {}", ctx.channel(), e);
    } finally {
      send.content().release();
    }
  }
Handles conversion of {@link Send} to HTTP/2 frames.
ambry-network/src/main/java/com/github/ambry/network/http2/AmbrySendToHttp2Adaptor.java