Skip to content

Commit

Permalink
GEODE-2898 A non-responsive SSL client can block a server's "acceptor…
Browse files Browse the repository at this point in the history
…" thread

This moves the SSL handshake from the acceptor thread to the handshake thread
and adds a unit test.
  • Loading branch information
bschuchardt committed May 10, 2017
1 parent 014ad42 commit 810186d
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -1235,7 +1235,6 @@ public void accept() {
break;
}
}
this.socketCreator.configureServerSSLSocket(s);
this.loggedAcceptError = false;

handOffNewClientConnection(s);
Expand Down Expand Up @@ -1408,6 +1407,7 @@ public void run2() {
}
} else {
s.setSoTimeout(this.acceptTimeout);
this.socketCreator.configureServerSSLSocket(s);
communicationMode = (byte) s.getInputStream().read();
if (logger.isTraceEnabled()) {
logger.trace("read communications mode(2) ", communicationMode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.sql.Time;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
Expand All @@ -34,13 +38,15 @@
import org.apache.geode.cache.server.CacheServer;
import org.apache.geode.internal.security.SecurableCommunicationChannel;
import org.apache.geode.security.AuthenticationRequiredException;
import org.apache.geode.test.dunit.AsyncInvocation;
import org.apache.geode.test.dunit.Host;
import org.apache.geode.test.dunit.IgnoredException;
import org.apache.geode.test.dunit.VM;
import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase;
import org.apache.geode.test.junit.categories.ClientServerTest;
import org.apache.geode.test.junit.categories.DistributedTest;
import org.apache.geode.util.test.TestUtil;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.experimental.categories.Category;

Expand Down Expand Up @@ -298,6 +304,73 @@ public void testCacheServerSSL() throws Exception {
serverVM.invoke(() -> doServerRegionTestTask());
}

/**
* GEODE-2898: A non-responsive SSL client can block a server's "acceptor" thread
* <p>
* Start a server and then connect to it without completing the SSL handshake
* </p>
* <p>
* Attempt to connect to the server using a real SSL client, demonstrating that the server is not
* blocked and can process the new connection request.
* </p>
*/
@Test
public void clientSlowToHandshakeDoesNotBlockServer() throws Throwable {
final Host host = Host.getHost(0);
VM serverVM = host.getVM(1);
VM clientVM = host.getVM(2);
VM slowClientVM = host.getVM(3);
getBlackboard().initBlackboard();

// a plain-text socket is used to connect to an ssl server & the handshake
// is never performed. The server will log this exception & it should be ignored
IgnoredException.addIgnoredException("javax.net.ssl.SSLHandshakeException", serverVM);

boolean cacheServerSslenabled = true;
boolean cacheClientSslenabled = true;
boolean cacheClientSslRequireAuth = true;

serverVM.invoke(() -> setUpServerVMTask(cacheServerSslenabled, true));
int port = serverVM.invoke(() -> createServerTask());

String hostName = host.getHostName();

AsyncInvocation slowAsync = slowClientVM.invokeAsync(() -> connectToServer(hostName, port));
try {
getBlackboard().waitForGate("serverIsBlocked", 60, TimeUnit.SECONDS);

clientVM.invoke(() -> setUpClientVMTask(hostName, port, cacheClientSslenabled,
cacheClientSslRequireAuth, CLIENT_KEY_STORE, CLIENT_TRUST_STORE));
clientVM.invoke(() -> doClientRegionTestTask());
serverVM.invoke(() -> doServerRegionTestTask());

} finally {
getBlackboard().signalGate("testIsCompleted");
try {
if (slowAsync.isAlive()) {
slowAsync.join(60000);
}
if (slowAsync.exceptionOccurred()) {
throw slowAsync.getException();
}
} finally {
assertFalse(slowAsync.isAlive());
}
}

}

private void connectToServer(String hostName, int port) throws Exception {
Socket sock = new Socket();
sock.connect(new InetSocketAddress(hostName, port));
try {
getBlackboard().signalGate("serverIsBlocked");
getBlackboard().waitForGate("testIsCompleted", 60, TimeUnit.SECONDS);
} finally {
sock.close();
}
}

@Test
public void testNonSSLClient() throws Exception {
final Host host = Host.getHost(0);
Expand Down

0 comments on commit 810186d

Please sign in to comment.