Execute low level handshake in #openConnection (#22440)
Today we execute the low level handshake on the TCP layer in #connectToNode. If #openConnection is used directly, which is truly expert, no handshake is executed which allows connecting to nodes that are not necessarily compatible. This change moves the handshake to #openConnection to prevent bypassing this logic.
This commit is contained in:
parent
bf51522788
commit
a5daa5d3a2
|
@ -458,13 +458,6 @@ public abstract class TcpTransport<Channel> extends AbstractLifecycleComponent i
|
||||||
"failed to connect to [{}], cleaning dangling connections", node), e);
|
"failed to connect to [{}], cleaning dangling connections", node), e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
Channel channel = nodeChannels.channel(TransportRequestOptions.Type.PING);
|
|
||||||
final TimeValue connectTimeout = connectionProfile.getConnectTimeout() == null ?
|
|
||||||
defaultConnectionProfile.getConnectTimeout() :
|
|
||||||
connectionProfile.getConnectTimeout();
|
|
||||||
final TimeValue handshakeTimeout = connectionProfile.getHandshakeTimeout() == null ?
|
|
||||||
connectTimeout : connectionProfile.getHandshakeTimeout();
|
|
||||||
Version version = executeHandshake(node, channel, handshakeTimeout);
|
|
||||||
// we acquire a connection lock, so no way there is an existing connection
|
// we acquire a connection lock, so no way there is an existing connection
|
||||||
connectedNodes.put(node, nodeChannels);
|
connectedNodes.put(node, nodeChannels);
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
|
@ -483,11 +476,18 @@ public abstract class TcpTransport<Channel> extends AbstractLifecycleComponent i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final NodeChannels openConnection(DiscoveryNode node, ConnectionProfile profile) throws IOException {
|
public final NodeChannels openConnection(DiscoveryNode node, ConnectionProfile connectionProfile) throws IOException {
|
||||||
try {
|
try {
|
||||||
NodeChannels nodeChannels = connectToChannels(node, profile);
|
NodeChannels nodeChannels = connectToChannels(node, connectionProfile);
|
||||||
|
final Channel channel = nodeChannels.getChannels().get(0); // one channel is guaranteed by the connection profile
|
||||||
|
final TimeValue connectTimeout = connectionProfile.getConnectTimeout() == null ?
|
||||||
|
defaultConnectionProfile.getConnectTimeout() :
|
||||||
|
connectionProfile.getConnectTimeout();
|
||||||
|
final TimeValue handshakeTimeout = connectionProfile.getHandshakeTimeout() == null ?
|
||||||
|
connectTimeout : connectionProfile.getHandshakeTimeout();
|
||||||
|
final Version version = executeHandshake(node, channel, handshakeTimeout);
|
||||||
transportServiceAdapter.onConnectionOpened(node);
|
transportServiceAdapter.onConnectionOpened(node);
|
||||||
return nodeChannels;
|
return new NodeChannels(nodeChannels, version); // clone the channels - we now have the correct version
|
||||||
} catch (ConnectTransportException e) {
|
} catch (ConnectTransportException e) {
|
||||||
throw e;
|
throw e;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -684,6 +684,11 @@ public final class MockTransportService extends TransportService {
|
||||||
return connection.getNode();
|
return connection.getNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version getVersion() {
|
||||||
|
return connection.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendRequest(long requestId, String action, TransportRequest request, TransportRequestOptions options)
|
public void sendRequest(long requestId, String action, TransportRequest request, TransportRequestOptions options)
|
||||||
throws IOException, TransportException {
|
throws IOException, TransportException {
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.elasticsearch.indices.breaker.NoneCircuitBreakerService;
|
||||||
import org.elasticsearch.mocksocket.MockServerSocket;
|
import org.elasticsearch.mocksocket.MockServerSocket;
|
||||||
import org.elasticsearch.node.Node;
|
import org.elasticsearch.node.Node;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
import org.elasticsearch.test.VersionUtils;
|
||||||
import org.elasticsearch.test.transport.MockTransportService;
|
import org.elasticsearch.test.transport.MockTransportService;
|
||||||
import org.elasticsearch.threadpool.TestThreadPool;
|
import org.elasticsearch.threadpool.TestThreadPool;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
@ -1818,6 +1819,52 @@ public abstract class AbstractSimpleTransportTestCase extends ESTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testHandshakeWithIncompatVersion() {
|
||||||
|
assumeTrue("only tcp transport has a handshake method", serviceA.getOriginalTransport() instanceof TcpTransport);
|
||||||
|
NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry(Collections.emptyList());
|
||||||
|
try (MockTcpTransport transport = new MockTcpTransport(Settings.EMPTY, threadPool, BigArrays.NON_RECYCLING_INSTANCE,
|
||||||
|
new NoneCircuitBreakerService(), namedWriteableRegistry, new NetworkService(Settings.EMPTY, Collections.emptyList()),
|
||||||
|
Version.fromString("2.0.0"))) {
|
||||||
|
transport.transportServiceAdapter(serviceA.new Adapter());
|
||||||
|
transport.start();
|
||||||
|
DiscoveryNode node =
|
||||||
|
new DiscoveryNode("TS_TPC", "TS_TPC", transport.boundAddress().publishAddress(), emptyMap(), emptySet(), version0);
|
||||||
|
ConnectionProfile.Builder builder = new ConnectionProfile.Builder();
|
||||||
|
builder.addConnections(1,
|
||||||
|
TransportRequestOptions.Type.BULK,
|
||||||
|
TransportRequestOptions.Type.PING,
|
||||||
|
TransportRequestOptions.Type.RECOVERY,
|
||||||
|
TransportRequestOptions.Type.REG,
|
||||||
|
TransportRequestOptions.Type.STATE);
|
||||||
|
expectThrows(ConnectTransportException.class, () -> serviceA.openConnection(node, builder.build()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testHandshakeUpdatesVersion() throws IOException {
|
||||||
|
assumeTrue("only tcp transport has a handshake method", serviceA.getOriginalTransport() instanceof TcpTransport);
|
||||||
|
NamedWriteableRegistry namedWriteableRegistry = new NamedWriteableRegistry(Collections.emptyList());
|
||||||
|
Version version = VersionUtils.randomVersionBetween(random(), Version.CURRENT.minimumCompatibilityVersion(), Version.CURRENT);
|
||||||
|
try (MockTcpTransport transport = new MockTcpTransport(Settings.EMPTY, threadPool, BigArrays.NON_RECYCLING_INSTANCE,
|
||||||
|
new NoneCircuitBreakerService(), namedWriteableRegistry, new NetworkService(Settings.EMPTY, Collections.emptyList()),version)) {
|
||||||
|
transport.transportServiceAdapter(serviceA.new Adapter());
|
||||||
|
transport.start();
|
||||||
|
DiscoveryNode node =
|
||||||
|
new DiscoveryNode("TS_TPC", "TS_TPC", transport.boundAddress().publishAddress(), emptyMap(), emptySet(),
|
||||||
|
Version.fromString("2.0.0"));
|
||||||
|
ConnectionProfile.Builder builder = new ConnectionProfile.Builder();
|
||||||
|
builder.addConnections(1,
|
||||||
|
TransportRequestOptions.Type.BULK,
|
||||||
|
TransportRequestOptions.Type.PING,
|
||||||
|
TransportRequestOptions.Type.RECOVERY,
|
||||||
|
TransportRequestOptions.Type.REG,
|
||||||
|
TransportRequestOptions.Type.STATE);
|
||||||
|
try (Transport.Connection connection = serviceA.openConnection(node, builder.build())) {
|
||||||
|
assertEquals(connection.getVersion(), version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void testTcpHandshake() throws IOException, InterruptedException {
|
public void testTcpHandshake() throws IOException, InterruptedException {
|
||||||
assumeTrue("only tcp transport has a handshake method", serviceA.getOriginalTransport() instanceof TcpTransport);
|
assumeTrue("only tcp transport has a handshake method", serviceA.getOriginalTransport() instanceof TcpTransport);
|
||||||
TcpTransport originalTransport = (TcpTransport) serviceA.getOriginalTransport();
|
TcpTransport originalTransport = (TcpTransport) serviceA.getOriginalTransport();
|
||||||
|
@ -1830,7 +1877,7 @@ public abstract class AbstractSimpleTransportTestCase extends ESTestCase {
|
||||||
int messageLengthBytes, Version version, InetSocketAddress remoteAddress, byte status)
|
int messageLengthBytes, Version version, InetSocketAddress remoteAddress, byte status)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
return super.handleRequest(mockChannel, profileName, stream, requestId, messageLengthBytes, version, remoteAddress,
|
return super.handleRequest(mockChannel, profileName, stream, requestId, messageLengthBytes, version, remoteAddress,
|
||||||
(byte)(status & ~(1<<3))); // we flip the isHanshake bit back and ackt like the handler is not found
|
(byte)(status & ~(1<<3))); // we flip the isHandshake bit back and act like the handler is not found
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
transport.transportServiceAdapter(serviceA.new Adapter());
|
transport.transportServiceAdapter(serviceA.new Adapter());
|
||||||
|
|
Loading…
Reference in New Issue