@Override
  public void channelRead0(ChannelHandlerContext ctx, HttpObject obj) throws RestServiceException {
    if (isOpen()) {
      logger.trace("Reading on channel {}", ctx.channel());
      long currentTime = System.currentTimeMillis();
      if (firstMessageReceived.compareAndSet(false, true)) {
        if (ctx.pipeline().get(SslHandler.class) != null) {
          nettyMetrics.sslChannelActiveToFirstMessageReceiveTimeInMs.update(currentTime - channelActiveTimeMs);
        } else {
          nettyMetrics.channelActiveToFirstMessageReceiveTimeInMs.update(currentTime - channelActiveTimeMs);
        }
      }
      boolean recognized = false;
      boolean success = true;
      if (obj instanceof HttpRequest) {
        recognized = true;
        success = handleRequest((HttpRequest) obj);
      }
      // this is an if and not an else-if because a HttpObject can be both HttpRequest and HttpContent.
      if (success && obj instanceof HttpContent) {
        recognized = true;
        success = handleContent((HttpContent) obj);
      }
      if (success && !recognized) {
        logger.warn("Received null/unrecognized HttpObject {} on channel {}", obj, ctx.channel());
        nettyMetrics.unknownHttpObjectError.inc();
        if (responseChannel == null || requestContentFullyReceived) {
          resetState();
        }
        onRequestAborted(new RestServiceException("HttpObject received is null or not of a known type",
            RestServiceErrorCode.MalformedRequest));
      }
      if (lastChannelReadTime != null) {
        nettyMetrics.channelReadIntervalInMs.update(currentTime - lastChannelReadTime);
        logger.trace("Delay between channel reads is {} ms for channel {}", (currentTime - lastChannelReadTime),
            ctx.channel());
      }
      lastChannelReadTime = currentTime;
    } else {
      logger.debug("Read on channel {} ignored because it is inactive", ctx.channel());
    }
  }
Netty calls this function whenever data is available on the channel that can be read.
<p/>
{@link HttpRequest} is converted to {@link NettyRequest} and passed to the the {@link RestRequestHandler}.
<p/>
{@link HttpContent} is added to the {@link NettyRequest} currently being served by the channel.
@param ctx The {@link ChannelHandlerContext} that can be used to perform operations on the channel.
@param obj The {@link HttpObject} that forms a part of a request.
@throws RestServiceException if there is an error handling the processing of the current {@link HttpObject}.
ambry-rest/src/main/java/com/github/ambry/rest/NettyMessageProcessor.java