Skip to content

Commit 911d02b

Browse files
author
Andrey Ershov
authored
Enhanced logging when transport is misconfigured to talk to HTTP port (#45964)
If a node is misconfigured to talk to remote node HTTP port (instead of transport port) eventually it will receive an HTTP response from the remote node on transport port (this happens when a node sends accidentally line terminating byte in a transport request). If this happens today it results in a non-friendly log message and a long stack trace. This commit adds a check if a malformed response is HTTP response. In this case, a concise log message would appear.
1 parent 3179a0c commit 911d02b

File tree

4 files changed

+59
-31
lines changed

4 files changed

+59
-31
lines changed

server/src/main/java/org/elasticsearch/ElasticsearchException.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -961,8 +961,8 @@ private enum ElasticsearchExceptionHandle {
961961
RESOURCE_ALREADY_EXISTS_EXCEPTION(ResourceAlreadyExistsException.class,
962962
ResourceAlreadyExistsException::new, 123, UNKNOWN_VERSION_ADDED),
963963
// 124 used to be Script.ScriptParseException
964-
HTTP_ON_TRANSPORT_EXCEPTION(TcpTransport.HttpOnTransportException.class,
965-
TcpTransport.HttpOnTransportException::new, 125, UNKNOWN_VERSION_ADDED),
964+
HTTP_REQUEST_ON_TRANSPORT_EXCEPTION(TcpTransport.HttpRequestOnTransportException.class,
965+
TcpTransport.HttpRequestOnTransportException::new, 125, UNKNOWN_VERSION_ADDED),
966966
MAPPER_PARSING_EXCEPTION(org.elasticsearch.index.mapper.MapperParsingException.class,
967967
org.elasticsearch.index.mapper.MapperParsingException::new, 126, UNKNOWN_VERSION_ADDED),
968968
SEARCH_CONTEXT_EXCEPTION(org.elasticsearch.search.SearchContextException.class,

server/src/main/java/org/elasticsearch/transport/TcpTransport.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ public void onException(TcpChannel channel, Exception e) {
601601
"cancelled key exception caught on transport layer [{}], disconnecting from relevant node", channel), e);
602602
// close the channel as safe measure, which will cause a node to be disconnected if relevant
603603
CloseableChannel.closeChannel(channel);
604-
} else if (e instanceof TcpTransport.HttpOnTransportException) {
604+
} else if (e instanceof HttpRequestOnTransportException) {
605605
// in case we are able to return data, serialize the exception content and sent it back to the client
606606
if (channel.isOpen()) {
607607
BytesArray message = new BytesArray(e.getMessage().getBytes(StandardCharsets.UTF_8));
@@ -674,7 +674,7 @@ public void inboundMessage(TcpChannel channel, BytesReference message) {
674674
* @param bytesReference the bytes available to consume
675675
* @return the number of bytes consumed
676676
* @throws StreamCorruptedException if the message header format is not recognized
677-
* @throws TcpTransport.HttpOnTransportException if the message header appears to be an HTTP message
677+
* @throws HttpRequestOnTransportException if the message header appears to be an HTTP message
678678
* @throws IllegalArgumentException if the message length is greater that the maximum allowed frame size.
679679
* This is dependent on the available memory.
680680
*/
@@ -696,7 +696,7 @@ public int consumeNetworkReads(TcpChannel channel, BytesReference bytesReference
696696
* @param networkBytes the will be read
697697
* @return the message decoded
698698
* @throws StreamCorruptedException if the message header format is not recognized
699-
* @throws TcpTransport.HttpOnTransportException if the message header appears to be an HTTP message
699+
* @throws HttpRequestOnTransportException if the message header appears to be an HTTP message
700700
* @throws IllegalArgumentException if the message length is greater that the maximum allowed frame size.
701701
* This is dependent on the available memory.
702702
*/
@@ -723,7 +723,7 @@ static BytesReference decodeFrame(BytesReference networkBytes) throws IOExceptio
723723
* @param networkBytes the will be read
724724
* @return the length of the message
725725
* @throws StreamCorruptedException if the message header format is not recognized
726-
* @throws TcpTransport.HttpOnTransportException if the message header appears to be an HTTP message
726+
* @throws HttpRequestOnTransportException if the message header appears to be an HTTP message
727727
* @throws IllegalArgumentException if the message length is greater that the maximum allowed frame size.
728728
* This is dependent on the available memory.
729729
*/
@@ -737,8 +737,13 @@ public static int readMessageLength(BytesReference networkBytes) throws IOExcept
737737

738738
private static int readHeaderBuffer(BytesReference headerBuffer) throws IOException {
739739
if (headerBuffer.get(0) != 'E' || headerBuffer.get(1) != 'S') {
740-
if (appearsToBeHTTP(headerBuffer)) {
741-
throw new TcpTransport.HttpOnTransportException("This is not an HTTP port");
740+
if (appearsToBeHTTPRequest(headerBuffer)) {
741+
throw new HttpRequestOnTransportException("This is not an HTTP port");
742+
}
743+
744+
if (appearsToBeHTTPResponse(headerBuffer)) {
745+
throw new StreamCorruptedException("received HTTP response on transport port, ensure that transport port (not " +
746+
"HTTP port) of a remote node is specified in the configuration");
742747
}
743748

744749
String firstBytes = "("
@@ -772,7 +777,7 @@ private static int readHeaderBuffer(BytesReference headerBuffer) throws IOExcept
772777
return messageLength;
773778
}
774779

775-
private static boolean appearsToBeHTTP(BytesReference headerBuffer) {
780+
private static boolean appearsToBeHTTPRequest(BytesReference headerBuffer) {
776781
return bufferStartsWith(headerBuffer, "GET") ||
777782
bufferStartsWith(headerBuffer, "POST") ||
778783
bufferStartsWith(headerBuffer, "PUT") ||
@@ -784,6 +789,10 @@ private static boolean appearsToBeHTTP(BytesReference headerBuffer) {
784789
bufferStartsWith(headerBuffer, "TRACE");
785790
}
786791

792+
private static boolean appearsToBeHTTPResponse(BytesReference headerBuffer) {
793+
return bufferStartsWith(headerBuffer, "HTTP");
794+
}
795+
787796
private static boolean appearsToBeTLS(BytesReference headerBuffer) {
788797
return headerBuffer.get(0) == 0x16 && headerBuffer.get(1) == 0x03;
789798
}
@@ -802,9 +811,9 @@ private static boolean bufferStartsWith(BytesReference buffer, String method) {
802811
* A helper exception to mark an incoming connection as potentially being HTTP
803812
* so an appropriate error code can be returned
804813
*/
805-
public static class HttpOnTransportException extends ElasticsearchException {
814+
public static class HttpRequestOnTransportException extends ElasticsearchException {
806815

807-
private HttpOnTransportException(String msg) {
816+
private HttpRequestOnTransportException(String msg) {
808817
super(msg);
809818
}
810819

@@ -813,7 +822,7 @@ public RestStatus status() {
813822
return RestStatus.BAD_REQUEST;
814823
}
815824

816-
public HttpOnTransportException(StreamInput in) throws IOException {
825+
public HttpRequestOnTransportException(StreamInput in) throws IOException {
817826
super(in);
818827
}
819828
}

server/src/test/java/org/elasticsearch/ExceptionSerializationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -787,7 +787,7 @@ public void testIds() {
787787
ids.put(122, null);
788788
ids.put(123, org.elasticsearch.ResourceAlreadyExistsException.class);
789789
ids.put(124, null);
790-
ids.put(125, TcpTransport.HttpOnTransportException.class);
790+
ids.put(125, TcpTransport.HttpRequestOnTransportException.class);
791791
ids.put(126, org.elasticsearch.index.mapper.MapperParsingException.class);
792792
ids.put(127, org.elasticsearch.search.SearchContextException.class);
793793
ids.put(128, org.elasticsearch.search.builder.SearchSourceBuilderException.class);

server/src/test/java/org/elasticsearch/transport/TcpTransportTests.java

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,28 @@ public void testInvalidHeader() throws IOException {
288288
}
289289
}
290290

291+
public void testHTTPRequest() throws IOException {
292+
String[] httpHeaders = {"GET", "POST", "PUT", "HEAD", "DELETE", "OPTIONS", "PATCH", "TRACE"};
293+
294+
for (String httpHeader : httpHeaders) {
295+
BytesStreamOutput streamOutput = new BytesStreamOutput(1 << 14);
296+
297+
for (char c : httpHeader.toCharArray()) {
298+
streamOutput.write((byte) c);
299+
}
300+
streamOutput.write(new byte[6]);
301+
302+
try {
303+
BytesReference bytes = streamOutput.bytes();
304+
TcpTransport.decodeFrame(bytes);
305+
fail("Expected exception");
306+
} catch (Exception ex) {
307+
assertThat(ex, instanceOf(TcpTransport.HttpRequestOnTransportException.class));
308+
assertEquals("This is not an HTTP port", ex.getMessage());
309+
}
310+
}
311+
}
312+
291313
public void testTLSHeader() throws IOException {
292314
BytesStreamOutput streamOutput = new BytesStreamOutput(1 << 14);
293315

@@ -314,25 +336,22 @@ public void testTLSHeader() throws IOException {
314336
}
315337
}
316338

317-
public void testHTTPHeader() throws IOException {
318-
String[] httpHeaders = {"GET", "POST", "PUT", "HEAD", "DELETE", "OPTIONS", "PATCH", "TRACE"};
319-
320-
for (String httpHeader : httpHeaders) {
321-
BytesStreamOutput streamOutput = new BytesStreamOutput(1 << 14);
322-
323-
for (char c : httpHeader.toCharArray()) {
324-
streamOutput.write((byte) c);
325-
}
326-
streamOutput.write(new byte[6]);
339+
public void testHTTPResponse() throws IOException {
340+
BytesStreamOutput streamOutput = new BytesStreamOutput(1 << 14);
341+
streamOutput.write('H');
342+
streamOutput.write('T');
343+
streamOutput.write('T');
344+
streamOutput.write('P');
345+
streamOutput.write(randomByte());
346+
streamOutput.write(randomByte());
327347

328-
try {
329-
BytesReference bytes = streamOutput.bytes();
330-
TcpTransport.decodeFrame(bytes);
331-
fail("Expected exception");
332-
} catch (Exception ex) {
333-
assertThat(ex, instanceOf(TcpTransport.HttpOnTransportException.class));
334-
assertEquals("This is not an HTTP port", ex.getMessage());
335-
}
348+
try {
349+
TcpTransport.decodeFrame(streamOutput.bytes());
350+
fail("Expected exception");
351+
} catch (Exception ex) {
352+
assertThat(ex, instanceOf(StreamCorruptedException.class));
353+
assertEquals("received HTTP response on transport port, ensure that transport port " +
354+
"(not HTTP port) of a remote node is specified in the configuration", ex.getMessage());
336355
}
337356
}
338357
}

0 commit comments

Comments
 (0)