protected void messageReceived(
      byte[] data,
      String action,
      LocalTransport sourceTransport,
      Version version,
      @Nullable final Long sendRequestId) {
    Transports.assertTransportThread();
    try {
      transportServiceAdapter.received(data.length);
      StreamInput stream = StreamInput.wrap(data);
      stream.setVersion(version);

      long requestId = stream.readLong();
      byte status = stream.readByte();
      boolean isRequest = TransportStatus.isRequest(status);
      if (isRequest) {
        ThreadContext threadContext = threadPool.getThreadContext();
        threadContext.readHeaders(stream);
        handleRequest(stream, requestId, data.length, sourceTransport, version);
      } else {
        final TransportResponseHandler handler =
            transportServiceAdapter.onResponseReceived(requestId);
        // ignore if its null, the adapter logs it
        if (handler != null) {
          if (TransportStatus.isError(status)) {
            handleResponseError(stream, handler);
          } else {
            handleResponse(stream, sourceTransport, handler);
          }
        }
      }
    } catch (Throwable e) {
      if (sendRequestId != null) {
        TransportResponseHandler handler =
            sourceTransport.transportServiceAdapter.onResponseReceived(sendRequestId);
        if (handler != null) {
          RemoteTransportException error =
              new RemoteTransportException(nodeName(), localAddress, action, e);
          sourceTransport
              .workers()
              .execute(
                  () -> {
                    ThreadContext threadContext = sourceTransport.threadPool.getThreadContext();
                    try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
                      sourceTransport.handleException(handler, error);
                    }
                  });
        }
      } else {
        logger.warn("Failed to receive message for action [{}]", e, action);
      }
    }
  }
  @Override
  public void sendRequest(
      final DiscoveryNode node,
      final long requestId,
      final String action,
      final TransportRequest request,
      TransportRequestOptions options)
      throws IOException, TransportException {
    final Version version = Version.smallest(node.getVersion(), this.version);

    try (BytesStreamOutput stream = new BytesStreamOutput()) {
      stream.setVersion(version);

      stream.writeLong(requestId);
      byte status = 0;
      status = TransportStatus.setRequest(status);
      stream.writeByte(status); // 0 for request, 1 for response.

      threadPool.getThreadContext().writeTo(stream);
      stream.writeString(action);
      request.writeTo(stream);

      stream.close();

      final LocalTransport targetTransport = connectedNodes.get(node);
      if (targetTransport == null) {
        throw new NodeNotConnectedException(node, "Node not connected");
      }

      final byte[] data = stream.bytes().toBytes();
      transportServiceAdapter.sent(data.length);
      transportServiceAdapter.onRequestSent(node, requestId, action, request, options);
      targetTransport
          .workers()
          .execute(
              () -> {
                ThreadContext threadContext = targetTransport.threadPool.getThreadContext();
                try (ThreadContext.StoredContext context = threadContext.stashContext()) {
                  targetTransport.messageReceived(
                      data, action, LocalTransport.this, version, requestId);
                }
              });
    }
  }
  @Override
  public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    Object m = e.getMessage();
    if (!(m instanceof ChannelBuffer)) {
      ctx.sendUpstream(e);
      return;
    }
    ChannelBuffer buffer = (ChannelBuffer) m;
    int size = buffer.getInt(buffer.readerIndex() - 4);
    transportServiceAdapter.received(size + 6);

    // we have additional bytes to read, outside of the header
    boolean hasMessageBytesToRead = (size - (NettyHeader.HEADER_SIZE - 6)) != 0;

    int markedReaderIndex = buffer.readerIndex();
    int expectedIndexReader = markedReaderIndex + size;

    // netty always copies a buffer, either in NioWorker in its read handler, where it copies to a
    // fresh
    // buffer, or in the cumlation buffer, which is cleaned each time
    StreamInput streamIn = ChannelBufferStreamInputFactory.create(buffer, size);

    long requestId = buffer.readLong();
    byte status = buffer.readByte();
    Version version = Version.fromId(buffer.readInt());

    StreamInput wrappedStream;
    if (TransportStatus.isCompress(status) && hasMessageBytesToRead && buffer.readable()) {
      Compressor compressor = CompressorFactory.compressor(buffer);
      if (compressor == null) {
        int maxToRead = Math.min(buffer.readableBytes(), 10);
        int offset = buffer.readerIndex();
        StringBuilder sb =
            new StringBuilder("stream marked as compressed, but no compressor found, first [")
                .append(maxToRead)
                .append("] content bytes out of [")
                .append(buffer.readableBytes())
                .append("] readable bytes with message size [")
                .append(size)
                .append("] ")
                .append("] are [");
        for (int i = 0; i < maxToRead; i++) {
          sb.append(buffer.getByte(offset + i)).append(",");
        }
        sb.append("]");
        throw new ElasticsearchIllegalStateException(sb.toString());
      }
      wrappedStream = CachedStreamInput.cachedHandlesCompressed(compressor, streamIn);
    } else {
      wrappedStream = CachedStreamInput.cachedHandles(streamIn);
    }
    wrappedStream.setVersion(version);

    if (TransportStatus.isRequest(status)) {
      String action = handleRequest(ctx.getChannel(), wrappedStream, requestId, version);
      if (buffer.readerIndex() != expectedIndexReader) {
        if (buffer.readerIndex() < expectedIndexReader) {
          logger.warn(
              "Message not fully read (request) for [{}] and action [{}], resetting",
              requestId,
              action);
        } else {
          logger.warn(
              "Message read past expected size (request) for [{}] and action [{}], resetting",
              requestId,
              action);
        }
        buffer.readerIndex(expectedIndexReader);
      }
    } else {
      TransportResponseHandler handler = transportServiceAdapter.remove(requestId);
      // ignore if its null, the adapter logs it
      if (handler != null) {
        if (TransportStatus.isError(status)) {
          handlerResponseError(wrappedStream, handler);
        } else {
          handleResponse(ctx.getChannel(), wrappedStream, handler);
        }
      } else {
        // if its null, skip those bytes
        buffer.readerIndex(markedReaderIndex + size);
      }
      if (buffer.readerIndex() != expectedIndexReader) {
        if (buffer.readerIndex() < expectedIndexReader) {
          logger.warn(
              "Message not fully read (response) for [{}] handler {}, error [{}], resetting",
              requestId,
              handler,
              TransportStatus.isError(status));
        } else {
          logger.warn(
              "Message read past expected size (response) for [{}] handler {}, error [{}], resetting",
              requestId,
              handler,
              TransportStatus.isError(status));
        }
        buffer.readerIndex(expectedIndexReader);
      }
    }
    wrappedStream.close();
  }