/**
   * Provides translation between HTTP/2 and HTTP header objects while ensuring the stream is in a
   * valid state for additional headers.
   *
   * @param ctx The context for which this message has been received. Used to send informational
   *     header if detected.
   * @param streamId The stream id the {@code headers} apply to
   * @param headers The headers to process
   * @param endOfStream {@code true} if the {@code streamId} has received the end of stream flag
   * @param allowAppend
   *     <ul>
   *       <li>{@code true} if headers will be appended if the stream already exists.
   *       <li>if {@code false} and the stream already exists this method returns {@code null}.
   *     </ul>
   *
   * @param appendToTrailer
   *     <ul>
   *       <li>{@code true} if a message {@code streamId} already exists then the headers should be
   *           added to the trailing headers.
   *       <li>{@code false} then appends will be done to the initial headers.
   *     </ul>
   *
   * @return The object used to track the stream corresponding to {@code streamId}. {@code null} if
   *     {@code allowAppend} is {@code false} and the stream already exists.
   * @throws Http2Exception If the stream id is not in the correct state to process the headers
   *     request
   */
  protected FullHttpMessage processHeadersBegin(
      ChannelHandlerContext ctx,
      int streamId,
      Http2Headers headers,
      boolean endOfStream,
      boolean allowAppend,
      boolean appendToTrailer)
      throws Http2Exception {
    FullHttpMessage msg = messageMap.get(streamId);
    if (msg == null) {
      msg = newMessage(streamId, headers, validateHttpHeaders);
    } else if (allowAppend) {
      try {
        HttpConversionUtil.addHttp2ToHttpHeaders(streamId, headers, msg, appendToTrailer);
      } catch (Http2Exception e) {
        removeMessage(streamId);
        throw e;
      }
    } else {
      msg = null;
    }

    if (sendDetector.mustSendImmediately(msg)) {
      // Copy the message (if necessary) before sending. The content is not expected to be copied
      // (or used) in
      // this operation but just in case it is used do the copy before sending and the resource may
      // be released
      final FullHttpMessage copy = endOfStream ? null : sendDetector.copyIfNeeded(msg);
      fireChannelRead(ctx, msg, streamId);
      return copy;
    }

    return msg;
  }
 /**
  * Create a new {@link FullHttpMessage} based upon the current connection parameters
  *
  * @param streamId The stream id to create a message for
  * @param headers The headers associated with {@code streamId}
  * @param validateHttpHeaders
  *     <ul>
  *       <li>{@code true} to validate HTTP headers in the http-codec
  *       <li>{@code false} not to validate HTTP headers in the http-codec
  *     </ul>
  *
  * @throws Http2Exception
  */
 protected FullHttpMessage newMessage(
     int streamId, Http2Headers headers, boolean validateHttpHeaders) throws Http2Exception {
   return connection.isServer()
       ? HttpConversionUtil.toHttpRequest(streamId, headers, validateHttpHeaders)
       : HttpConversionUtil.toHttpResponse(streamId, headers, validateHttpHeaders);
 }