From 9524e327a811af1da9e1b8157fdd3d647299cd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kohlschu=CC=88tter?= Date: Thu, 7 Mar 2024 08:53:27 +0100 Subject: [PATCH] mysql: AFUNIXDatabaseSocketFactoryCJ: Respect timeouts AFUNIXDatabaseSocketFactoryCJ.connect takes a "loginTimeout" parameter, and the system properties may specify a "connectTimeout". While connecting to AF_UNIX sockets appears to not block in the regular case, let's assume there's a corner case where respecting these values makes sense. Follow standard Mysql connector behavior and use these two timeouts to determine the connect timeout for our unix domain sockets. https://github.com/kohlschutter/junixsocket/issues/154 --- .../mysql/AFUNIXDatabaseSocketFactoryCJ.java | 11 +++- .../org/newsclub/net/mysql/MysqlHelper.java | 43 ++++++++++++++++ .../AFUNIXDatabaseSocketFactoryCJTest.java | 51 +++++++++++++++++++ 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 junixsocket-mysql/src/main/java/org/newsclub/net/mysql/MysqlHelper.java create mode 100644 junixsocket-mysql/src/test/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJTest.java diff --git a/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJ.java b/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJ.java index 9a49376f0..828fc614e 100644 --- a/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJ.java +++ b/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJ.java @@ -25,6 +25,7 @@ import org.newsclub.net.unix.AFUNIXSocketAddress; import com.kohlschutter.annotations.compiletime.SuppressFBWarnings; +import com.mysql.cj.conf.PropertyKey; import com.mysql.cj.conf.PropertySet; import com.mysql.cj.conf.RuntimeProperty; import com.mysql.cj.protocol.ExportControlled; @@ -61,8 +62,14 @@ public Socket connect(String hostname, int portNumber, } final File socketFile = new File(sock); - this.rawSocket = AFUNIXSocket.connectTo(AFUNIXSocketAddress.of(socketFile)); - return rawSocket; + AFUNIXSocket socket = AFUNIXSocket.newInstance(); + + int connectTimeout = props.getIntegerProperty(PropertyKey.connectTimeout).getValue(); + int timeout = MysqlHelper.shorterTimeout(connectTimeout, loginTimeout); + + socket.connect(AFUNIXSocketAddress.of(socketFile), timeout); + + return (this.rawSocket = socket); } @SuppressWarnings({"unchecked"}) diff --git a/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/MysqlHelper.java b/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/MysqlHelper.java new file mode 100644 index 000000000..92ba3f6b7 --- /dev/null +++ b/junixsocket-mysql/src/main/java/org/newsclub/net/mysql/MysqlHelper.java @@ -0,0 +1,43 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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.newsclub.net.mysql; + +/** + * Code sharable between code in junixsocket-mysql. + * + * @author Christian Kohlschütter + */ +final class MysqlHelper { + private MysqlHelper() { + } + + /** + * Returns the shorter timeout of two given timeouts (assuming 0 means "unlimited"). + * + * @param a The first timeout. + * @param b The second timeout. + * @return The shorter timeout. + */ + static int shorterTimeout(int a, int b) { + if (a == 0 || (b != 0 && b < a)) { + return b; + } else { + return a; + } + } +} diff --git a/junixsocket-mysql/src/test/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJTest.java b/junixsocket-mysql/src/test/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJTest.java new file mode 100644 index 000000000..acb1c7e95 --- /dev/null +++ b/junixsocket-mysql/src/test/java/org/newsclub/net/mysql/AFUNIXDatabaseSocketFactoryCJTest.java @@ -0,0 +1,51 @@ +/* + * junixsocket + * + * Copyright 2009-2023 Christian Kohlschütter + * + * Licensed 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.newsclub.net.mysql; + +import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; +import org.newsclub.net.unix.AFUNIXServerSocket; +import org.newsclub.net.unix.AFUNIXSocketAddress; + +import com.mysql.cj.conf.DefaultPropertySet; +import com.mysql.cj.conf.StringPropertyDefinition; + +public class AFUNIXDatabaseSocketFactoryCJTest { + + @Test + public void testConnectTimeout() throws Exception { + AFUNIXSocketAddress addr = AFUNIXSocketAddress.ofNewTempFile(); + try (AFUNIXServerSocket serverSocket = AFUNIXServerSocket.newInstance()) { + serverSocket.bind(addr, 1); + + AFUNIXDatabaseSocketFactoryCJ sf = new AFUNIXDatabaseSocketFactoryCJ(); + + DefaultPropertySet props = new DefaultPropertySet(); + props.addProperty(new StringPropertyDefinition("junixsocket.file", "junixsocket.file", addr + .getFile().toString(), true, "description", "0", "category", 1).createRuntimeProperty()); + + assertTimeoutPreemptively(Duration.ofSeconds(5), () -> { + sf.connect("localhost", 0, props, 1); + }); + } + } + +}