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
 protected void doStop() {
   transports.remove(localAddress);
   // now, go over all the transports connected to me, and raise disconnected event
   for (final LocalTransport targetTransport : transports.values()) {
     for (final Map.Entry<DiscoveryNode, LocalTransport> entry :
         targetTransport.connectedNodes.entrySet()) {
       if (entry.getValue() == this) {
         targetTransport.disconnectFromNode(entry.getKey());
       }
     }
   }
 }
 @Override
 protected void handleParsedResponse(
     final TransportResponse response, final TransportResponseHandler handler) {
   ElasticsearchAssertions.assertVersionSerializable(
       VersionUtils.randomVersionBetween(random, minVersion, maxVersion), response);
   super.handleParsedResponse(response, handler);
 }
  @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 sendRequest(
     final DiscoveryNode node,
     final long requestId,
     final String action,
     final TransportRequest request,
     TransportRequestOptions options)
     throws IOException, TransportException {
   ElasticsearchAssertions.assertVersionSerializable(
       VersionUtils.randomVersionBetween(random, minVersion, maxVersion), request);
   super.sendRequest(node, requestId, action, request, options);
 }
 @Override
 public void sendResponse(Throwable error) throws IOException {
   BytesStreamOutput stream;
   try {
     stream = CachedStreamOutput.cachedBytes();
     writeResponseExceptionHeader(stream);
     RemoteTransportException tx =
         new RemoteTransportException(
             targetTransport.nodeName(),
             targetTransport.boundAddress().boundAddress(),
             action,
             error);
     ThrowableObjectOutputStream too = new ThrowableObjectOutputStream(stream);
     too.writeObject(tx);
     too.close();
   } catch (NotSerializableException e) {
     stream = CachedStreamOutput.cachedBytes();
     writeResponseExceptionHeader(stream);
     RemoteTransportException tx =
         new RemoteTransportException(
             targetTransport.nodeName(),
             targetTransport.boundAddress().boundAddress(),
             action,
             new NotSerializableTransportException(error));
     ThrowableObjectOutputStream too = new ThrowableObjectOutputStream(stream);
     too.writeObject(tx);
     too.close();
   }
   final byte[] data = stream.copiedByteArray();
   targetTransport
       .threadPool()
       .execute(
           new Runnable() {
             @Override
             public void run() {
               targetTransport.messageReceived(data, action, sourceTransport, null);
             }
           });
 }
 @Override
 public void sendResponse(Streamable message, TransportResponseOptions options)
     throws IOException {
   HandlesStreamOutput stream = CachedStreamOutput.cachedHandlesBytes();
   stream.writeLong(requestId);
   byte status = 0;
   status = TransportStreams.statusSetResponse(status);
   stream.writeByte(status); // 0 for request, 1 for response.
   message.writeTo(stream);
   final byte[] data = ((BytesStreamOutput) stream.wrappedOut()).copiedByteArray();
   targetTransport
       .threadPool()
       .execute(
           new Runnable() {
             @Override
             public void run() {
               targetTransport.messageReceived(data, action, sourceTransport, null);
             }
           });
 }