Search code examples
javatcpdeserializationspring-integrationproxy-protocol

TCP Proxy protocol deserialization with Spring Integration


I'm attempting to workout the best way to parse a proxy protocol header over a TCP connection. The data in the proxy header is used to determine if the connection should be accepted or rejected.

The TCP payload is streaming data so there are no definable "messages". We use a custom deserializer that simply reads the InputStream and returns a byte[] containing whatever was in the stream at the time.

protected final byte[] doDeserialize(InputStream inputStream, byte[] buffer) throws IOException
{
    int len = inputStream.read(buffer, 0, buffer.length);
    if (len < 0)
        throw new IOException("Stream closed");
    return this.copyToSizedArray(buffer, len);
}

This question seems very close to what I need but it was asked 10 years ago and I'm hoping there is some updated capability in the Spring Integration TCP/IP library (How to implement TCP connection handshaking with Spring Integration?).

From what I've researched these are the potential solutions:

  1. Read the proxy header in the deserializer and somehow inform the TCP Server Factory to drop/keep the connection
  2. Read the deserialized byte[]s in a TCP Interceptor but risk complication if the proxy header is split into multiple messages
  3. Create a custom ServerConnectionFactory that somehow performs this logic

Solution

  • The logic in TcpNetConnection is like this:

    private boolean receiveAndProcessMessage() {
        Message<?> message = null;
        try {
            message = getMapper().toMessage(this);
            this.lastRead = System.currentTimeMillis();
        }
        catch (Exception e) {
            publishConnectionExceptionEvent(e);
            if (handleReadException(e)) {
                return false;
            }
        }
    

    So, if you throw an exception from your Deserializer, a TcpConnectionExceptionEvent is going to be emitted. Plus that handleReadException() has the logic like:

        if (checkTimeout(e)) {
            boolean readErrorOnClose = !isNoReadErrorOnClose();
            closeConnection(true);
    

    So, all good to parse that header in your Deserializer and throw some exception to enforce connection to be closed.