Testing Large TLS Records for Jetty 9.2.x
Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This commit is contained in:
parent
45a5b9c8b9
commit
c58fd58e41
|
@ -18,19 +18,45 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
|
||||
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.ClientConnectionFactory;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
|
||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class HttpClientTLSTest
|
||||
{
|
||||
private Server server;
|
||||
|
@ -180,4 +206,133 @@ public class HttpClientTLSTest
|
|||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTLSLargeFragments() throws Exception
|
||||
{
|
||||
final CountDownLatch serverLatch = new CountDownLatch(1);
|
||||
SslContextFactory serverTLSFactory = createSslContextFactory();
|
||||
QueuedThreadPool serverThreads = new QueuedThreadPool();
|
||||
serverThreads.setName("server");
|
||||
server = new Server(serverThreads);
|
||||
HttpConfiguration httpConfig = new HttpConfiguration();
|
||||
httpConfig.addCustomizer(new SecureRequestCustomizer());
|
||||
HttpConnectionFactory http = new HttpConnectionFactory(httpConfig);
|
||||
SslConnectionFactory ssl = new SslConnectionFactory(serverTLSFactory, http.getProtocol())
|
||||
{
|
||||
@Override
|
||||
protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine)
|
||||
{
|
||||
return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption())
|
||||
{
|
||||
@Override
|
||||
protected SSLEngineResult unwrap(SSLEngine sslEngine, ByteBuffer input, ByteBuffer output) throws SSLException
|
||||
{
|
||||
int inputBytes = input.remaining();
|
||||
SSLEngineResult result = super.unwrap(sslEngine, input, output);
|
||||
if (inputBytes == 5)
|
||||
serverLatch.countDown();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
connector = new ServerConnector(server, 1, 1, ssl, http);
|
||||
server.addConnector(connector);
|
||||
server.setHandler(new EmptyServerHandler());
|
||||
server.start();
|
||||
|
||||
long idleTimeout = 2000;
|
||||
|
||||
final CountDownLatch clientLatch = new CountDownLatch(1);
|
||||
final SslContextFactory clientTLSFactory = createSslContextFactory();
|
||||
QueuedThreadPool clientThreads = new QueuedThreadPool();
|
||||
clientThreads.setName("client");
|
||||
client = new HttpClient(
|
||||
new HttpClientTransportOverHTTP()
|
||||
{
|
||||
@Override
|
||||
public HttpDestination newHttpDestination(Origin origin)
|
||||
{
|
||||
return new HttpDestinationOverHTTP(getHttpClient(), origin)
|
||||
{
|
||||
@Override
|
||||
protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
|
||||
{
|
||||
SslContextFactory sslContextFactory = getHttpClient().getSslContextFactory();
|
||||
ByteBufferPool bufferPool = getHttpClient().getByteBufferPool();
|
||||
Executor executor = getHttpClient().getExecutor();
|
||||
return new SslClientConnectionFactory(sslContextFactory, bufferPool, executor, connectionFactory)
|
||||
{
|
||||
@Override
|
||||
protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine)
|
||||
{
|
||||
return new SslConnection(byteBufferPool, executor, endPoint, engine, isDirectBuffersForEncryption(), isDirectBuffersForDecryption())
|
||||
{
|
||||
@Override
|
||||
protected SSLEngineResult wrap(SSLEngine sslEngine, ByteBuffer[] input, ByteBuffer output) throws SSLException
|
||||
{
|
||||
try
|
||||
{
|
||||
clientLatch.countDown();
|
||||
assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
|
||||
return super.wrap(sslEngine, input, output);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new SSLException(x);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}, clientTLSFactory);
|
||||
client.setIdleTimeout(idleTimeout);
|
||||
client.setExecutor(clientThreads);
|
||||
client.start();
|
||||
|
||||
String host = "localhost";
|
||||
int port = connector.getLocalPort();
|
||||
|
||||
final CountDownLatch responseLatch = new CountDownLatch(1);
|
||||
client.newRequest(host, port)
|
||||
.scheme(HttpScheme.HTTPS.asString())
|
||||
.send(new Response.CompleteListener()
|
||||
{
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
assertTrue(result.isSucceeded());
|
||||
assertEquals(HttpStatus.OK_200, result.getResponse().getStatus());
|
||||
responseLatch.countDown();
|
||||
}
|
||||
}
|
||||
);
|
||||
// Wait for the TLS buffers to be acquired by the client, then the
|
||||
// HTTP request will be paused waiting for the TLS buffer to be expanded.
|
||||
assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Send the large frame bytes that will enlarge the TLS buffers.
|
||||
try (Socket socket = new Socket(host, port))
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
byte[] largeFrameBytes = new byte[5];
|
||||
largeFrameBytes[0] = 22; // Type = handshake
|
||||
largeFrameBytes[1] = 3; // Major TLS version
|
||||
largeFrameBytes[2] = 3; // Minor TLS version
|
||||
// Frame length is 0x7FFF == 32767, i.e. a "large fragment".
|
||||
// Maximum allowed by RFC 8446 is 16384, but SSLEngine supports up to 33093.
|
||||
largeFrameBytes[3] = 0x7F; // Length hi byte
|
||||
largeFrameBytes[4] = (byte)0xFF; // Length lo byte
|
||||
output.write(largeFrameBytes);
|
||||
output.flush();
|
||||
// Just close the connection now, the large frame
|
||||
// length was enough to trigger the buffer expansion.
|
||||
}
|
||||
|
||||
// The HTTP request will resume and be forced to handle the TLS buffer expansion.
|
||||
assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.nio.ByteBuffer;
|
|||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
|
@ -230,6 +229,16 @@ public class SslConnection extends AbstractConnection
|
|||
_decryptedEndPoint.getWriteFlusher().onFail(cause);
|
||||
}
|
||||
|
||||
protected SSLEngineResult wrap(SSLEngine sslEngine, ByteBuffer[] input, ByteBuffer output) throws SSLException
|
||||
{
|
||||
return sslEngine.wrap(input, output);
|
||||
}
|
||||
|
||||
protected SSLEngineResult unwrap(SSLEngine sslEngine, ByteBuffer input, ByteBuffer output) throws SSLException
|
||||
{
|
||||
return sslEngine.unwrap(input, output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
@ -525,7 +534,7 @@ public class SslConnection extends AbstractConnection
|
|||
SSLEngineResult unwrapResult;
|
||||
try
|
||||
{
|
||||
unwrapResult = _sslEngine.unwrap(_encryptedInput, app_in);
|
||||
unwrapResult = unwrap(_sslEngine, _encryptedInput, app_in);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -757,7 +766,7 @@ public class SslConnection extends AbstractConnection
|
|||
SSLEngineResult wrapResult;
|
||||
try
|
||||
{
|
||||
wrapResult=_sslEngine.wrap(appOuts, _encryptedOutput);
|
||||
wrapResult = wrap(_sslEngine, appOuts, _encryptedOutput);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue