Issue #5684 - Update TLS tests
Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
parent
c4021023ee
commit
713136fb0e
|
@ -14,10 +14,12 @@
|
||||||
package org.eclipse.jetty.client;
|
package org.eclipse.jetty.client;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.UnresolvedAddressException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -38,9 +40,11 @@ import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.toolchain.test.IO;
|
import org.eclipse.jetty.toolchain.test.IO;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Assumptions;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
|
@ -52,6 +56,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
{
|
{
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(HttpClientRedirectTest.class);
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(ScenarioProvider.class)
|
@ArgumentsSource(ScenarioProvider.class)
|
||||||
public void test303(Scenario scenario) throws Exception
|
public void test303(Scenario scenario) throws Exception
|
||||||
|
@ -287,24 +293,26 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(ScenarioProvider.class)
|
@ArgumentsSource(ScenarioProvider.class)
|
||||||
@Disabled
|
|
||||||
public void testRedirectFailed(Scenario scenario) throws Exception
|
public void testRedirectFailed(Scenario scenario) throws Exception
|
||||||
{
|
{
|
||||||
// TODO this test is failing with timout after an ISP upgrade?? DNS dependent?
|
// Skip this test if DNS Hijacking is detected
|
||||||
|
Assumptions.assumeFalse(detectDnsHijacking());
|
||||||
|
|
||||||
start(scenario, new RedirectHandler());
|
start(scenario, new RedirectHandler());
|
||||||
|
|
||||||
try
|
ExecutionException e = assertThrows(ExecutionException.class,
|
||||||
{
|
() -> client.newRequest("localhost", connector.getLocalPort())
|
||||||
client.newRequest("localhost", connector.getLocalPort())
|
|
||||||
.scheme(scenario.getScheme())
|
.scheme(scenario.getScheme())
|
||||||
.path("/303/doesNotExist/done")
|
.path("/303/doesNotExist/done")
|
||||||
.timeout(5, TimeUnit.SECONDS)
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
.send();
|
.send());
|
||||||
}
|
|
||||||
catch (ExecutionException x)
|
assertThat("Cause", e.getCause(), Matchers.anyOf(
|
||||||
{
|
// Exception seen on some updates of OpenJDK 8
|
||||||
assertThat(x.getCause(), Matchers.instanceOf(UnresolvedAddressException.class));
|
// Matchers.instanceOf(UnresolvedAddressException.class),
|
||||||
}
|
// Exception seen on OpenJDK 11+
|
||||||
|
Matchers.instanceOf(UnknownHostException.class))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -711,4 +719,48 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean detectDnsHijacking()
|
||||||
|
{
|
||||||
|
String host1 = randomHostname();
|
||||||
|
String host2 = randomHostname();
|
||||||
|
String addr1 = getInetHostAddress(host1);
|
||||||
|
String addr2 = getInetHostAddress(host2);
|
||||||
|
|
||||||
|
boolean ret = (addr1.equals(addr2));
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
LOG.warn("DNS Hijacking detected (these should not return the same host address): host1={} ({}), host2={} ({})",
|
||||||
|
host1, addr1,
|
||||||
|
host2, addr2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getInetHostAddress(String hostname)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InetAddress addr = InetAddress.getByName(hostname);
|
||||||
|
return addr.getHostAddress();
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
return "<unknown:" + hostname + ">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String randomHostname()
|
||||||
|
{
|
||||||
|
String digits = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||||
|
Random random = new Random();
|
||||||
|
char[] host = new char[7 + random.nextInt(8)];
|
||||||
|
for (int i = 0; i < host.length; ++i)
|
||||||
|
{
|
||||||
|
host[i] = digits.charAt(random.nextInt(digits.length()));
|
||||||
|
}
|
||||||
|
return new String(host) + ".tld.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
@ -66,7 +67,6 @@ import org.eclipse.jetty.util.thread.ExecutorThreadPool;
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.EnabledOnJre;
|
import org.junit.jupiter.api.condition.EnabledOnJre;
|
||||||
import org.junit.jupiter.api.condition.JRE;
|
import org.junit.jupiter.api.condition.JRE;
|
||||||
|
@ -265,10 +265,20 @@ public class HttpClientTLSTest
|
||||||
assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
|
assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
// In JDK 11+, a mismatch on the client does not generate any bytes towards
|
/**
|
||||||
// the server, while in previous JDKs the client sends to the server the close_notify.
|
* This tests the behavior of the Client side when you have an intentional
|
||||||
// @EnabledOnJre({JRE.JAVA_8, JRE.JAVA_9, JRE.JAVA_10})
|
* mismatch of the TLS Protocol and TLS Ciphers on the client and attempt to
|
||||||
@Disabled("No longer viable, TLS protocol behavior changed in 8u272")
|
* initiate a connection.
|
||||||
|
* <p>
|
||||||
|
* In older versions of Java 8 (pre 8u272) the JDK client side logic
|
||||||
|
* would generate bytes and send it to the server along with a close_notify
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Starting in Java 8 (8u272) and Java 11.0.0, the client logic will not
|
||||||
|
* send any bytes to the server.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
public void testMismatchBetweenTLSProtocolAndTLSCiphersOnClient() throws Exception
|
public void testMismatchBetweenTLSProtocolAndTLSCiphersOnClient() throws Exception
|
||||||
{
|
{
|
||||||
SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
|
SslContextFactory.Server serverTLSFactory = createServerSslContextFactory();
|
||||||
|
@ -285,11 +295,18 @@ public class HttpClientTLSTest
|
||||||
});
|
});
|
||||||
|
|
||||||
SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
|
SslContextFactory.Client clientTLSFactory = createClientSslContextFactory();
|
||||||
// TLS 1.1 protocol, but only TLS 1.2 ciphers.
|
// Use TLSv1.2 (as TLSv1.1 and older are now disabled in Java 8u292+ and Java 11+)
|
||||||
clientTLSFactory.setIncludeProtocols("TLSv1.1");
|
clientTLSFactory.setIncludeProtocols("TLSv1.2");
|
||||||
clientTLSFactory.setIncludeCipherSuites("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
|
// Use only a cipher suite that exists for TLSv1.3 (this is the mismatch)
|
||||||
|
clientTLSFactory.setIncludeCipherSuites("TLS_AES_256_GCM_SHA384");
|
||||||
startClient(clientTLSFactory);
|
startClient(clientTLSFactory);
|
||||||
|
|
||||||
|
// If this JVM has "TLSv1.3" present, then it's assumed that it also has the new logic
|
||||||
|
// to not send bytes to the server when a protocol and cipher suite mismatch occurs
|
||||||
|
// on the client side configuration.
|
||||||
|
List<String> supportedClientProtocols = Arrays.asList(clientTLSFactory.getSslContext().getSupportedSSLParameters().getProtocols());
|
||||||
|
boolean expectServerFailure = !(supportedClientProtocols.contains("TLSv1.3"));
|
||||||
|
|
||||||
CountDownLatch clientLatch = new CountDownLatch(1);
|
CountDownLatch clientLatch = new CountDownLatch(1);
|
||||||
client.addBean(new SslHandshakeListener()
|
client.addBean(new SslHandshakeListener()
|
||||||
{
|
{
|
||||||
|
@ -306,7 +323,8 @@ public class HttpClientTLSTest
|
||||||
.timeout(5, TimeUnit.SECONDS)
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
.send());
|
.send());
|
||||||
|
|
||||||
assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
|
if (expectServerFailure)
|
||||||
|
assertTrue(serverLatch.await(1, TimeUnit.SECONDS));
|
||||||
assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
|
assertTrue(clientLatch.await(1, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,358 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
|
||||||
//
|
|
||||||
// This program and the accompanying materials are made available under the
|
|
||||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
||||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
|
||||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
|
||||||
// ========================================================================
|
|
||||||
//
|
|
||||||
|
|
||||||
package org.eclipse.jetty.client.ssl;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
import javax.net.ssl.SSLServerSocket;
|
|
||||||
import javax.net.ssl.SSLSocket;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
|
||||||
import org.eclipse.jetty.client.api.Request;
|
|
||||||
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
|
|
||||||
import org.eclipse.jetty.client.util.FutureResponseListener;
|
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
|
||||||
import org.eclipse.jetty.io.ClientConnector;
|
|
||||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
// This whole test is very specific to how TLS < 1.3 works.
|
|
||||||
// Starting in Java 11, TLS/1.3 is now enabled by default.
|
|
||||||
@Disabled("Since 8u272 this is no longer valid")
|
|
||||||
public class SslBytesClientTest extends SslBytesTest
|
|
||||||
{
|
|
||||||
private ExecutorService threadPool;
|
|
||||||
private HttpClient client;
|
|
||||||
private SslContextFactory.Client sslContextFactory;
|
|
||||||
private SSLServerSocket acceptor;
|
|
||||||
private SimpleProxy proxy;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void init() throws Exception
|
|
||||||
{
|
|
||||||
threadPool = Executors.newCachedThreadPool();
|
|
||||||
|
|
||||||
ClientConnector clientConnector = new ClientConnector();
|
|
||||||
clientConnector.setSelectors(1);
|
|
||||||
sslContextFactory = new SslContextFactory.Client(true);
|
|
||||||
clientConnector.setSslContextFactory(sslContextFactory);
|
|
||||||
client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector));
|
|
||||||
client.setMaxConnectionsPerDestination(1);
|
|
||||||
File keyStore = MavenTestingUtils.getTestResourceFile("keystore.p12");
|
|
||||||
sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath());
|
|
||||||
sslContextFactory.setKeyStorePassword("storepwd");
|
|
||||||
client.start();
|
|
||||||
|
|
||||||
SSLContext sslContext = this.sslContextFactory.getSslContext();
|
|
||||||
acceptor = (SSLServerSocket)sslContext.getServerSocketFactory().createServerSocket(0);
|
|
||||||
|
|
||||||
int serverPort = acceptor.getLocalPort();
|
|
||||||
|
|
||||||
proxy = new SimpleProxy(threadPool, "localhost", serverPort);
|
|
||||||
proxy.start();
|
|
||||||
logger.info(":{} <==> :{}", proxy.getPort(), serverPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void destroy() throws Exception
|
|
||||||
{
|
|
||||||
if (acceptor != null)
|
|
||||||
acceptor.close();
|
|
||||||
if (proxy != null)
|
|
||||||
proxy.stop();
|
|
||||||
if (client != null)
|
|
||||||
client.stop();
|
|
||||||
if (threadPool != null)
|
|
||||||
threadPool.shutdownNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testHandshake() throws Exception
|
|
||||||
{
|
|
||||||
Request request = client.newRequest("localhost", proxy.getPort());
|
|
||||||
FutureResponseListener listener = new FutureResponseListener(request);
|
|
||||||
request.scheme(HttpScheme.HTTPS.asString()).send(listener);
|
|
||||||
|
|
||||||
assertTrue(proxy.awaitClient(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
try (SSLSocket server = (SSLSocket)acceptor.accept())
|
|
||||||
{
|
|
||||||
server.setUseClientMode(false);
|
|
||||||
|
|
||||||
Future<Object> handshake = threadPool.submit(() ->
|
|
||||||
{
|
|
||||||
server.startHandshake();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Client Hello
|
|
||||||
TLSRecord record = proxy.readFromClient();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToServer(record);
|
|
||||||
|
|
||||||
// Server Hello + Certificate + Server Done
|
|
||||||
record = proxy.readFromServer();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToClient(record);
|
|
||||||
|
|
||||||
// Client Key Exchange
|
|
||||||
record = proxy.readFromClient();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToServer(record);
|
|
||||||
|
|
||||||
// Change Cipher Spec
|
|
||||||
record = proxy.readFromClient();
|
|
||||||
assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType());
|
|
||||||
proxy.flushToServer(record);
|
|
||||||
|
|
||||||
// Client Done
|
|
||||||
record = proxy.readFromClient();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToServer(record);
|
|
||||||
|
|
||||||
// Change Cipher Spec
|
|
||||||
record = proxy.readFromServer();
|
|
||||||
assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType());
|
|
||||||
proxy.flushToClient(record);
|
|
||||||
|
|
||||||
// Server Done
|
|
||||||
record = proxy.readFromServer();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToClient(record);
|
|
||||||
|
|
||||||
assertNull(handshake.get(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
|
|
||||||
// Read request
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(server.getInputStream(), StandardCharsets.UTF_8));
|
|
||||||
String line = reader.readLine();
|
|
||||||
assertTrue(line.startsWith("GET"));
|
|
||||||
while (line.length() > 0)
|
|
||||||
{
|
|
||||||
line = reader.readLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write response
|
|
||||||
OutputStream output = server.getOutputStream();
|
|
||||||
output.write(("HTTP/1.1 200 OK\r\n" +
|
|
||||||
"Content-Length: 0\r\n" +
|
|
||||||
"\r\n").getBytes(StandardCharsets.UTF_8));
|
|
||||||
output.flush();
|
|
||||||
assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
ContentResponse response = listener.get(5, TimeUnit.SECONDS);
|
|
||||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testServerRenegotiation() throws Exception
|
|
||||||
{
|
|
||||||
Request request = client.newRequest("localhost", proxy.getPort());
|
|
||||||
FutureResponseListener listener = new FutureResponseListener(request);
|
|
||||||
request.scheme(HttpScheme.HTTPS.asString()).send(listener);
|
|
||||||
|
|
||||||
assertTrue(proxy.awaitClient(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
try (SSLSocket server = (SSLSocket)acceptor.accept())
|
|
||||||
{
|
|
||||||
server.setUseClientMode(false);
|
|
||||||
|
|
||||||
Future<Object> handshake = threadPool.submit(() ->
|
|
||||||
{
|
|
||||||
server.startHandshake();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
|
|
||||||
assertNull(handshake.get(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
// Read request
|
|
||||||
InputStream serverInput = server.getInputStream();
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(serverInput, StandardCharsets.UTF_8));
|
|
||||||
String line = reader.readLine();
|
|
||||||
assertTrue(line.startsWith("GET"));
|
|
||||||
while (line.length() > 0)
|
|
||||||
{
|
|
||||||
line = reader.readLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputStream serverOutput = server.getOutputStream();
|
|
||||||
byte[] data1 = new byte[1024];
|
|
||||||
Arrays.fill(data1, (byte)'X');
|
|
||||||
String content1 = new String(data1, StandardCharsets.UTF_8);
|
|
||||||
byte[] data2 = new byte[1024];
|
|
||||||
Arrays.fill(data2, (byte)'Y');
|
|
||||||
final String content2 = new String(data2, StandardCharsets.UTF_8);
|
|
||||||
// Write first part of the response
|
|
||||||
serverOutput.write(("HTTP/1.1 200 OK\r\n" +
|
|
||||||
"Content-Type: text/plain\r\n" +
|
|
||||||
"Content-Length: " + (content1.length() + content2.length()) + "\r\n" +
|
|
||||||
"\r\n" +
|
|
||||||
content1).getBytes(StandardCharsets.UTF_8));
|
|
||||||
serverOutput.flush();
|
|
||||||
assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
// Renegotiate
|
|
||||||
Future<Object> renegotiation = threadPool.submit(() ->
|
|
||||||
{
|
|
||||||
server.startHandshake();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Renegotiation Handshake
|
|
||||||
TLSRecord record = proxy.readFromServer();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToClient(record);
|
|
||||||
|
|
||||||
// Renegotiation Handshake
|
|
||||||
record = proxy.readFromClient();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToServer(record);
|
|
||||||
|
|
||||||
// Trigger a read to have the server write the final renegotiation steps
|
|
||||||
server.setSoTimeout(100);
|
|
||||||
assertThrows(SocketTimeoutException.class, () -> serverInput.read());
|
|
||||||
|
|
||||||
// Renegotiation Handshake
|
|
||||||
record = proxy.readFromServer();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToClient(record);
|
|
||||||
|
|
||||||
// Renegotiation Change Cipher
|
|
||||||
record = proxy.readFromServer();
|
|
||||||
assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType());
|
|
||||||
proxy.flushToClient(record);
|
|
||||||
|
|
||||||
// Renegotiation Handshake
|
|
||||||
record = proxy.readFromServer();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToClient(record);
|
|
||||||
|
|
||||||
// Renegotiation Change Cipher
|
|
||||||
record = proxy.readFromClient();
|
|
||||||
assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType());
|
|
||||||
proxy.flushToServer(record);
|
|
||||||
|
|
||||||
// Renegotiation Handshake
|
|
||||||
record = proxy.readFromClient();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToServer(record);
|
|
||||||
|
|
||||||
assertNull(renegotiation.get(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
// Complete the response
|
|
||||||
automaticProxyFlow = proxy.startAutomaticFlow();
|
|
||||||
serverOutput.write(data2);
|
|
||||||
serverOutput.flush();
|
|
||||||
assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
ContentResponse response = listener.get(5, TimeUnit.SECONDS);
|
|
||||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
|
||||||
assertEquals(data1.length + data2.length, response.getContent().length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testServerRenegotiationWhenRenegotiationIsForbidden() throws Exception
|
|
||||||
{
|
|
||||||
sslContextFactory.setRenegotiationAllowed(false);
|
|
||||||
|
|
||||||
Request request = client.newRequest("localhost", proxy.getPort());
|
|
||||||
FutureResponseListener listener = new FutureResponseListener(request);
|
|
||||||
request.scheme(HttpScheme.HTTPS.asString()).send(listener);
|
|
||||||
|
|
||||||
assertTrue(proxy.awaitClient(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
try (SSLSocket server = (SSLSocket)acceptor.accept())
|
|
||||||
{
|
|
||||||
server.setUseClientMode(false);
|
|
||||||
|
|
||||||
Future<Object> handshake = threadPool.submit(() ->
|
|
||||||
{
|
|
||||||
server.startHandshake();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
|
|
||||||
assertNull(handshake.get(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
// Read request
|
|
||||||
InputStream serverInput = server.getInputStream();
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(serverInput, StandardCharsets.UTF_8));
|
|
||||||
String line = reader.readLine();
|
|
||||||
assertTrue(line.startsWith("GET"));
|
|
||||||
while (line.length() > 0)
|
|
||||||
{
|
|
||||||
line = reader.readLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputStream serverOutput = server.getOutputStream();
|
|
||||||
byte[] data1 = new byte[1024];
|
|
||||||
Arrays.fill(data1, (byte)'X');
|
|
||||||
String content1 = new String(data1, StandardCharsets.UTF_8);
|
|
||||||
byte[] data2 = new byte[1024];
|
|
||||||
Arrays.fill(data2, (byte)'Y');
|
|
||||||
final String content2 = new String(data2, StandardCharsets.UTF_8);
|
|
||||||
// Write first part of the response
|
|
||||||
serverOutput.write(("HTTP/1.1 200 OK\r\n" +
|
|
||||||
"Content-Type: text/plain\r\n" +
|
|
||||||
"Content-Length: " + (content1.length() + content2.length()) + "\r\n" +
|
|
||||||
"\r\n" +
|
|
||||||
content1).getBytes(StandardCharsets.UTF_8));
|
|
||||||
serverOutput.flush();
|
|
||||||
assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
|
|
||||||
|
|
||||||
// Renegotiate
|
|
||||||
threadPool.submit(() ->
|
|
||||||
{
|
|
||||||
server.startHandshake();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Renegotiation Handshake
|
|
||||||
TLSRecord record = proxy.readFromServer();
|
|
||||||
assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
|
|
||||||
proxy.flushToClient(record);
|
|
||||||
|
|
||||||
// Client sends close alert.
|
|
||||||
record = proxy.readFromClient();
|
|
||||||
assertEquals(TLSRecord.Type.ALERT, record.getType());
|
|
||||||
record = proxy.readFromClient();
|
|
||||||
assertNull(record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -46,8 +46,8 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
import org.junit.jupiter.api.Assumptions;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledOnOs;
|
import org.junit.jupiter.api.condition.DisabledOnOs;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -62,14 +62,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.condition.OS.WINDOWS;
|
import static org.junit.jupiter.api.condition.OS.WINDOWS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HttpServer Tester.
|
* HttpServer Tester for SSL based ServerConnector
|
||||||
*/
|
*/
|
||||||
public class SelectChannelServerSslTest extends HttpServerTestBase
|
public class ServerConnectorSslServerTest extends HttpServerTestBase
|
||||||
{
|
{
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SelectChannelServerSslTest.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ServerConnectorSslServerTest.class);
|
||||||
private SSLContext _sslContext;
|
private SSLContext _sslContext;
|
||||||
|
|
||||||
public SelectChannelServerSslTest()
|
public ServerConnectorSslServerTest()
|
||||||
{
|
{
|
||||||
_scheme = "https";
|
_scheme = "https";
|
||||||
}
|
}
|
||||||
|
@ -227,16 +227,15 @@ public class SelectChannelServerSslTest extends HttpServerTestBase
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Test
|
@Test
|
||||||
@Disabled("Override and ignore this test as SSLSocket.shutdownOutput() is not supported, " +
|
|
||||||
"but shutdownOutput() is needed by the test.")
|
|
||||||
public void testInterruptedRequest()
|
public void testInterruptedRequest()
|
||||||
{
|
{
|
||||||
|
Assumptions.assumeFalse(_serverURI.getScheme().equals("https"), "SSLSocket.shutdownOutput() is not supported, but shutdownOutput() is needed by the test");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Disabled
|
|
||||||
public void testAvailable() throws Exception
|
public void testAvailable() throws Exception
|
||||||
{
|
{
|
||||||
|
Assumptions.assumeFalse(_serverURI.getScheme().equals("https"), "SSLSocket available() is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
Loading…
Reference in New Issue