This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 0cbdb9b67088 CAMEL-26090: camel-hl7 - add a maxFrameLength option to
the HL7 MLLP Netty decoder (#28758)
0cbdb9b67088 is described below
commit 0cbdb9b670883f0f0cc1443b290cbdc8c3ebc194
Author: allthingssecurity <[email protected]>
AuthorDate: Sat Oct 31 15:32:40 2026 +0530
CAMEL-26090: camel-hl7 - add a maxFrameLength option to the HL7 MLLP Netty
decoder (#28758)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
.../apache/camel/catalog/docs/hl7-dataformat.adoc | 19 +++
.../camel-hl7/src/main/docs/hl7-dataformat.adoc | 19 +++
.../apache/camel/component/hl7/HL7MLLPConfig.java | 18 +++
.../camel/component/hl7/HL7MLLPNettyDecoder.java | 9 +-
.../component/hl7/HL7MLLPNettyDecoderFactory.java | 15 ++
.../hl7/HL7MLLPNettyDecoderMaxFrameLengthTest.java | 158 +++++++++++++++++++++
.../hl7/HL7MLLPNettyMaxFrameLengthRouteTest.java | 130 +++++++++++++++++
7 files changed, 362 insertions(+), 6 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hl7-dataformat.adoc
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hl7-dataformat.adoc
index 9787b933bc6d..bdbf5edf75b8 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hl7-dataformat.adoc
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/hl7-dataformat.adoc
@@ -69,6 +69,13 @@ the HL7 message content.
|`convertLFtoCR` |`false` |Will convert `\n` to `\r` (`0x0d`, 13 decimal) as
HL7 stipulates `\r` as
segment terminators. The HAPI library requires the use of `\r`.
+
+|`maxFrameLength` |`2147483647` |Netty decoder (`HL7MLLPNettyDecoderFactory`)
only: the maximum length in bytes of
+a message, counted from the start of the data up to the end bytes (the start
byte included, the end bytes not). A
+longer message is discarded and fails with a `TooLongFrameException`: the
Netty consumer reports it to its exception
+handler and closes the connection. The decoder fails as soon as more bytes
than the limit have arrived without both end
+bytes, so a message of exactly this length fails when its first end byte
arrives without the second; set the limit
+with some margin over the largest message you expect. The default
(`Integer.MAX_VALUE`) means no limit.
|===
=== Exposing an HL7 listener using Mina
@@ -161,6 +168,18 @@ The beans can be set in the Spring XML file:
<bean id="hl7encoder"
class="org.apache.camel.component.hl7.HL7MLLPNettyEncoderFactory"/>
----
+By default the decoder accepts messages of any size. To limit the size of a
message, set `maxFrameLength` (in bytes)
+on the decoder factory; a longer message fails with a `TooLongFrameException`
and the connection is closed (see
+`maxFrameLength` in the options above):
+
+._XML-only: Spring bean declaration for an HL7 Netty decoder with a message
size limit_
+[source,xml]
+----
+<bean id="hl7decoder"
class="org.apache.camel.component.hl7.HL7MLLPNettyDecoderFactory">
+ <property name="maxFrameLength" value="1048576"/>
+</bean>
+----
+
The endpoint *hl7NettyListener* can then be used in a route as a
consumer, as this Java DSL example illustrates:
diff --git a/components/camel-hl7/src/main/docs/hl7-dataformat.adoc
b/components/camel-hl7/src/main/docs/hl7-dataformat.adoc
index 9787b933bc6d..bdbf5edf75b8 100644
--- a/components/camel-hl7/src/main/docs/hl7-dataformat.adoc
+++ b/components/camel-hl7/src/main/docs/hl7-dataformat.adoc
@@ -69,6 +69,13 @@ the HL7 message content.
|`convertLFtoCR` |`false` |Will convert `\n` to `\r` (`0x0d`, 13 decimal) as
HL7 stipulates `\r` as
segment terminators. The HAPI library requires the use of `\r`.
+
+|`maxFrameLength` |`2147483647` |Netty decoder (`HL7MLLPNettyDecoderFactory`)
only: the maximum length in bytes of
+a message, counted from the start of the data up to the end bytes (the start
byte included, the end bytes not). A
+longer message is discarded and fails with a `TooLongFrameException`: the
Netty consumer reports it to its exception
+handler and closes the connection. The decoder fails as soon as more bytes
than the limit have arrived without both end
+bytes, so a message of exactly this length fails when its first end byte
arrives without the second; set the limit
+with some margin over the largest message you expect. The default
(`Integer.MAX_VALUE`) means no limit.
|===
=== Exposing an HL7 listener using Mina
@@ -161,6 +168,18 @@ The beans can be set in the Spring XML file:
<bean id="hl7encoder"
class="org.apache.camel.component.hl7.HL7MLLPNettyEncoderFactory"/>
----
+By default the decoder accepts messages of any size. To limit the size of a
message, set `maxFrameLength` (in bytes)
+on the decoder factory; a longer message fails with a `TooLongFrameException`
and the connection is closed (see
+`maxFrameLength` in the options above):
+
+._XML-only: Spring bean declaration for an HL7 Netty decoder with a message
size limit_
+[source,xml]
+----
+<bean id="hl7decoder"
class="org.apache.camel.component.hl7.HL7MLLPNettyDecoderFactory">
+ <property name="maxFrameLength" value="1048576"/>
+</bean>
+----
+
The endpoint *hl7NettyListener* can then be used in a route as a
consumer, as this Java DSL example illustrates:
diff --git
a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPConfig.java
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPConfig.java
index fdc28a6bc21d..8e7a0aed356b 100644
---
a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPConfig.java
+++
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPConfig.java
@@ -46,6 +46,8 @@ public class HL7MLLPConfig {
private CodingErrorAction unmappableCharacterErrorAction =
CodingErrorAction.REPORT;
+ private int maxFrameLength = Integer.MAX_VALUE;
+
public Charset getCharset() {
return charset;
}
@@ -134,4 +136,20 @@ public class HL7MLLPConfig {
public void setUnmappableCharacterErrorAction(CodingErrorAction
unmappableCharacterErrorAction) {
this.unmappableCharacterErrorAction = unmappableCharacterErrorAction;
}
+
+ public int getMaxFrameLength() {
+ return maxFrameLength;
+ }
+
+ /**
+ * The maximum length in bytes of a message that the Netty decoder reads,
counted from the start of the data up to
+ * the end bytes (the start byte included, the end bytes not). A longer
message is discarded and fails with a
+ * {@code TooLongFrameException}. The default is {@link Integer#MAX_VALUE}
(no limit).
+ */
+ public void setMaxFrameLength(int maxFrameLength) {
+ if (maxFrameLength <= 0) {
+ throw new IllegalArgumentException("maxFrameLength must be a
positive number: " + maxFrameLength);
+ }
+ this.maxFrameLength = maxFrameLength;
+ }
}
diff --git
a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoder.java
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoder.java
index 286fa16406c0..a60d1dc1a3d2 100644
---
a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoder.java
+++
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoder.java
@@ -16,8 +16,6 @@
*/
package org.apache.camel.component.hl7;
-import java.nio.charset.Charset;
-
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
@@ -32,7 +30,6 @@ import org.slf4j.LoggerFactory;
class HL7MLLPNettyDecoder extends DelimiterBasedFrameDecoder {
private static final Logger LOG =
LoggerFactory.getLogger(HL7MLLPNettyDecoder.class);
- private static final int MAX_FRAME_LENGTH = Integer.MAX_VALUE;
private final HL7MLLPConfig config;
/**
@@ -49,9 +46,9 @@ class HL7MLLPNettyDecoder extends DelimiterBasedFrameDecoder {
* @throws java.lang.NullPointerException is config is null
*/
HL7MLLPNettyDecoder(HL7MLLPConfig config) {
- super(MAX_FRAME_LENGTH, true, Unpooled.copiedBuffer(
- new char[] { config.getEndByte1(), config.getEndByte2() },
- Charset.defaultCharset()));
+ // the delimiter must be the exact bytes the encoder writes ((byte)
char), so it is not encoded with a charset
+ super(config.getMaxFrameLength(), true, Unpooled.wrappedBuffer(
+ new byte[] { (byte) config.getEndByte1(), (byte)
config.getEndByte2() }));
this.config = config;
}
diff --git
a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoderFactory.java
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoderFactory.java
index 5740516a4e1b..72acede6f808 100644
---
a/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoderFactory.java
+++
b/components/camel-hl7/src/main/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoderFactory.java
@@ -23,6 +23,21 @@ import io.netty.channel.ChannelHandler;
*/
public class HL7MLLPNettyDecoderFactory extends
HL7MLLPConfigAwareChannelHandlerFactory {
+ public int getMaxFrameLength() {
+ return config.getMaxFrameLength();
+ }
+
+ /**
+ * The maximum length in bytes of a message that the decoder reads,
counted from the start of the data up to the end
+ * bytes (the start byte included, the end bytes not). A longer message is
discarded and fails with a
+ * {@code TooLongFrameException}; a netty consumer reports it to its
exception handler and closes the connection.
+ * The decoder fails as soon as more bytes than the limit have arrived
without both end bytes, so leave some margin
+ * over the largest expected message. The default is {@link
Integer#MAX_VALUE} (no limit).
+ */
+ public void setMaxFrameLength(int maxFrameLength) {
+ config.setMaxFrameLength(maxFrameLength);
+ }
+
@Override
public ChannelHandler newChannelHandler() {
return new HL7MLLPNettyDecoder(config);
diff --git
a/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoderMaxFrameLengthTest.java
b/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoderMaxFrameLengthTest.java
new file mode 100644
index 000000000000..80a50a9fed93
--- /dev/null
+++
b/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7MLLPNettyDecoderMaxFrameLengthTest.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.hl7;
+
+import java.nio.charset.StandardCharsets;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.codec.TooLongFrameException;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests the maxFrameLength option of the HL7 MLLP Netty decoder.
+ */
+class HL7MLLPNettyDecoderMaxFrameLengthTest {
+
+ private static final String START = "\u000b";
+ private static final String END = "\u001c\r";
+
+ @Test
+ void messageAtTheLimitIsDecoded() {
+ // the start byte and 49 bytes of message: 50 bytes before the end
bytes
+ String message = "A".repeat(49);
+ EmbeddedChannel channel = new EmbeddedChannel(decoder(50));
+
+ channel.writeInbound(Unpooled.copiedBuffer(START + message + END,
StandardCharsets.ISO_8859_1));
+
+ assertEquals(message, channel.readInbound());
+ assertFalse(channel.finish());
+ }
+
+ @Test
+ void messageOverTheLimitFails() {
+ String message = "A".repeat(50);
+ EmbeddedChannel channel = new EmbeddedChannel(decoder(50));
+
+ TooLongFrameException e = assertThrows(TooLongFrameException.class,
+ () -> channel.writeInbound(Unpooled.copiedBuffer(START +
message + END, StandardCharsets.ISO_8859_1)));
+
+ assertTrue(e.getMessage().startsWith("frame length exceeds 50"),
e.getMessage());
+ assertNull(channel.readInbound());
+ assertFalse(channel.finish());
+ }
+
+ @Test
+ void dataWithoutEndBytesFailsOnceOverTheLimitAndTheNextMessageIsDecoded() {
+ EmbeddedChannel channel = new EmbeddedChannel(decoder(50));
+
+ // a peer that sends more than the limit without the end bytes: the
decoder does not wait for them
+ TooLongFrameException e = assertThrows(TooLongFrameException.class,
+ () -> channel.writeInbound(Unpooled.copiedBuffer(START +
"B".repeat(60), StandardCharsets.ISO_8859_1)));
+ assertTrue(e.getMessage().startsWith("frame length exceeds 50"),
e.getMessage());
+ assertNull(channel.readInbound());
+
+ // the rest of the long message is discarded up to its end bytes, and
the next message is decoded
+ channel.writeInbound(Unpooled.copiedBuffer("B".repeat(20) + END +
START + "next" + END,
+ StandardCharsets.ISO_8859_1));
+ assertEquals("next", channel.readInbound());
+ assertNull(channel.readInbound());
+ assertFalse(channel.finish());
+ }
+
+ @Test
+ void factoryAppliesTheLimitToItsDecoders() {
+ HL7MLLPNettyDecoderFactory factory = new HL7MLLPNettyDecoderFactory();
+ factory.setMaxFrameLength(50);
+ assertEquals(50, factory.getMaxFrameLength());
+
+ EmbeddedChannel channel = new
EmbeddedChannel(factory.newChannelHandler());
+ channel.writeInbound(Unpooled.copiedBuffer(START + "A".repeat(49) +
END, StandardCharsets.ISO_8859_1));
+ assertEquals("A".repeat(49), channel.readInbound());
+
+ assertThrows(TooLongFrameException.class,
+ () -> channel.writeInbound(Unpooled.copiedBuffer(START +
"A".repeat(50) + END, StandardCharsets.ISO_8859_1)));
+ assertNull(channel.readInbound());
+ assertFalse(channel.finish());
+ }
+
+ @Test
+ void defaultIsNoLimit() {
+ assertEquals(Integer.MAX_VALUE, new
HL7MLLPConfig().getMaxFrameLength());
+ assertEquals(Integer.MAX_VALUE, new
HL7MLLPNettyDecoderFactory().getMaxFrameLength());
+
+ String message = "C".repeat(1024 * 1024);
+ EmbeddedChannel channel = new EmbeddedChannel(new
HL7MLLPNettyDecoderFactory().newChannelHandler());
+ channel.writeInbound(Unpooled.copiedBuffer(START + message + END,
StandardCharsets.ISO_8859_1));
+
+ assertEquals(message, channel.readInbound());
+ assertFalse(channel.finish());
+ }
+
+ @Test
+ void highStartAndEndBytesAreMatchedAsTheBytesTheEncoderWrites() {
+ // the encoder writes each start and end byte as one byte: the decoder
must look for that byte too, and not
+ // for its encoding in the default charset (two bytes in UTF-8), or
with a limit every message is too long
+ HL7MLLPConfig config = new HL7MLLPConfig();
+ config.setCharset(StandardCharsets.ISO_8859_1);
+ config.setStartByte('\u008c');
+ config.setEndByte1('\u00ff');
+ config.setMaxFrameLength(50);
+ EmbeddedChannel encoder = new EmbeddedChannel(new
HL7MLLPNettyEncoder(config));
+ EmbeddedChannel decoder = new EmbeddedChannel(new
HL7MLLPNettyDecoder(config));
+
+ String message = "A".repeat(49);
+ assertTrue(encoder.writeOutbound(message));
+ ByteBuf frame = encoder.readOutbound();
+ try {
+ assertEquals(52, frame.readableBytes());
+ assertEquals((byte) 0x8c, frame.getByte(frame.readerIndex()));
+ assertEquals((byte) 0xff, frame.getByte(frame.writerIndex() - 2));
+ decoder.writeInbound(frame.retain());
+ } finally {
+ frame.release();
+ }
+
+ assertEquals(0, frame.refCnt());
+ assertEquals(message, decoder.readInbound());
+ assertFalse(encoder.finish());
+ assertFalse(decoder.finish());
+ }
+
+ @Test
+ void maxFrameLengthMustBePositive() {
+ HL7MLLPConfig config = new HL7MLLPConfig();
+ assertThrows(IllegalArgumentException.class, () ->
config.setMaxFrameLength(0));
+ assertThrows(IllegalArgumentException.class, () ->
config.setMaxFrameLength(-1));
+ assertEquals(Integer.MAX_VALUE, config.getMaxFrameLength());
+ }
+
+ private static ChannelHandler decoder(int maxFrameLength) {
+ HL7MLLPConfig config = new HL7MLLPConfig();
+ config.setCharset(StandardCharsets.ISO_8859_1);
+ config.setMaxFrameLength(maxFrameLength);
+ return new HL7MLLPNettyDecoder(config);
+ }
+}
diff --git
a/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7MLLPNettyMaxFrameLengthRouteTest.java
b/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7MLLPNettyMaxFrameLengthRouteTest.java
new file mode 100644
index 000000000000..b1428509a95e
--- /dev/null
+++
b/components/camel-hl7/src/test/java/org/apache/camel/component/hl7/HL7MLLPNettyMaxFrameLengthRouteTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.hl7;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.SocketException;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import io.netty.handler.codec.TooLongFrameException;
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spi.ExceptionHandler;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * A Netty HL7 listener with a maxFrameLength refuses a longer message and
keeps serving other connections.
+ */
+class HL7MLLPNettyMaxFrameLengthRouteTest extends HL7TestSupport {
+
+ private static final String MESSAGE =
"MSH|^~\\&|MYSENDER|MYRECEIVER|MYAPPLICATION||200612211200||QRY^A19|1234|P|2.4";
+
+ private final CountDownLatch handled = new CountDownLatch(1);
+ private final AtomicReference<Throwable> handledException = new
AtomicReference<>();
+
+ @BindToRegistry("hl7decoder")
+ HL7MLLPNettyDecoderFactory addDecoder() {
+ HL7MLLPNettyDecoderFactory decoder = new HL7MLLPNettyDecoderFactory();
+ decoder.setMaxFrameLength(1024);
+ return decoder;
+ }
+
+ @BindToRegistry("hl7encoder")
+ HL7MLLPNettyEncoderFactory addEncoder() {
+ return new HL7MLLPNettyEncoderFactory();
+ }
+
+ @BindToRegistry("recordingHandler")
+ ExceptionHandler addExceptionHandler() {
+ return new ExceptionHandler() {
+ @Override
+ public void handleException(Throwable exception) {
+ handleException(null, null, exception);
+ }
+
+ @Override
+ public void handleException(String message, Throwable exception) {
+ handleException(message, null, exception);
+ }
+
+ @Override
+ public void handleException(String message, Exchange exchange,
Throwable exception) {
+ handledException.compareAndSet(null, exception);
+ handled.countDown();
+ }
+ };
+ }
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ public void configure() {
+ from("netty:tcp://127.0.0.1:" + getPort()
+ +
"?sync=true&decoders=#hl7decoder&encoders=#hl7encoder&exceptionHandler=#recordingHandler")
+ .to("mock:result")
+ .setBody(constant("ACK"));
+ }
+ };
+ }
+
+ @Test
+ void messageOverTheLimitClosesItsConnectionAndOtherMessagesAreServed()
throws Exception {
+ MockEndpoint mock = getMockEndpoint("mock:result");
+ mock.expectedBodiesReceived(MESSAGE);
+
+ try (Socket socket = new Socket("127.0.0.1", getPort())) {
+ socket.setSoTimeout(10000);
+ OutputStream out = socket.getOutputStream();
+ // a message of 2000 bytes without the end bytes: the listener
does not wait for them
+ out.write(0x0b);
+ out.write("A".repeat(2000).getBytes(StandardCharsets.ISO_8859_1));
+ out.flush();
+ assertEquals(-1, readUntilClosed(socket.getInputStream()));
+ }
+
+ assertTrue(handled.await(10, TimeUnit.SECONDS), "the exception handler
was not called");
+ assertInstanceOf(TooLongFrameException.class, handledException.get());
+
+ String reply = template.requestBody(
+ "netty:tcp://127.0.0.1:" + getPort() +
"?sync=true&decoders=#hl7decoder&encoders=#hl7encoder", MESSAGE,
+ String.class);
+ assertEquals("ACK", reply);
+
+ mock.assertIsSatisfied();
+ }
+
+ private static int readUntilClosed(InputStream in) throws IOException {
+ try {
+ return in.read();
+ } catch (SocketException e) {
+ // connection reset by the listener
+ return -1;
+ }
+ }
+}