Merge pull request #3312 from eclipse/jetty-9.4.x-3311-http_https_same_port
Fixes #3311 - Ability to serve HTTP and HTTPS from the same port.
This commit is contained in:
commit
bf6521da5f
|
@ -427,15 +427,16 @@ public abstract class AbstractEndPoint extends IdleTimeout implements EndPoint
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{} upgrading from {} to {}", this, old_connection, newConnection);
|
LOG.debug("{} upgrading from {} to {}", this, old_connection, newConnection);
|
||||||
|
|
||||||
ByteBuffer prefilled = (old_connection instanceof Connection.UpgradeFrom)
|
ByteBuffer buffer = (old_connection instanceof Connection.UpgradeFrom) ?
|
||||||
?((Connection.UpgradeFrom)old_connection).onUpgradeFrom():null;
|
((Connection.UpgradeFrom)old_connection).onUpgradeFrom() :
|
||||||
|
null;
|
||||||
old_connection.onClose();
|
old_connection.onClose();
|
||||||
old_connection.getEndPoint().setConnection(newConnection);
|
old_connection.getEndPoint().setConnection(newConnection);
|
||||||
|
|
||||||
if (newConnection instanceof Connection.UpgradeTo)
|
if (newConnection instanceof Connection.UpgradeTo)
|
||||||
((Connection.UpgradeTo)newConnection).onUpgradeTo(prefilled);
|
((Connection.UpgradeTo)newConnection).onUpgradeTo(buffer);
|
||||||
else if (BufferUtil.hasContent(prefilled))
|
else if (BufferUtil.hasContent(buffer))
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException("Cannot upgrade: " + newConnection + " does not implement " + Connection.UpgradeTo.class.getName());
|
||||||
|
|
||||||
newConnection.onOpen();
|
newConnection.onOpen();
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,7 @@ import org.eclipse.jetty.util.thread.Invocable;
|
||||||
* be called again and make another best effort attempt to progress the connection.
|
* be called again and make another best effort attempt to progress the connection.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class SslConnection extends AbstractConnection
|
public class SslConnection extends AbstractConnection implements Connection.UpgradeTo
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(SslConnection.class);
|
private static final Logger LOG = Log.getLogger(SslConnection.class);
|
||||||
private static final String TLS_1_3 = "TLSv1.3";
|
private static final String TLS_1_3 = "TLSv1.3";
|
||||||
|
@ -260,6 +260,22 @@ public class SslConnection extends AbstractConnection
|
||||||
this._allowMissingCloseMessage = allowMissingCloseMessage;
|
this._allowMissingCloseMessage = allowMissingCloseMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void acquireEncryptedInput()
|
||||||
|
{
|
||||||
|
if (_encryptedInput == null)
|
||||||
|
_encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgradeTo(ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
if (BufferUtil.hasContent(buffer))
|
||||||
|
{
|
||||||
|
acquireEncryptedInput();
|
||||||
|
BufferUtil.append(_encryptedInput, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpen()
|
public void onOpen()
|
||||||
{
|
{
|
||||||
|
@ -526,8 +542,7 @@ public class SslConnection extends AbstractConnection
|
||||||
throw new IllegalStateException("Unexpected HandshakeStatus " + status);
|
throw new IllegalStateException("Unexpected HandshakeStatus " + status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_encryptedInput == null)
|
acquireEncryptedInput();
|
||||||
_encryptedInput = _bufferPool.acquire(_sslEngine.getSession().getPacketBufferSize(), _encryptedDirectBuffers);
|
|
||||||
|
|
||||||
// can we use the passed buffer if it is big enough
|
// can we use the passed buffer if it is big enough
|
||||||
ByteBuffer app_in;
|
ByteBuffer app_in;
|
||||||
|
|
|
@ -50,7 +50,7 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
/**
|
/**
|
||||||
* <p>A {@link Connection} that handles the HTTP protocol.</p>
|
* <p>A {@link Connection} that handles the HTTP protocol.</p>
|
||||||
*/
|
*/
|
||||||
public class HttpConnection extends AbstractConnection implements Runnable, HttpTransport, Connection.UpgradeFrom, WriteFlusher.Listener
|
public class HttpConnection extends AbstractConnection implements Runnable, HttpTransport, WriteFlusher.Listener, Connection.UpgradeFrom, Connection.UpgradeTo
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(HttpConnection.class);
|
private static final Logger LOG = Log.getLogger(HttpConnection.class);
|
||||||
public static final HttpField CONNECTION_CLOSE = new PreEncodedHttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE.asString());
|
public static final HttpField CONNECTION_CLOSE = new PreEncodedHttpField(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE.asString());
|
||||||
|
@ -196,6 +196,13 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgradeTo(ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
if (BufferUtil.hasContent(buffer))
|
||||||
|
BufferUtil.append(getRequestBuffer(), buffer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFlushed(long bytes) throws IOException
|
public void onFlushed(long bytes) throws IOException
|
||||||
{
|
{
|
||||||
|
@ -500,7 +507,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||||
public void onOpen()
|
public void onOpen()
|
||||||
{
|
{
|
||||||
super.onOpen();
|
super.onOpen();
|
||||||
|
if (isRequestBufferEmpty())
|
||||||
fillInterested();
|
fillInterested();
|
||||||
|
else
|
||||||
|
getExecutor().execute(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,226 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
|
import org.eclipse.jetty.io.Connection;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A ConnectionFactory whose connections detect whether the first bytes are
|
||||||
|
* TLS bytes and upgrades to either a TLS connection or to another configurable
|
||||||
|
* connection.</p>
|
||||||
|
*/
|
||||||
|
public class OptionalSslConnectionFactory extends AbstractConnectionFactory
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(OptionalSslConnection.class);
|
||||||
|
private static final int TLS_ALERT_FRAME_TYPE = 0x15;
|
||||||
|
private static final int TLS_HANDSHAKE_FRAME_TYPE = 0x16;
|
||||||
|
private static final int TLS_MAJOR_VERSION = 3;
|
||||||
|
|
||||||
|
private final SslConnectionFactory sslConnectionFactory;
|
||||||
|
private final String otherProtocol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Creates a new ConnectionFactory whose connections can upgrade to TLS or another protocol.</p>
|
||||||
|
* <p>If {@code otherProtocol} is {@code null}, and the first bytes are not TLS, then
|
||||||
|
* {@link #otherProtocol(ByteBuffer, EndPoint)} is called.</p>
|
||||||
|
*
|
||||||
|
* @param sslConnectionFactory The SslConnectionFactory to use if the first bytes are TLS
|
||||||
|
* @param otherProtocol the protocol of the ConnectionFactory to use if the first bytes are not TLS,
|
||||||
|
* or null to explicitly handle the non-TLS case
|
||||||
|
*/
|
||||||
|
public OptionalSslConnectionFactory(SslConnectionFactory sslConnectionFactory, String otherProtocol)
|
||||||
|
{
|
||||||
|
super("ssl|other");
|
||||||
|
this.sslConnectionFactory = sslConnectionFactory;
|
||||||
|
this.otherProtocol = otherProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection newConnection(Connector connector, EndPoint endPoint)
|
||||||
|
{
|
||||||
|
return configure(new OptionalSslConnection(endPoint, connector), connector, endPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param buffer The buffer with the first bytes of the connection
|
||||||
|
* @return whether the bytes seem TLS bytes
|
||||||
|
*/
|
||||||
|
protected boolean seemsTLS(ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
int tlsFrameType = buffer.get(0) & 0xFF;
|
||||||
|
int tlsMajorVersion = buffer.get(1) & 0xFF;
|
||||||
|
return (tlsFrameType == TLS_HANDSHAKE_FRAME_TYPE || tlsFrameType == TLS_ALERT_FRAME_TYPE) && tlsMajorVersion == TLS_MAJOR_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Callback method invoked when {@code otherProtocol} is {@code null}
|
||||||
|
* and the first bytes are not TLS.</p>
|
||||||
|
* <p>This typically happens when a client is trying to connect to a TLS
|
||||||
|
* port using the {@code http} scheme (and not the {@code https} scheme).</p>
|
||||||
|
*
|
||||||
|
* @param buffer The buffer with the first bytes of the connection
|
||||||
|
* @param endPoint The connection EndPoint object
|
||||||
|
* @see #seemsTLS(ByteBuffer)
|
||||||
|
*/
|
||||||
|
protected void otherProtocol(ByteBuffer buffer, EndPoint endPoint)
|
||||||
|
{
|
||||||
|
// There are always at least 2 bytes.
|
||||||
|
int byte1 = buffer.get(0) & 0xFF;
|
||||||
|
int byte2 = buffer.get(1) & 0xFF;
|
||||||
|
if (byte1 == 'G' && byte2 == 'E')
|
||||||
|
{
|
||||||
|
// Plain text HTTP to a HTTPS port,
|
||||||
|
// write a minimal response.
|
||||||
|
String body = "" +
|
||||||
|
"<!DOCTYPE html>\r\n" +
|
||||||
|
"<html>\r\n" +
|
||||||
|
"<head><title>Bad Request</title></head>\r\n" +
|
||||||
|
"<body>" +
|
||||||
|
"<h1>Bad Request</h1>" +
|
||||||
|
"<p>HTTP request to HTTPS port</p>" +
|
||||||
|
"</body>\r\n" +
|
||||||
|
"</html>";
|
||||||
|
String response = "" +
|
||||||
|
"HTTP/1.1 400 Bad Request\r\n" +
|
||||||
|
"Content-Type: text/html\r\n" +
|
||||||
|
"Content-Length: " + body.length() + "\r\n" +
|
||||||
|
"Connection: close\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
body;
|
||||||
|
Callback.Completable completable = new Callback.Completable();
|
||||||
|
endPoint.write(completable, ByteBuffer.wrap(response.getBytes(StandardCharsets.US_ASCII)));
|
||||||
|
completable.whenComplete((r, x) -> endPoint.close());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
endPoint.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OptionalSslConnection extends AbstractConnection implements Connection.UpgradeFrom
|
||||||
|
{
|
||||||
|
private final Connector connector;
|
||||||
|
private final ByteBuffer buffer;
|
||||||
|
|
||||||
|
public OptionalSslConnection(EndPoint endPoint, Connector connector)
|
||||||
|
{
|
||||||
|
super(endPoint, connector.getExecutor());
|
||||||
|
this.connector = connector;
|
||||||
|
this.buffer = BufferUtil.allocateDirect(1536);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen()
|
||||||
|
{
|
||||||
|
super.onOpen();
|
||||||
|
fillInterested();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFillable()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int filled = getEndPoint().fill(buffer);
|
||||||
|
if (filled > 0)
|
||||||
|
{
|
||||||
|
// Always have at least 2 bytes.
|
||||||
|
if (BufferUtil.length(buffer) >= 2)
|
||||||
|
{
|
||||||
|
upgrade(buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (filled == 0)
|
||||||
|
{
|
||||||
|
fillInterested();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
LOG.warn(x);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuffer onUpgradeFrom()
|
||||||
|
{
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upgrade(ByteBuffer buffer)
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Read {}", BufferUtil.toDetailString(buffer));
|
||||||
|
|
||||||
|
EndPoint endPoint = getEndPoint();
|
||||||
|
if (seemsTLS(buffer))
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Detected TLS bytes, upgrading to {}", sslConnectionFactory);
|
||||||
|
endPoint.upgrade(sslConnectionFactory.newConnection(connector, endPoint));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (otherProtocol != null)
|
||||||
|
{
|
||||||
|
ConnectionFactory connectionFactory = connector.getConnectionFactory(otherProtocol);
|
||||||
|
if (connectionFactory != null)
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Detected non-TLS bytes, upgrading to {}", connectionFactory);
|
||||||
|
Connection next = connectionFactory.newConnection(connector, endPoint);
|
||||||
|
endPoint.upgrade(next);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.warn("Missing {} {} in {}", otherProtocol, ConnectionFactory.class.getSimpleName(), connector);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Detected non-TLS bytes, but no other protocol to upgrade to");
|
||||||
|
otherProtocol(buffer, endPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
|
import org.eclipse.jetty.http.HttpTester;
|
||||||
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
public class OptionalSslConnectionTest
|
||||||
|
{
|
||||||
|
private Server server;
|
||||||
|
private ServerConnector connector;
|
||||||
|
|
||||||
|
private void startServer(Function<SslConnectionFactory, OptionalSslConnectionFactory> configFn, Handler handler) throws Exception
|
||||||
|
{
|
||||||
|
QueuedThreadPool serverThreads = new QueuedThreadPool();
|
||||||
|
serverThreads.setName("server");
|
||||||
|
server = new Server(serverThreads);
|
||||||
|
|
||||||
|
String keystore = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
|
||||||
|
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||||
|
sslContextFactory.setKeyStorePath(keystore);
|
||||||
|
sslContextFactory.setKeyStorePassword("storepwd");
|
||||||
|
sslContextFactory.setKeyManagerPassword("keypwd");
|
||||||
|
|
||||||
|
HttpConfiguration httpConfig = new HttpConfiguration();
|
||||||
|
HttpConnectionFactory http = new HttpConnectionFactory(httpConfig);
|
||||||
|
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, http.getProtocol());
|
||||||
|
OptionalSslConnectionFactory sslOrOther = configFn.apply(ssl);
|
||||||
|
connector = new ServerConnector(server, 1, 1, sslOrOther, ssl, http);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
server.setHandler(handler);
|
||||||
|
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void stopServer() throws Exception
|
||||||
|
{
|
||||||
|
if (server != null)
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private OptionalSslConnectionFactory optionalSsl(SslConnectionFactory ssl)
|
||||||
|
{
|
||||||
|
return new OptionalSslConnectionFactory(ssl, ssl.getNextProtocol());
|
||||||
|
}
|
||||||
|
|
||||||
|
private OptionalSslConnectionFactory optionalSslNoOtherProtocol(SslConnectionFactory ssl)
|
||||||
|
{
|
||||||
|
return new OptionalSslConnectionFactory(ssl, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptionalSslConnection() throws Exception
|
||||||
|
{
|
||||||
|
startServer(this::optionalSsl, new EmptyServerHandler());
|
||||||
|
|
||||||
|
String request = "" +
|
||||||
|
"GET / HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
byte[] requestBytes = request.getBytes(StandardCharsets.US_ASCII);
|
||||||
|
|
||||||
|
// Try first a plain text connection.
|
||||||
|
try (Socket plain = new Socket())
|
||||||
|
{
|
||||||
|
plain.connect(new InetSocketAddress("localhost", connector.getLocalPort()), 1000);
|
||||||
|
OutputStream plainOutput = plain.getOutputStream();
|
||||||
|
plainOutput.write(requestBytes);
|
||||||
|
plainOutput.flush();
|
||||||
|
|
||||||
|
plain.setSoTimeout(5000);
|
||||||
|
InputStream plainInput = plain.getInputStream();
|
||||||
|
HttpTester.Response response = HttpTester.parseResponse(plainInput);
|
||||||
|
assertNotNull(response);
|
||||||
|
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then try a SSL connection.
|
||||||
|
SslContextFactory sslContextFactory = new SslContextFactory(true);
|
||||||
|
sslContextFactory.start();
|
||||||
|
try (Socket ssl = sslContextFactory.newSslSocket())
|
||||||
|
{
|
||||||
|
ssl.connect(new InetSocketAddress("localhost", connector.getLocalPort()), 1000);
|
||||||
|
OutputStream sslOutput = ssl.getOutputStream();
|
||||||
|
sslOutput.write(requestBytes);
|
||||||
|
sslOutput.flush();
|
||||||
|
|
||||||
|
ssl.setSoTimeout(5000);
|
||||||
|
InputStream sslInput = ssl.getInputStream();
|
||||||
|
HttpTester.Response response = HttpTester.parseResponse(sslInput);
|
||||||
|
assertNotNull(response);
|
||||||
|
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
sslContextFactory.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptionalSslConnectionWithOnlyOneByteShouldIdleTimeout() throws Exception
|
||||||
|
{
|
||||||
|
startServer(this::optionalSsl, new EmptyServerHandler());
|
||||||
|
long idleTimeout = 1000;
|
||||||
|
connector.setIdleTimeout(idleTimeout);
|
||||||
|
|
||||||
|
try (Socket socket = new Socket())
|
||||||
|
{
|
||||||
|
socket.connect(new InetSocketAddress("localhost", connector.getLocalPort()), 1000);
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(0x16);
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
socket.setSoTimeout((int)(2 * idleTimeout));
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
int read = input.read();
|
||||||
|
assertEquals(-1, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptionalSslConnectionWithUnknownBytes() throws Exception
|
||||||
|
{
|
||||||
|
startServer(this::optionalSslNoOtherProtocol, new EmptyServerHandler());
|
||||||
|
|
||||||
|
try (Socket socket = new Socket())
|
||||||
|
{
|
||||||
|
socket.connect(new InetSocketAddress("localhost", connector.getLocalPort()), 1000);
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(0x00);
|
||||||
|
output.flush();
|
||||||
|
Thread.sleep(500);
|
||||||
|
output.write(0x00);
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
socket.setSoTimeout(5000);
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
int read = input.read();
|
||||||
|
assertEquals(-1, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOptionalSslConnectionWithHTTPBytes() throws Exception
|
||||||
|
{
|
||||||
|
startServer(this::optionalSslNoOtherProtocol, new EmptyServerHandler());
|
||||||
|
|
||||||
|
String request = "" +
|
||||||
|
"GET / HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
byte[] requestBytes = request.getBytes(StandardCharsets.US_ASCII);
|
||||||
|
|
||||||
|
// Send a plain text HTTP request to SSL port,
|
||||||
|
// we should get back a minimal HTTP response.
|
||||||
|
try (Socket socket = new Socket())
|
||||||
|
{
|
||||||
|
socket.connect(new InetSocketAddress("localhost", connector.getLocalPort()), 1000);
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(requestBytes);
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
socket.setSoTimeout(5000);
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
HttpTester.Response response = HttpTester.parseResponse(input);
|
||||||
|
assertNotNull(response);
|
||||||
|
assertEquals(HttpStatus.BAD_REQUEST_400, response.getStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EmptyServerHandler extends AbstractHandler.ErrorDispatchHandler
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected void doNonErrorHandle(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
|
{
|
||||||
|
jettyRequest.setHandled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||||
#org.eclipse.jetty.LEVEL=DEBUG
|
#org.eclipse.jetty.LEVEL=DEBUG
|
||||||
|
#org.eclipse.jetty.server.LEVEL=DEBUG
|
||||||
#org.eclipse.jetty.server.ConnectionLimit.LEVEL=DEBUG
|
#org.eclipse.jetty.server.ConnectionLimit.LEVEL=DEBUG
|
||||||
#org.eclipse.jetty.server.AcceptRateLimit.LEVEL=DEBUG
|
#org.eclipse.jetty.server.AcceptRateLimit.LEVEL=DEBUG
|
Loading…
Reference in New Issue