You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ud...@apache.org on 2017/06/06 17:49:11 UTC
geode git commit: GEODE-3023: Moved the population of the
socket.setSoTimeout before the SSL handshake. This allows for the SO_TIMEOUT
on the socket to be set before the any potential SSL failure.
Repository: geode
Updated Branches:
refs/heads/feature/GEODE-3023 [created] 00859197f
GEODE-3023: Moved the population of the socket.setSoTimeout before the SSL handshake.
This allows for the SO_TIMEOUT on the socket to be set before the any potential SSL failure.
Project: http://git-wip-us.apache.org/repos/asf/geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/00859197
Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/00859197
Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/00859197
Branch: refs/heads/feature/GEODE-3023
Commit: 00859197f08b6d1ed6096c2fdff7a4624607d685
Parents: 3110bd0
Author: Udo Kohlmeyer <uk...@pivotal.io>
Authored: Tue Jun 6 10:49:05 2017 -0700
Committer: Udo Kohlmeyer <uk...@pivotal.io>
Committed: Tue Jun 6 10:49:05 2017 -0700
----------------------------------------------------------------------
.../internal/tcpserver/TcpServer.java | 79 ++++---
.../tcpserver/TCPServerSSLJUnitTest.java | 219 +++++++++++++++++++
.../internal/tcpserver/TcpServerJUnitTest.java | 64 +++---
.../geode/internal/net/DelaySocketCreator.java | 45 ++++
4 files changed, 339 insertions(+), 68 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/geode/blob/00859197/geode-core/src/main/java/org/apache/geode/distributed/internal/tcpserver/TcpServer.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/tcpserver/TcpServer.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/tcpserver/TcpServer.java
index 86fe532..a4c6532 100755
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/tcpserver/TcpServer.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/tcpserver/TcpServer.java
@@ -14,32 +14,6 @@
*/
package org.apache.geode.distributed.internal.tcpserver;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.EOFException;
-import java.io.File;
-import java.io.IOException;
-import java.io.StreamCorruptedException;
-import java.net.InetAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.URL;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.net.ssl.SSLException;
-
-import org.apache.logging.log4j.Logger;
-
import org.apache.geode.CancelException;
import org.apache.geode.DataSerializer;
import org.apache.geode.SystemFailure;
@@ -62,6 +36,31 @@ import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.net.SocketCreator;
import org.apache.geode.internal.net.SocketCreatorFactory;
import org.apache.geode.internal.security.SecurableCommunicationChannel;
+import org.apache.logging.log4j.Logger;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.StreamCorruptedException;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.net.ssl.SSLException;
/**
* TCP server which listens on a port and delegates requests to a request handler. The server uses
@@ -69,7 +68,6 @@ import org.apache.geode.internal.security.SecurableCommunicationChannel;
* <p>
* This code was factored out of GossipServer.java to allow multiple handlers to share the same
* gossip server port.
- *
* @since GemFire 5.7
*/
public class TcpServer {
@@ -128,7 +126,7 @@ public class TcpServer {
private final String threadName;
private volatile Thread serverThread;
- private SocketCreator socketCreator;
+ protected SocketCreator socketCreator;
/*
* GemStoneAddition - Initialize versions map. Warning: This map must be compatible with all
@@ -143,8 +141,8 @@ public class TcpServer {
}
public TcpServer(int port, InetAddress bind_address, Properties sslConfig,
- DistributionConfigImpl cfg, TcpHandler handler, PoolStatHelper poolHelper,
- ThreadGroup threadGroup, String threadName) {
+ DistributionConfigImpl cfg, TcpHandler handler, PoolStatHelper poolHelper,
+ ThreadGroup threadGroup, String threadName) {
this.port = port;
this.bind_address = bind_address;
this.handler = handler;
@@ -164,17 +162,18 @@ public class TcpServer {
}
cfg = new DistributionConfigImpl(sslConfig);
}
+ }
+ protected SocketCreator getSocketCreator() {
if (this.socketCreator == null) {
this.socketCreator =
SocketCreatorFactory.getSocketCreatorForComponent(SecurableCommunicationChannel.LOCATOR);
- } else {
- throw new RuntimeException("The socket Creator already exists");
}
+ return socketCreator;
}
private static PooledExecutorWithDMStats createExecutor(PoolStatHelper poolHelper,
- final ThreadGroup threadGroup) {
+ final ThreadGroup threadGroup) {
ThreadFactory factory = new ThreadFactory() {
private final AtomicInteger threadNum = new AtomicInteger();
@@ -191,7 +190,7 @@ public class TcpServer {
}
public void restarting(InternalDistributedSystem ds, InternalCache cache,
- ClusterConfigurationService sharedConfig) throws IOException {
+ ClusterConfigurationService sharedConfig) throws IOException {
this.shuttingDown = false;
this.handler.restarting(ds, cache, sharedConfig);
startServerThread();
@@ -210,10 +209,10 @@ public class TcpServer {
private void startServerThread() throws IOException {
if (srv_sock == null || srv_sock.isClosed()) {
if (bind_address == null) {
- srv_sock = socketCreator.createServerSocket(port, BACKLOG);
+ srv_sock = getSocketCreator().createServerSocket(port, BACKLOG);
bind_address = srv_sock.getInetAddress();
} else {
- srv_sock = socketCreator.createServerSocket(port, BACKLOG, bind_address);
+ srv_sock = getSocketCreator().createServerSocket(port, BACKLOG, bind_address);
}
if (log.isInfoEnabled()) {
@@ -263,7 +262,6 @@ public class TcpServer {
/**
* Returns the value of the bound port. If the server was initialized with a port of 0 indicating
* that any ephemeral port should be used, this method will return the actual bound port.
- *
* @return the locator's tcp/ip port. This will be zero if the locator hasn't been started.
*/
public int getPort() {
@@ -340,8 +338,10 @@ public class TcpServer {
DataInputStream input = null;
Object request, response;
try {
- socketCreator.configureServerSSLSocket(sock);
+
sock.setSoTimeout(READ_TIMEOUT);
+ getSocketCreator().configureServerSSLSocket(sock);
+
try {
input = new DataInputStream(sock.getInputStream());
} catch (StreamCorruptedException e) {
@@ -351,7 +351,7 @@ public class TcpServer {
+ (sock.getInetAddress().getHostAddress() + ":" + sock.getPort()), e);
return;
}
-
+ System.err.println(SimpleDateFormat.getTimeInstance().format(new Date(System.currentTimeMillis()))+" ---- UDO: TcpServer.processRequest...."+sock.toString());
int gossipVersion = readGossipVersion(sock, input);
short versionOrdinal;
@@ -511,7 +511,6 @@ public class TcpServer {
/**
* Returns GossipVersion for older Gemfire versions.
- *
* @return gossip version
*/
public static int getGossipVersionForOrdinal(short ordinal) {
http://git-wip-us.apache.org/repos/asf/geode/blob/00859197/geode-core/src/test/java/org/apache/geode/distributed/internal/tcpserver/TCPServerSSLJUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/distributed/internal/tcpserver/TCPServerSSLJUnitTest.java b/geode-core/src/test/java/org/apache/geode/distributed/internal/tcpserver/TCPServerSSLJUnitTest.java
new file mode 100644
index 0000000..0851392
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/distributed/internal/tcpserver/TCPServerSSLJUnitTest.java
@@ -0,0 +1,219 @@
+/*
+ * 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.geode.distributed.internal.tcpserver;
+
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_CIPHERS;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_ENABLED_COMPONENTS;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_PASSWORD;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_TYPE;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_PROTOCOLS;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD;
+
+import org.apache.geode.cache.GemFireCache;
+import org.apache.geode.distributed.DistributedSystem;
+import org.apache.geode.distributed.internal.ClusterConfigurationService;
+import org.apache.geode.distributed.internal.DistributionConfig;
+import org.apache.geode.distributed.internal.DistributionConfigImpl;
+import org.apache.geode.distributed.internal.PoolStatHelper;
+import org.apache.geode.internal.AvailablePort;
+import org.apache.geode.internal.admin.SSLConfig;
+import org.apache.geode.internal.net.DelaySocketCreator;
+import org.apache.geode.internal.net.SSLConfigurationFactory;
+import org.apache.geode.internal.net.SocketCreator;
+import org.apache.geode.internal.net.SocketCreatorFactory;
+import org.apache.geode.internal.security.SecurableCommunicationChannel;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+import org.apache.geode.test.junit.categories.MembershipTest;
+import org.apache.geode.util.test.TestUtil;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.RestoreSystemProperties;
+import org.junit.experimental.categories.Category;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+
+@Category({IntegrationTest.class, MembershipTest.class})
+public class TCPServerSSLJUnitTest {
+
+ private InetAddress localhost;
+ private int port;
+ private PoolStatHelper stats;
+ private DummyTcpServer server;
+
+ @Rule
+ public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
+ private final String expectedSocketTimeout = "2000";
+
+ @Before
+ public void setup() {
+ SocketCreatorFactory.setDistributionConfig(new DistributionConfigImpl(new Properties()));
+ }
+
+ @After
+ public void teardown() {
+ SocketCreatorFactory.close();
+ }
+
+ private void startTimeDelayedTcpServer(Properties sslProperties) throws IOException {
+ localhost = InetAddress.getLocalHost();
+ port = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
+
+ stats = new SimpleStats();
+ server = new DummyTcpServer(port, localhost, sslProperties, null, new EchoHandler(), stats,
+ Thread.currentThread().getThreadGroup(), "server thread");
+ server.start();
+ }
+
+ @Test
+ public void testSSLSocketTimeOut() throws IOException {
+ try {
+
+ System.setProperty(DistributionConfig.GEMFIRE_PREFIX + "TcpServer.READ_TIMEOUT",
+ expectedSocketTimeout);
+ Properties sslProperties = getSSLConfigurationProperties();
+ startTimeDelayedTcpServer(sslProperties);
+
+ createTcpClientConnection();
+
+ } catch (LocatorCancelException e){
+ // we catching the LocatorCancelException. Expected to have the exception thrown
+ }
+
+ List<Integer> recordedSocketsForSocketCreator = server.getRecordedSocketTimeouts();
+
+ Assert.assertEquals(1, recordedSocketsForSocketCreator.size());
+ for (Integer socketTimeOut : recordedSocketsForSocketCreator) {
+ Assert.assertEquals(Integer.parseInt(expectedSocketTimeout), socketTimeOut.intValue());
+ }
+ }
+
+ private Properties getSSLConfigurationProperties() {
+ Properties sslProperties = new Properties();
+ sslProperties
+ .setProperty(SSL_ENABLED_COMPONENTS, SecurableCommunicationChannel.LOCATOR.getConstant());
+ sslProperties.setProperty(SSL_KEYSTORE,
+ TestUtil.getResourcePath(getClass(), "/org/apache/geode/internal/net/multiKey.jks"));
+ sslProperties.setProperty(SSL_TRUSTSTORE,
+ TestUtil.getResourcePath(getClass(), "/org/apache/geode/internal/net/multiKeyTrust.jks"));
+ sslProperties.setProperty(SSL_KEYSTORE_PASSWORD, "password");
+ sslProperties.setProperty(SSL_TRUSTSTORE_PASSWORD, "password");
+ sslProperties.setProperty(SSL_KEYSTORE_TYPE, "JKS");
+ sslProperties.setProperty(SSL_CIPHERS, "any");
+ sslProperties.setProperty(SSL_PROTOCOLS, "any");
+ return sslProperties;
+ }
+
+ private void createTcpClientConnection() {
+ try {
+ new TcpClient().requestToServer(localhost, port, Boolean.valueOf(false), 60 * 1000);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static class EchoHandler implements TcpHandler {
+
+ protected/* GemStoneAddition */ boolean shutdown;
+
+
+ public void init(TcpServer tcpServer) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Object processRequest(Object request) throws IOException {
+ return request;
+ }
+
+ public void shutDown() {
+ shutdown = true;
+ }
+
+ public void restarting(DistributedSystem ds, GemFireCache cache,
+ ClusterConfigurationService sharedConfig) {
+ }
+
+ public void endRequest(Object request, long startTime) {
+ }
+
+ public void endResponse(Object request, long startTime) {
+ }
+
+ }
+
+
+ private class DummyTcpServer extends TcpServer {
+ private DistributionConfig distributionConfig;
+ private List<Integer> recordedSocketsTimeouts = new ArrayList<>();
+
+ public DummyTcpServer(int port, InetAddress bind_address, Properties sslConfig,
+ DistributionConfigImpl cfg,
+ TcpHandler handler,
+ PoolStatHelper poolHelper, ThreadGroup threadGroup, String threadName) {
+ super(port, bind_address, sslConfig, cfg, handler, poolHelper, threadGroup, threadName);
+ if (cfg == null) {
+ cfg = new DistributionConfigImpl(sslConfig);
+ }
+ this.distributionConfig = cfg;
+ }
+
+ @Override
+ protected SocketCreator getSocketCreator() {
+ if (this.socketCreator == null) {
+ SSLConfigurationFactory.setDistributionConfig(distributionConfig);
+ SSLConfig
+ sslConfig =
+ SSLConfigurationFactory.getSSLConfigForComponent(SecurableCommunicationChannel.LOCATOR);
+ this.socketCreator = new DelaySocketCreator(sslConfig, recordedSocketsTimeouts);
+ }
+ return socketCreator;
+ }
+
+ public List<Integer> getRecordedSocketTimeouts() {
+ return recordedSocketsTimeouts;
+ }
+ }
+
+ private static class SimpleStats implements PoolStatHelper {
+ AtomicInteger started = new AtomicInteger();
+ AtomicInteger ended = new AtomicInteger();
+
+
+ public void endJob() {
+ started.incrementAndGet();
+ }
+
+ public void startJob() {
+ ended.incrementAndGet();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/geode/blob/00859197/geode-core/src/test/java/org/apache/geode/distributed/internal/tcpserver/TcpServerJUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/distributed/internal/tcpserver/TcpServerJUnitTest.java b/geode-core/src/test/java/org/apache/geode/distributed/internal/tcpserver/TcpServerJUnitTest.java
index 7c7a2b3..05fc302 100644
--- a/geode-core/src/test/java/org/apache/geode/distributed/internal/tcpserver/TcpServerJUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/distributed/internal/tcpserver/TcpServerJUnitTest.java
@@ -14,7 +14,25 @@
*/
package org.apache.geode.distributed.internal.tcpserver;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.geode.DataSerializable;
+import org.apache.geode.cache.GemFireCache;
+import org.apache.geode.distributed.DistributedSystem;
+import org.apache.geode.distributed.internal.ClusterConfigurationService;
+import org.apache.geode.distributed.internal.DistributionConfigImpl;
+import org.apache.geode.distributed.internal.PoolStatHelper;
+import org.apache.geode.internal.AvailablePort;
+import org.apache.geode.internal.net.SocketCreatorFactory;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+import org.apache.geode.test.junit.categories.MembershipTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
import java.io.DataInput;
import java.io.DataOutput;
@@ -27,22 +45,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.geode.distributed.internal.ClusterConfigurationService;
-import org.apache.geode.test.junit.categories.MembershipTest;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-import org.apache.geode.DataSerializable;
-import org.apache.geode.cache.GemFireCache;
-import org.apache.geode.distributed.DistributedSystem;
-import org.apache.geode.distributed.internal.DistributionConfigImpl;
-import org.apache.geode.distributed.internal.PoolStatHelper;
-import org.apache.geode.internal.AvailablePort;
-import org.apache.geode.internal.net.SocketCreatorFactory;
-import org.apache.geode.test.junit.categories.IntegrationTest;
-
@Category({IntegrationTest.class, MembershipTest.class})
public class TcpServerJUnitTest {
@@ -179,11 +181,14 @@ public class TcpServerJUnitTest {
}
public void restarting(DistributedSystem ds, GemFireCache cache,
- ClusterConfigurationService sharedConfig) {}
+ ClusterConfigurationService sharedConfig) {
+ }
- public void endRequest(Object request, long startTime) {}
+ public void endRequest(Object request, long startTime) {
+ }
- public void endResponse(Object request, long startTime) {}
+ public void endResponse(Object request, long startTime) {
+ }
}
@@ -195,7 +200,8 @@ public class TcpServerJUnitTest {
this.latch = latch;
}
- public void init(TcpServer tcpServer) {}
+ public void init(TcpServer tcpServer) {
+ }
public Object processRequest(Object request) throws IOException {
Boolean delay = (Boolean) request;
@@ -205,20 +211,22 @@ public class TcpServerJUnitTest {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
- return delay;
- } else {
- return delay;
}
+ return delay;
}
- public void shutDown() {}
+ public void shutDown() {
+ }
public void restarting(DistributedSystem ds, GemFireCache cache,
- ClusterConfigurationService sharedConfig) {}
+ ClusterConfigurationService sharedConfig) {
+ }
- public void endRequest(Object request, long startTime) {}
+ public void endRequest(Object request, long startTime) {
+ }
- public void endResponse(Object request, long startTime) {}
+ public void endResponse(Object request, long startTime) {
+ }
}
private/* GemStoneAddition */ static class SimpleStats implements PoolStatHelper {
http://git-wip-us.apache.org/repos/asf/geode/blob/00859197/geode-core/src/test/java/org/apache/geode/internal/net/DelaySocketCreator.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/net/DelaySocketCreator.java b/geode-core/src/test/java/org/apache/geode/internal/net/DelaySocketCreator.java
new file mode 100644
index 0000000..ce518b1
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/internal/net/DelaySocketCreator.java
@@ -0,0 +1,45 @@
+/*
+ * 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.geode.internal.net;
+
+import org.apache.geode.internal.admin.SSLConfig;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.List;
+import javax.net.ssl.SSLException;
+
+public class DelaySocketCreator extends SocketCreator {
+
+ private List<Integer> socketSoTimeouts;
+
+ /**
+ * Constructs new SocketCreator instance.
+ */
+ public DelaySocketCreator(SSLConfig sslConfig, List<Integer> sockets) {
+ super(sslConfig);
+ this.socketSoTimeouts = sockets;
+ }
+
+ @Override
+ public void configureServerSSLSocket(Socket socket) throws IOException {
+ this.socketSoTimeouts.add(socket.getSoTimeout());
+ throw new SSLException("This is a test SSLException");
+ }
+
+ public List<Integer> getSocketSoTimeouts() { return socketSoTimeouts; }
+}