You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by sl...@apache.org on 2026/10/18 18:25:53 UTC
(maven-mvnd) branch mvnd-1.x updated: Time out the daemon's token read and keep its state on a failed check
This is an automated email from the ASF dual-hosted git repository.
slachiewicz pushed a commit to branch mvnd-1.x
in repository https://gitbox.apache.org/repos/asf/maven-mvnd.git
The following commit(s) were added to refs/heads/mvnd-1.x by this push:
new 486901d4 Time out the daemon's token read and keep its state on a failed check
486901d4 is described below
commit 486901d4f5be34eb79b3522b60b07458dabde84a
Author: Sylwester Lachiewicz <sl...@apache.org>
AuthorDate: Sun Oct 18 15:51:55 2026 +0200
Time out the daemon's token read and keep its state on a failed check
---
.../java/org/mvndaemon/mvnd/daemon/Server.java | 45 +++++++++++----
.../org/mvndaemon/mvnd/daemon/ServerTokenTest.java | 67 ++++++++++++++++++++++
2 files changed, 101 insertions(+), 11 deletions(-)
diff --git a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java
index 09a67548..68eb487e 100644
--- a/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java
+++ b/daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java
@@ -23,6 +23,8 @@ import java.io.InputStream;
import java.io.InterruptedIOException;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
@@ -85,6 +87,7 @@ public class Server implements AutoCloseable, Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(Server.class);
public static final int CANCEL_TIMEOUT = 10 * 1000;
+ static final long TOKEN_READ_TIMEOUT_MS = 5 * 1000;
private final String daemonId;
private final boolean noDaemon;
@@ -265,9 +268,8 @@ public class Server implements AutoCloseable, Runnable {
private void client(SocketChannel socket) {
LOGGER.info("Client connected");
if (!checkToken(socket)) {
+ // Leave the state alone: a connection that cannot authenticate must not change it
LOGGER.error("Received invalid token, dropping connection");
- updateState(DaemonState.Idle);
-
return;
}
@@ -312,20 +314,41 @@ public class Server implements AutoCloseable, Runnable {
}
private boolean checkToken(SocketChannel socket) {
- byte[] token = new byte[info.getToken().length];
- ByteBuffer tokenBuffer = ByteBuffer.wrap(token);
+ byte[] token = readToken(socket, info.getToken().length, TOKEN_READ_TIMEOUT_MS);
+ return MessageDigest.isEqual(info.getToken(), token);
+ }
+ /**
+ * Reads up to {@code size} bytes, giving up after {@code timeoutMs} so that a peer that connects and sends
+ * nothing cannot hold the (serial) accept loop. Bytes not received stay zero.
+ */
+ static byte[] readToken(SocketChannel socket, int size, long timeoutMs) {
+ byte[] token = new byte[size];
+ ByteBuffer tokenBuffer = ByteBuffer.wrap(token);
try {
- do {
- if (socket.read(tokenBuffer) == -1) {
- break;
+ socket.configureBlocking(false);
+ try (Selector selector = Selector.open()) {
+ socket.register(selector, SelectionKey.OP_READ);
+ long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs);
+ while (tokenBuffer.hasRemaining()) {
+ long left = TimeUnit.NANOSECONDS.toMillis(deadline - System.nanoTime());
+ if (left <= 0) {
+ LOGGER.info("No token received within {} ms", timeoutMs);
+ break;
+ }
+ if (selector.select(left) > 0) {
+ selector.selectedKeys().clear();
+ if (socket.read(tokenBuffer) == -1) {
+ break;
+ }
+ }
}
- } while (tokenBuffer.remaining() > 0);
+ }
+ socket.configureBlocking(true);
} catch (final IOException e) {
- LOGGER.debug("Discarding EOFException: {}", e.toString(), e);
+ LOGGER.debug("Discarding exception while reading the token: {}", e.toString(), e);
}
-
- return MessageDigest.isEqual(info.getToken(), token);
+ return token;
}
private void expirationCheck() {
diff --git a/daemon/src/test/java/org/mvndaemon/mvnd/daemon/ServerTokenTest.java b/daemon/src/test/java/org/mvndaemon/mvnd/daemon/ServerTokenTest.java
new file mode 100644
index 00000000..20c112be
--- /dev/null
+++ b/daemon/src/test/java/org/mvndaemon/mvnd/daemon/ServerTokenTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.mvndaemon.mvnd.daemon;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ServerTokenTest {
+
+ @Test
+ void readsTheFullToken() throws IOException {
+ byte[] sent = new byte[16];
+ for (int i = 0; i < sent.length; i++) {
+ sent[i] = (byte) (i + 1);
+ }
+ try (ServerSocketChannel server = listen();
+ SocketChannel client = SocketChannel.open(server.getLocalAddress());
+ SocketChannel accepted = server.accept()) {
+ client.write(ByteBuffer.wrap(sent));
+ assertArrayEquals(sent, Server.readToken(accepted, 16, 5_000));
+ assertTrue(accepted.isBlocking(), "the channel is handed back in blocking mode");
+ }
+ }
+
+ @Test
+ void givesUpOnASilentPeer() throws IOException {
+ try (ServerSocketChannel server = listen();
+ SocketChannel client = SocketChannel.open(server.getLocalAddress());
+ SocketChannel accepted = server.accept()) {
+ client.write(ByteBuffer.wrap(new byte[] {1, 2, 3}));
+ long start = System.nanoTime();
+ byte[] token = Server.readToken(accepted, 16, 300);
+ long elapsedMs = (System.nanoTime() - start) / 1_000_000;
+ assertTrue(elapsedMs < 5_000, "returned after " + elapsedMs + " ms");
+ assertArrayEquals(new byte[] {1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, token);
+ }
+ }
+
+ private static ServerSocketChannel listen() throws IOException {
+ return ServerSocketChannel.open().bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
+ }
+}