This is an automated email from the ASF dual-hosted git repository.

twolf pushed a commit to branch dev_3.0
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 38efed38b150ae96d8dff2d812a249c8caff8954
Author: Thomas Wolf <tw...@apache.org>
AuthorDate: Sat Apr 5 16:23:14 2025 +0200

    Server-side HAProxyProtocolFilter
    
    Provide an IoFilter that can be added at the front of the filter chain
    of a server session to handle the HAProxy protocol header. The filter
    handles both protocol versions 1 and 2.
    
    Remove all the ServerProxyAcceptor handling in the server session, and
    completely drop this interface. The session doesn't need to know about
    this anymore; user code can just add the filter and have this handled
    transparently for the session.
    
    Move and rewrite the existing ServerProxyAcceptorTest.
---
 .../server/filter/HAProxyProtocolFilter.java       |  90 +++++++++
 .../proxyprotocol/ProxyProtocolAcceptor.java       |  27 ++-
 .../proxyprotocolv2/ProxyProtocolV2Acceptor.java   |  26 ++-
 .../server/session/ServerProxyAcceptorTest.java    | 211 +++++++++++++++++++++
 .../apache/sshd/server/ServerFactoryManager.java   |   2 -
 .../java/org/apache/sshd/server/SshServer.java     |  12 --
 .../sshd/server/session/AbstractServerSession.java |  32 ----
 .../sshd/server/session/ServerProxyAcceptor.java   |  53 ------
 .../server/session/ServerProxyAcceptorHolder.java  |  29 ---
 .../apache/sshd/server/session/ServerSession.java  |   1 -
 .../sshd/server/ServerProxyAcceptorTest.java       | 154 ---------------
 11 files changed, 341 insertions(+), 296 deletions(-)

diff --git 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/filter/HAProxyProtocolFilter.java
 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/filter/HAProxyProtocolFilter.java
new file mode 100644
index 000000000..91a226d48
--- /dev/null
+++ 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/filter/HAProxyProtocolFilter.java
@@ -0,0 +1,90 @@
+/*
+ * 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.sshd.contrib.server.filter;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.sshd.common.filter.InputHandler;
+import org.apache.sshd.common.filter.IoFilter;
+import org.apache.sshd.common.filter.OutputHandler;
+import org.apache.sshd.common.util.Readable;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import 
org.apache.sshd.contrib.server.session.proxyprotocol.ProxyProtocolAcceptor;
+import 
org.apache.sshd.contrib.server.session.proxyprotocolv2.ProxyProtocolV2Acceptor;
+import org.apache.sshd.server.session.ServerSession;
+
+/**
+ * A {@link IoFilter} that parses a proxy protocol header (either version 1 or 
2) and sets the client address reported
+ * on the {@link ServerSession}. Useful if a server is running behind a 
HAProxy.
+ *
+ * <p>
+ * The filter is intended to be added in first place in a {@link 
ServerSession}'s filter chain.
+ * </p>
+ *
+ * @see <a 
href="https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt";>HAProxy 
Protocol 1 Documentation</a>
+ * @see <a 
href="https://www.haproxy.org/download/2.7/doc/proxy-protocol.txt";>HAProxy 
Protocol 2 Documentation</a>
+ */
+public class HAProxyProtocolFilter extends IoFilter {
+
+    private AtomicReference<InputHandler> input = new AtomicReference<>();
+
+    public HAProxyProtocolFilter(ServerSession session) {
+        input.set(new ProxyHeaderReceiver(session));
+    }
+
+    @Override
+    public InputHandler in() {
+        return input.get();
+    }
+
+    @Override
+    public OutputHandler out() {
+        return null;
+    }
+
+    private class ProxyHeaderReceiver implements InputHandler {
+
+        private final ServerSession session;
+
+        private final ProxyProtocolAcceptor handler = new 
ProxyProtocolV2Acceptor();
+
+        private Buffer buffer = new ByteArrayBuffer();
+
+        ProxyHeaderReceiver(ServerSession session) {
+            this.session = Objects.requireNonNull(session);
+        }
+
+        @Override
+        public synchronized void received(Readable message) throws Exception {
+            if (buffer == null) {
+                owner().passOn(message);
+            } else {
+                buffer.putBuffer(message);
+                if (handler.acceptServerProxyMetadata(session, buffer)) {
+                    buffer.compact();
+                    owner().passOn(buffer);
+                    input.set(null);
+                    buffer = null;
+                }
+            }
+        }
+    }
+}
diff --git 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocol/ProxyProtocolAcceptor.java
 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocol/ProxyProtocolAcceptor.java
index 21f65b08a..ef3a3845c 100644
--- 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocol/ProxyProtocolAcceptor.java
+++ 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocol/ProxyProtocolAcceptor.java
@@ -28,7 +28,6 @@ import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.common.util.logging.AbstractLoggingBean;
 import org.apache.sshd.server.session.AbstractServerSession;
-import org.apache.sshd.server.session.ServerProxyAcceptor;
 import org.apache.sshd.server.session.ServerSession;
 
 /**
@@ -38,18 +37,35 @@ import org.apache.sshd.server.session.ServerSession;
  * @see    <A 
HREF="https://gist.github.com/codingtony/a8684c9ffa08ad56899f94d3b6c2a040";>Tony 
Bussieres contribution</A>
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
-public class ProxyProtocolAcceptor extends AbstractLoggingBean implements 
ServerProxyAcceptor {
+public class ProxyProtocolAcceptor extends AbstractLoggingBean {
     // 108 bytes is the largest buffer needed for the PROXY protocol, but we 
are a bit more lenient
     public static final int MAX_PROXY_HEADER_LENGTH = Byte.MAX_VALUE;
     public static final String PROX_PROTOCOL_PREFIX = "PROXY";
 
+    // 'P' 'R' 'O' 'X' 'Y' ' '
     private static final byte[] PROXY_HEADER = new byte[] { 0x50, 0x52, 0x4F, 
0x58, 0x59, 0x20 };
 
     public ProxyProtocolAcceptor() {
         super();
     }
 
-    @Override
+    /**
+     * Invoked by the ProxyProtocolFilter; may get called multiple times if 
the proxy header is not received in a single
+     * message. Should parse the proxy header from the beginning of the given 
{@link Buffer} and return {@code true} if
+     * the header was fully consumed (or there is no proxy header at all), and 
{@code false} if the buffer contains only
+     * a partial (potential) proxy header.
+     *
+     * <p>
+     * Upon a successful return, calling code assumes that the proxy header 
has been consumed from the buffer and the
+     * buffer's read position is right after the last byte of the proxy header.
+     *
+     * @param  session   the {@link ServerSession} instance
+     * @param  buffer    the received data
+     * @return           {@code true} if successfully extracted the remote 
client peer meta-data or if there is no proxy
+     *                   header at all, {@code false} if more data is required.
+     * @throws Exception if failed to correctly extract and parse the 
meta-data, in which case the session will be
+     *                   closed
+     */
     public boolean acceptServerProxyMetadata(ServerSession session, Buffer 
buffer) throws Exception {
         int mark = buffer.rpos();
         int dataLen = buffer.available();
@@ -87,7 +103,7 @@ public class ProxyProtocolAcceptor extends 
AbstractLoggingBean implements Server
                 proxyPayload.setLength(ppLen - 1);
             }
 
-            return parseProxyHeader(session, proxyPayload.toString(), mark, 
buffer);
+            return parseProxyHeader(session, proxyPayload.toString());
         }
 
         // Could not see LF before MAX_PROXY_HEADER_LENGTH expired
@@ -95,8 +111,7 @@ public class ProxyProtocolAcceptor extends 
AbstractLoggingBean implements Server
         return false;
     }
 
-    protected boolean parseProxyHeader(ServerSession session, String 
proxyHeader, int markPosition, Buffer buffer)
-            throws Exception {
+    protected boolean parseProxyHeader(ServerSession session, String 
proxyHeader) {
         boolean debugEnabled = log.isDebugEnabled();
         if (debugEnabled) {
             log.debug("parseProxyHeader(session={}) parsing header='{}'", 
session, proxyHeader);
diff --git 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/ProxyProtocolV2Acceptor.java
 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/ProxyProtocolV2Acceptor.java
index 05d5e9330..f6a3c4e0b 100644
--- 
a/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/ProxyProtocolV2Acceptor.java
+++ 
b/sshd-contrib/src/main/java/org/apache/sshd/contrib/server/session/proxyprotocolv2/ProxyProtocolV2Acceptor.java
@@ -41,9 +41,14 @@ import org.apache.sshd.server.session.ServerSession;
  */
 public class ProxyProtocolV2Acceptor extends ProxyProtocolAcceptor {
 
+    // CR LF CR LF NUL CR LF 'Q' 'U' 'I' 'T' LF
     private static final byte[] PROXY_V2_HEADER
             = new byte[] { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 
0x55, 0x49, 0x54, 0x0A };
 
+    // Minimum protocol V2 header length: the magic header (12 bytes), a 
version byte, a protocol byte, and a two-byte
+    // MSB-first unsigned short.
+    private static final int MIN_HEADER_LENGTH = PROXY_V2_HEADER.length + 4;
+
     private static final char FIELD_SEPARATOR = ' ';
 
     public ProxyProtocolV2Acceptor() {
@@ -54,10 +59,10 @@ public class ProxyProtocolV2Acceptor extends 
ProxyProtocolAcceptor {
     public boolean acceptServerProxyMetadata(ServerSession session, Buffer 
buffer) throws Exception {
         int mark = buffer.rpos();
         int dataLen = buffer.available();
-        if (dataLen < PROXY_V2_HEADER.length) {
+        if (dataLen < MIN_HEADER_LENGTH) {
             if (log.isDebugEnabled()) {
                 log.debug("acceptServerProxyMetadata(session={}) incomplete 
data - {}/{}", session, dataLen,
-                        PROXY_V2_HEADER.length);
+                        MIN_HEADER_LENGTH);
             }
             return false;
         }
@@ -88,9 +93,17 @@ public class ProxyProtocolV2Acceptor extends 
ProxyProtocolAcceptor {
         proxyPayload.append(FIELD_SEPARATOR).append(familyAndTransport.name());
         // Read the data length
         int dataLength = buffer.getUShort();
+        if (dataLength > buffer.available()) {
+            if (log.isDebugEnabled()) {
+                log.debug("readProxyV2Header(session={}) incomplete data after 
header - {}/{}", session, buffer.available(),
+                        dataLength);
+            }
+            buffer.rpos(markPosition);
+            return false;
+        }
         // Unix Socket are not supported by SSHD
         if (familyAndTransport.hasSockAddress()) {
-            log.warn("parseProxyHeader(session={}) unsupported sub-protocol - 
{} - continue as usual", session,
+            log.warn("readProxyV2Header(session={}) unsupported sub-protocol - 
{} - continue as usual", session,
                     familyAndTransport);
             // Skip socket address data
             AddressData.skipUnprocessedData(log, session, buffer, 
FamilyAndTransport.UNSPEC, dataLength);
@@ -100,12 +113,11 @@ public class ProxyProtocolV2Acceptor extends 
ProxyProtocolAcceptor {
         AddressData data = AddressData.extractAddressData(log, session, 
buffer, familyAndTransport, dataLength);
         proxyPayload.append(FIELD_SEPARATOR).append(data);
         // Parse the converted proxy header
-        return parseProxyHeader(session, proxyPayload.toString(), 
markPosition, buffer);
+        return parseProxyHeader(session, proxyPayload.toString());
     }
 
     @Override
-    protected boolean parseProxyHeader(ServerSession session, String 
proxyHeader, int markPosition, Buffer buffer)
-            throws Exception {
+    protected boolean parseProxyHeader(ServerSession session, String 
proxyHeader) {
         String[] proxyFields = GenericUtils.split(proxyHeader, 
FIELD_SEPARATOR);
         // Trim all fields just in case more than one space used
         for (int index = 0; index < proxyFields.length; index++) {
@@ -117,6 +129,6 @@ public class ProxyProtocolV2Acceptor extends 
ProxyProtocolAcceptor {
             log.debug("parseProxyHeader(session={}) local proxy check", 
session);
             return true;
         }
-        return super.parseProxyHeader(session, proxyHeader, markPosition, 
buffer);
+        return super.parseProxyHeader(session, proxyHeader);
     }
 }
diff --git 
a/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/ServerProxyAcceptorTest.java
 
b/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/ServerProxyAcceptorTest.java
new file mode 100644
index 000000000..5e711c212
--- /dev/null
+++ 
b/sshd-contrib/src/test/java/org/apache/sshd/contrib/server/session/ServerProxyAcceptorTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.sshd.contrib.server.session;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Stream;
+
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.filter.FilterChain;
+import org.apache.sshd.common.filter.InputHandler;
+import org.apache.sshd.common.filter.IoFilter;
+import org.apache.sshd.common.filter.OutputHandler;
+import org.apache.sshd.common.io.IoWriteFuture;
+import org.apache.sshd.common.random.JceRandom;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.common.session.Session;
+import org.apache.sshd.common.session.SessionListener;
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.common.util.net.SshdSocketAddress;
+import org.apache.sshd.contrib.server.filter.HAProxyProtocolFilter;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.session.ServerSession;
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
+ */
+class ServerProxyAcceptorTest extends BaseTestSupport {
+
+    private static final SshdSocketAddress EXPECTED_CLIENT_ADDRESS = new 
SshdSocketAddress("7.3.6.5", 7365);
+
+    private static final Random RND = new JceRandom();
+
+    private SshServer sshd;
+    private SshClient client;
+
+    ServerProxyAcceptorTest() {
+        super();
+    }
+
+    @BeforeEach
+    void setUp() throws Exception {
+        sshd = setupTestServer();
+        client = setupTestClient();
+    }
+
+    @AfterEach
+    void tearDown() throws Exception {
+        if (sshd != null) {
+            sshd.stop(true);
+        }
+        if (client != null) {
+            client.stop();
+        }
+    }
+
+    static Stream<Arguments> parameters() {
+        // A V1 proxy header
+        final byte[] v1Header = "PROXY TCP4 7.3.6.5 7.3.6.6 7365 
443\r\n".getBytes(StandardCharsets.US_ASCII);
+        // A V2 proxy header
+        Buffer buf = new ByteArrayBuffer();
+        buf.putRawBytes(new byte[] { 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 
0x51, 0x55, 0x49, 0x54, 0x0A });
+        buf.putByte((byte) 0x21);
+        buf.putByte((byte) 0x11);
+        final int pos = buf.wpos();
+        int length = 2 * Integer.BYTES + 2 * Short.BYTES;
+        buf.putShort(length);
+        buf.putUInt(0x07030605);
+        buf.putUInt(0x07030606);
+        buf.putShort(7365);
+        buf.putShort(443);
+        byte[] v2Header = buf.getCompactData();
+        int end = buf.wpos();
+        buf.wpos(end + 33333);
+        RND.fill(buf.array(), end, 33333);
+        end += 33333;
+        buf.wpos(pos);
+        buf.putShort(end - pos - 2);
+        buf.wpos(end);
+        return Stream.of(
+                Arguments.of("V1 separate", v1Header, false),
+                Arguments.of("V1 combined", v1Header, true),
+                Arguments.of("V2 separate", v2Header, false),
+                Arguments.of("V2 combined", v2Header, true),
+                Arguments.of("No proxy header", null, false),
+                Arguments.of("V2 large", buf.getCompactData(), false));
+    }
+
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("parameters")
+    void clientAddressOverride(String name, byte[] proxyMessage, boolean 
combine) throws Exception {
+        AtomicReference<SocketAddress> actualClientAddress1 = new 
AtomicReference<>();
+        AtomicReference<SocketAddress> actualClientAddress2 = new 
AtomicReference<>();
+        sshd.addSessionListener(new SessionListener() {
+
+            @Override
+            public void sessionStarting(Session session) {
+                if (session instanceof ServerSession) {
+                    // Register the HAProxy filter
+                    FilterChain filters = session.getFilterChain();
+                    filters.addFirst(new HAProxyProtocolFilter((ServerSession) 
session));
+                }
+            }
+
+            @Override
+            public void sessionEvent(Session session, Event event) {
+                if (session instanceof ServerSession) {
+                    actualClientAddress1.set(((ServerSession) 
session).getClientAddress());
+                }
+            }
+
+            @Override
+            public void sessionClosed(Session session) {
+                if (session instanceof ServerSession) {
+                    actualClientAddress2.set(((ServerSession) 
session).getClientAddress());
+                }
+            }
+        });
+        sshd.start();
+
+        client.addSessionListener(new SessionListener() {
+
+            @Override
+            public void sessionStarting(Session session) {
+                // Add a filter that writes the fake HAProxy header before the 
first message
+                FilterChain filters = session.getFilterChain();
+                filters.addFirst(new IoFilter() {
+
+                    private boolean proxyHeaderSent;
+
+                    @Override
+                    public InputHandler in() {
+                        return null;
+                    }
+
+                    @Override
+                    public OutputHandler out() {
+                        return (cmd, message) -> {
+                            if (proxyMessage != null && !proxyHeaderSent) {
+                                if (combine) {
+                                    byte[] combined = 
Arrays.copyOf(proxyMessage, proxyMessage.length + message.available());
+                                    message.getRawBytes(combined, 
proxyMessage.length, message.available());
+                                    IoWriteFuture future = owner().send(cmd, 
new ByteArrayBuffer(combined));
+                                    proxyHeaderSent = true;
+                                    return future;
+                                }
+                                owner().send(-1, new 
ByteArrayBuffer(proxyMessage));
+                                proxyHeaderSent = true;
+                            }
+                            return owner().send(cmd, message);
+                        };
+                    }
+                });
+            }
+        });
+        client.start();
+
+        CountDownLatch sessionClosed = new CountDownLatch(1);
+        try (ClientSession session
+                = client.connect(getCurrentTestName(), TEST_LOCALHOST, 
sshd.getPort()).verify(CONNECT_TIMEOUT).getSession()) {
+            session.addPasswordIdentity(getCurrentTestName());
+            session.auth().verify(AUTH_TIMEOUT);
+            session.close(false).addListener(f -> sessionClosed.countDown());
+        } finally {
+            client.stop();
+        }
+        sessionClosed.await(CLOSE_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
+        SocketAddress address1 = actualClientAddress1.get();
+        assertNotNull(address1, "Client address should be set");
+        SocketAddress address2 = actualClientAddress2.get();
+        assertSame(address1, address2, "Client address should not change");
+        assertInstanceOf(InetSocketAddress.class, address1);
+        InetSocketAddress inet = (InetSocketAddress) address1;
+        if (proxyMessage != null) {
+            assertEquals(EXPECTED_CLIENT_ADDRESS.getHostName(), 
inet.getHostString(), "Host mismatch");
+            assertEquals(EXPECTED_CLIENT_ADDRESS.getPort(), inet.getPort(), 
"Port mismatch");
+        } else {
+            assertEquals("127.0.0.1", inet.getHostString(), "Host mismatch");
+        }
+    }
+}
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java 
b/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
index bd7788e73..3ec73dcc4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerFactoryManager.java
@@ -22,7 +22,6 @@ import java.util.List;
 
 import org.apache.sshd.common.FactoryManager;
 import org.apache.sshd.server.command.CommandFactory;
-import org.apache.sshd.server.session.ServerProxyAcceptorHolder;
 import org.apache.sshd.server.shell.ShellFactory;
 import org.apache.sshd.server.subsystem.SubsystemFactory;
 
@@ -34,7 +33,6 @@ import org.apache.sshd.server.subsystem.SubsystemFactory;
  */
 public interface ServerFactoryManager
         extends FactoryManager,
-        ServerProxyAcceptorHolder,
         ServerAuthenticationManager {
 
     /**
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java 
b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
index d956d2dde..24eb21d27 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java
@@ -54,7 +54,6 @@ import 
org.apache.sshd.server.auth.password.PasswordAuthenticator;
 import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
 import org.apache.sshd.server.command.CommandFactory;
 import org.apache.sshd.server.session.ServerConnectionServiceFactory;
-import org.apache.sshd.server.session.ServerProxyAcceptor;
 import org.apache.sshd.server.session.ServerUserAuthServiceFactory;
 import org.apache.sshd.server.session.SessionFactory;
 import org.apache.sshd.server.shell.ShellFactory;
@@ -96,7 +95,6 @@ public class SshServer extends AbstractFactoryManager 
implements ServerFactoryMa
     protected String host;
     protected int port;
 
-    private ServerProxyAcceptor proxyAcceptor;
     private ShellFactory shellFactory;
     private SessionFactory sessionFactory;
     private CommandFactory commandFactory;
@@ -171,16 +169,6 @@ public class SshServer extends AbstractFactoryManager 
implements ServerFactoryMa
         this.sessionFactory = sessionFactory;
     }
 
-    @Override
-    public ServerProxyAcceptor getServerProxyAcceptor() {
-        return proxyAcceptor;
-    }
-
-    @Override
-    public void setServerProxyAcceptor(ServerProxyAcceptor proxyAcceptor) {
-        this.proxyAcceptor = proxyAcceptor;
-    }
-
     @Override
     public CommandFactory getCommandFactory() {
         return commandFactory;
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
index 5500df52b..1a7a7ebce 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/server/session/AbstractServerSession.java
@@ -77,7 +77,6 @@ import 
org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator;
  * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
  */
 public abstract class AbstractServerSession extends AbstractSession implements 
ServerSession {
-    private ServerProxyAcceptor proxyAcceptor;
     private SocketAddress clientAddress;
     private PasswordAuthenticator passwordAuthenticator;
     private PublickeyAuthenticator publickeyAuthenticator;
@@ -97,17 +96,6 @@ public abstract class AbstractServerSession extends 
AbstractSession implements S
         return (ServerFactoryManager) super.getFactoryManager();
     }
 
-    @Override
-    public ServerProxyAcceptor getServerProxyAcceptor() {
-        return resolveEffectiveProvider(
-                ServerProxyAcceptor.class, proxyAcceptor, 
getFactoryManager().getServerProxyAcceptor());
-    }
-
-    @Override
-    public void setServerProxyAcceptor(ServerProxyAcceptor proxyAcceptor) {
-        this.proxyAcceptor = proxyAcceptor;
-    }
-
     @Override
     public SocketAddress getClientAddress() {
         return resolvePeerAddress(clientAddress);
@@ -412,28 +400,8 @@ public abstract class AbstractServerSession extends 
AbstractSession implements S
 
     @Override
     protected boolean readIdentification(Buffer buffer) throws Exception {
-        ServerProxyAcceptor acceptor = getServerProxyAcceptor();
         int rpos = buffer.rpos();
         boolean debugEnabled = log.isDebugEnabled();
-        if (acceptor != null) {
-            try {
-                boolean completed = acceptor.acceptServerProxyMetadata(this, 
buffer);
-                if (!completed) {
-                    buffer.rpos(rpos); // restore original buffer position
-                    return false; // more data required
-                }
-            } catch (Throwable t) {
-                warn("readIdentification({}) failed ({}) to accept proxy 
metadata: {}",
-                        this, t.getClass().getSimpleName(), t.getMessage(), t);
-
-                if (t instanceof IOException) {
-                    throw (IOException) t;
-                } else {
-                    throw new SshException(t);
-                }
-            }
-        }
-
         List<String> ident = doReadIdentification(buffer, true);
         int numLines = GenericUtils.size(ident);
         clientVersion = (numLines <= 0) ? null : ident.remove(numLines - 1);
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerProxyAcceptor.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerProxyAcceptor.java
deleted file mode 100644
index 0edd68708..000000000
--- 
a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerProxyAcceptor.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.sshd.server.session;
-
-import org.apache.sshd.common.util.buffer.Buffer;
-
-/**
- * Provides a way to implement proxied connections where some metadata about 
the client is sent <U>before</U> the actual
- * SSH protocol is executed - e.g., the <A 
HREF=@http://www.haproxy.org/download/1.6/doc/proxy-protocol.txt";>PROXY
- * protocol</A>.
- *
- * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
- */
-@FunctionalInterface
-public interface ServerProxyAcceptor {
-    /**
-     * Invoked <U>before</U> any attempt is made to retrieve the SSH client 
identification data of the standard SSH
-     * protocol. The implementor should extract whatever data it needs from 
the data buffer. <B>Note:</B> the method may
-     * be called <U>several times</U> for the <U>same</U> session even though 
the original proxy data was successfully
-     * extracted. This happens in case the client identification line 
following it is incomplete and thus requires
-     * waiting for more incoming packets.
-     *
-     * @param  session   The {@link ServerSession} instance
-     * @param  buffer    The received data {@link Buffer} - if not the 1st 
time this method is called because data was
-     *                   lacking on last invocation, then the buffer is 
guaranteed to contain the data from all the
-     *                   previous incomplete invocations plus any new received 
data. If not enough information is
-     *                   available, the buffer's read position should be 
restored to its original value when the method
-     *                   was invoked.
-     * @return           {@code true} if successfully extracted the remote 
client peer meta-data, {@code false} if more
-     *                   data is required. Upon successful return the buffer 
read position is assumed to indicate the
-     *                   first character of the SSH identification line
-     * @throws Exception If failed to correctly extract and parse the 
meta-data, in which case the session will be
-     *                   closed
-     */
-    boolean acceptServerProxyMetadata(ServerSession session, Buffer buffer) 
throws Exception;
-}
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerProxyAcceptorHolder.java
 
b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerProxyAcceptorHolder.java
deleted file mode 100644
index ef7cf8d4a..000000000
--- 
a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerProxyAcceptorHolder.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.sshd.server.session;
-
-/**
- * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
- */
-public interface ServerProxyAcceptorHolder {
-    ServerProxyAcceptor getServerProxyAcceptor();
-
-    void setServerProxyAcceptor(ServerProxyAcceptor proxyAcceptor);
-}
diff --git 
a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java 
b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
index b566ef54e..33665f267 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSession.java
@@ -33,7 +33,6 @@ import org.apache.sshd.server.ServerFactoryManager;
  */
 public interface ServerSession
         extends Session,
-        ServerProxyAcceptorHolder,
         ServerAuthenticationManager {
 
     /**
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/server/ServerProxyAcceptorTest.java 
b/sshd-core/src/test/java/org/apache/sshd/server/ServerProxyAcceptorTest.java
deleted file mode 100644
index 413494065..000000000
--- 
a/sshd-core/src/test/java/org/apache/sshd/server/ServerProxyAcceptorTest.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.sshd.server;
-
-import java.net.SocketAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.apache.sshd.client.SshClient;
-import org.apache.sshd.client.session.ClientSession;
-import org.apache.sshd.common.io.IoSession;
-import org.apache.sshd.common.session.Session;
-import org.apache.sshd.common.session.SessionListener;
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.common.util.io.IoUtils;
-import org.apache.sshd.common.util.net.SshdSocketAddress;
-import org.apache.sshd.server.ServerTest.TestEchoShellFactory;
-import org.apache.sshd.server.session.AbstractServerSession;
-import org.apache.sshd.server.session.ServerProxyAcceptor;
-import org.apache.sshd.server.session.ServerSession;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.MethodOrderer.MethodName;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestMethodOrder;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertSame;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * @author <a href="mailto:d...@mina.apache.org";>Apache MINA SSHD Project</a>
- */
-@TestMethodOrder(MethodName.class)
-public class ServerProxyAcceptorTest extends BaseTestSupport {
-    private SshServer sshd;
-    private SshClient client;
-
-    public ServerProxyAcceptorTest() {
-        super();
-    }
-
-    @BeforeEach
-    void setUp() throws Exception {
-        sshd = setupTestServer();
-        sshd.setShellFactory(new TestEchoShellFactory());
-        client = setupTestClient();
-    }
-
-    @AfterEach
-    void tearDown() throws Exception {
-        if (sshd != null) {
-            sshd.stop(true);
-        }
-        if (client != null) {
-            client.stop();
-        }
-    }
-
-    @Test
-    void clientAddressOverride() throws Exception {
-        SshdSocketAddress expectedClientAddress = new 
SshdSocketAddress("7.3.6.5", 7365);
-        String proxyMetadata = getCurrentTestName()
-                               + " " + expectedClientAddress.getHostName()
-                               + " " + expectedClientAddress.getPort();
-        byte[] metaDataBytes = (proxyMetadata + 
IoUtils.EOL).getBytes(StandardCharsets.UTF_8);
-        sshd.setServerProxyAcceptor(new ServerProxyAcceptor() {
-            private final AtomicInteger invocationCount = new AtomicInteger(0);
-
-            @Override
-            public boolean acceptServerProxyMetadata(ServerSession session, 
Buffer buffer) throws Exception {
-                if (buffer.available() < metaDataBytes.length) {
-                    return false; // wait for more data
-                }
-
-                byte[] rawData = new byte[metaDataBytes.length];
-                buffer.getRawBytes(rawData);
-                outputDebugMessage("acceptServerProxyMetadata(%s) proxy data: 
%s", session,
-                        new String(rawData, StandardCharsets.UTF_8));
-                assertArrayEquals(metaDataBytes, rawData, "Mismatched meta 
data");
-
-                int count = invocationCount.incrementAndGet();
-                if (count == 1) {
-                    ((AbstractServerSession) 
session).setClientAddress(expectedClientAddress);
-                } else {
-                    assertSame(expectedClientAddress,
-                            session.getClientAddress(),
-                            "Mismatched client address for invocation #" + 
count);
-                }
-                return true; // proxy completed
-            }
-        });
-
-        Semaphore sessionSignal = new Semaphore(0);
-        sshd.addSessionListener(new SessionListener() {
-            @Override
-            public void sessionEvent(Session session, Event event) {
-                verifyClientAddress(event.name(), session);
-                if (Event.KeyEstablished.equals(event)) {
-                    sessionSignal.release();
-                }
-            }
-
-            @Override
-            public void sessionClosed(Session session) {
-                verifyClientAddress("sessionClosed", session);
-            }
-
-            private void verifyClientAddress(String location, Session session) 
{
-                assertObjectInstanceOf(location + ": not a server session", 
ServerSession.class, session);
-                SocketAddress actualClientAddress = ((ServerSession) 
session).getClientAddress();
-                assertSame(expectedClientAddress, actualClientAddress, 
location + ": mismatched client address instance");
-            }
-        });
-        sshd.start();
-
-        client.setClientProxyConnector(session -> {
-            IoSession ioSession = session.getIoSession();
-            ioSession.writeBuffer(new ByteArrayBuffer(metaDataBytes));
-        });
-        client.start();
-
-        try (ClientSession session
-                = client.connect(getCurrentTestName(), TEST_LOCALHOST, 
sshd.getPort()).verify(CONNECT_TIMEOUT).getSession()) {
-            session.addPasswordIdentity(getCurrentTestName());
-            session.auth().verify(AUTH_TIMEOUT);
-            assertTrue(sessionSignal.tryAcquire(DEFAULT_TIMEOUT.toMillis(), 
TimeUnit.MILLISECONDS),
-                    "Failed to receive session signal on time");
-        } finally {
-            client.stop();
-        }
-    }
-}


Reply via email to