Jetty 9.4.x 2233 ssl flush try again 2 (#2726)

Major refactor of SslConnection to address #2233 and to simplify in preparation for java-11 support.

Made the `needFillInterest` and `onIncompleteFlush` methods the primary stateful methods with state for fill and flush side that does not reproduce state already held by the SslEngine itself.

Signed-off-by: Greg Wilkins <gregw@webtide.com>
Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Greg Wilkins 2018-07-18 10:11:35 +02:00 committed by GitHub
parent 9eca404da2
commit 17b6eee5ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 610 additions and 682 deletions

View File

@ -26,6 +26,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.log.Log;
public class EmptyServerHandler extends AbstractHandler.ErrorDispatchHandler public class EmptyServerHandler extends AbstractHandler.ErrorDispatchHandler
{ {
@ -38,5 +39,6 @@ public class EmptyServerHandler extends AbstractHandler.ErrorDispatchHandler
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{ {
Log.getRootLogger().info("EMPTY service {}",target);
} }
} }

View File

@ -510,7 +510,26 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@Test @Test
public void test_ExchangeIsComplete_OnlyWhenBothRequestAndResponseAreComplete() throws Exception public void test_ExchangeIsComplete_OnlyWhenBothRequestAndResponseAreComplete() throws Exception
{ {
start(new RespondThenConsumeHandler()); start(new AbstractHandler.ErrorDispatchHandler()
{
@Override
protected void doNonErrorHandle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
baseRequest.setHandled(true);
response.setContentLength(0);
response.setStatus(200);
response.flushBuffer();
byte[] buffer = new byte[1024];
InputStream in = request.getInputStream();
while(true)
{
int read = in.read(buffer);
if (read < 0)
break;
}
}
});
// Prepare a big file to upload // Prepare a big file to upload
Path targetTestsDir = testdir.getEmptyPathDir(); Path targetTestsDir = testdir.getEmptyPathDir();

View File

@ -1,45 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2018 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.client;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.handler.AbstractHandler;
class RespondThenConsumeHandler extends AbstractHandler
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
baseRequest.setHandled(true);
response.setContentLength(0);
response.setStatus(200);
response.flushBuffer();
InputStream in = request.getInputStream();
while(in.read()>=0);
}
}

View File

@ -43,7 +43,6 @@ import java.util.regex.Pattern;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocket;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream; import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -75,7 +74,6 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Assume; import org.junit.Assume;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
public class SslBytesServerTest extends SslBytesTest public class SslBytesServerTest extends SslBytesTest
@ -99,6 +97,11 @@ public class SslBytesServerTest extends SslBytesTest
threadPool = Executors.newCachedThreadPool(); threadPool = Executors.newCachedThreadPool();
server = new Server(); server = new Server();
sslFills.set(0);
sslFlushes.set(0);
httpParses.set(0);
serverEndPoint.set(null);
File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks"); File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks");
sslContextFactory = new SslContextFactory(); sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath()); sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath());
@ -185,7 +188,7 @@ public class SslBytesServerTest extends SslBytesTest
server.setHandler(new AbstractHandler() server.setHandler(new AbstractHandler()
{ {
@Override @Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException
{ {
try try
{ {
@ -1320,9 +1323,7 @@ public class SslBytesServerTest extends SslBytesTest
closeClient(client); closeClient(client);
} }
// TODO work out why this test frequently fails @Test(timeout=60000)
@Ignore
@Test(timeout=10000)
public void testRequestWithContentWithRenegotiationInMiddleOfContentWhenRenegotiationIsForbidden() throws Exception public void testRequestWithContentWithRenegotiationInMiddleOfContentWhenRenegotiationIsForbidden() throws Exception
{ {
assumeJavaVersionSupportsTLSRenegotiations(); assumeJavaVersionSupportsTLSRenegotiations();
@ -1367,37 +1368,28 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType());
proxy.flushToServer(record); proxy.flushToServer(record);
// Renegotiation now allowed, server has closed // Renegotiation not allowed, server has closed
loop: while(true)
{
record = proxy.readFromServer(); record = proxy.readFromServer();
if (record==null)
break;
switch(record.getType())
{
case APPLICATION:
Assert.fail("application data not allows after renegotiate");
case ALERT:
break loop;
default:
continue;
}
}
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType()); Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
proxy.flushToClient(record); proxy.flushToClient(record);
record = proxy.readFromServer(); record = proxy.readFromServer();
Assert.assertNull(record); Assert.assertNull(record);
// Write the rest of the request
threadPool.submit(() ->
{
clientOutput.write(content2.getBytes(StandardCharsets.UTF_8));
clientOutput.flush();
return null;
});
// Trying to write more application data results in an exception since the server closed
record = proxy.readFromClient();
proxy.flushToServer(record);
try
{
record = proxy.readFromClient();
Assert.assertNotNull(record);
proxy.flushToServer(record);
Assert.fail();
}
catch (IOException expected)
{
// Expected
}
// Check that we did not spin // Check that we did not spin
TimeUnit.MILLISECONDS.sleep(500); TimeUnit.MILLISECONDS.sleep(500);
Assert.assertThat(sslFills.get(), Matchers.lessThan(50)); Assert.assertThat(sslFills.get(), Matchers.lessThan(50));

View File

@ -2,4 +2,5 @@ class=org.eclipse.jetty.util.log.StdErrLog
#org.eclipse.jetty.LEVEL=INFO #org.eclipse.jetty.LEVEL=INFO
#org.eclipse.jetty.client.LEVEL=DEBUG #org.eclipse.jetty.client.LEVEL=DEBUG
#org.eclipse.jetty.io.ChannelEndPoint.LEVEL=DEBUG #org.eclipse.jetty.io.ChannelEndPoint.LEVEL=DEBUG
#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
#org.eclipse.jetty.http.LEVEL=DEBUG #org.eclipse.jetty.http.LEVEL=DEBUG

View File

@ -263,7 +263,7 @@ public abstract class AbstractConnection implements Connection
@Override @Override
public final String toString() public final String toString()
{ {
return String.format("%s<-%s",toConnectionString(),getEndPoint()); return String.format("%s@%h::%s",getClass().getSimpleName(),this,getEndPoint());
} }
public String toConnectionString() public String toConnectionString()

View File

@ -55,6 +55,7 @@ import org.junit.Test;
public class SslConnectionTest public class SslConnectionTest
{ {
private final static int TIMEOUT = 1000000;
private static SslContextFactory __sslCtxFactory=new SslContextFactory(); private static SslContextFactory __sslCtxFactory=new SslContextFactory();
private static ByteBufferPool __byteBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); private static ByteBufferPool __byteBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged());
@ -94,7 +95,7 @@ public class SslConnectionTest
protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey selectionKey) protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey selectionKey)
{ {
SocketChannelEndPoint endp = new TestEP(channel, selector, selectionKey, getScheduler()); SocketChannelEndPoint endp = new TestEP(channel, selector, selectionKey, getScheduler());
endp.setIdleTimeout(60000); endp.setIdleTimeout(TIMEOUT);
_lastEndp=endp; _lastEndp=endp;
return endp; return endp;
} }
@ -257,7 +258,7 @@ public class SslConnectionTest
{ {
try (Socket client = newClient()) try (Socket client = newClient())
{ {
client.setSoTimeout(60000); client.setSoTimeout(TIMEOUT);
try (SocketChannel server = _connector.accept()) try (SocketChannel server = _connector.accept())
{ {
server.configureBlocking(false); server.configureBlocking(false);
@ -283,7 +284,7 @@ public class SslConnectionTest
{ {
try (SSLSocket client = newClient()) try (SSLSocket client = newClient())
{ {
client.setSoTimeout(60000); client.setSoTimeout(TIMEOUT);
try (SocketChannel server = _connector.accept()) try (SocketChannel server = _connector.accept())
{ {
server.configureBlocking(false); server.configureBlocking(false);
@ -312,7 +313,7 @@ public class SslConnectionTest
try (SSLSocket client = newClient()) try (SSLSocket client = newClient())
{ {
client.setSoTimeout(60000); client.setSoTimeout(TIMEOUT);
try (SocketChannel server = _connector.accept()) try (SocketChannel server = _connector.accept())
{ {
server.configureBlocking(false); server.configureBlocking(false);
@ -348,7 +349,7 @@ public class SslConnectionTest
try (SSLSocket client = newClient()) try (SSLSocket client = newClient())
{ {
client.setSoTimeout(60000); client.setSoTimeout(TIMEOUT);
try (SocketChannel server = _connector.accept()) try (SocketChannel server = _connector.accept())
{ {
server.configureBlocking(false); server.configureBlocking(false);
@ -396,18 +397,23 @@ public class SslConnectionTest
_testFill=false; _testFill=false;
_writeCallback = new FutureCallback(); _writeCallback = new FutureCallback();
try (Socket client = newClient()) try (SSLSocket client = newClient())
{ {
client.setSoTimeout(10000); client.setSoTimeout(TIMEOUT);
try (SocketChannel server = _connector.accept()) try (SocketChannel server = _connector.accept())
{ {
server.configureBlocking(false); server.configureBlocking(false);
_manager.accept(server); _manager.accept(server);
// The server side will write something, and in order
// to proceed with the initial TLS handshake we need
// to start reading before waiting for the callback.
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
int len = client.getInputStream().read(buffer); int len = client.getInputStream().read(buffer);
Assert.assertEquals("Hello Client", new String(buffer, 0, len, StandardCharsets.UTF_8)); Assert.assertEquals("Hello Client", new String(buffer, 0, len, StandardCharsets.UTF_8));
Assert.assertNull(_writeCallback.get(100, TimeUnit.MILLISECONDS));
Assert.assertNull(_writeCallback.get(1, TimeUnit.SECONDS));
} }
} }
} }

View File

@ -1170,7 +1170,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
InputStream is = client.getInputStream(); InputStream is = client.getInputStream();
os.write(( os.write((
"POST /echo?charset=utf-8 HTTP/1.1\r\n" + "POST /echo/0?charset=utf-8 HTTP/1.1\r\n" +
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" + "host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
"content-type: text/plain; charset=utf-8\r\n" + "content-type: text/plain; charset=utf-8\r\n" +
"content-length: 10\r\n" + "content-length: 10\r\n" +
@ -1181,7 +1181,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
).getBytes("utf-8")); ).getBytes("utf-8"));
os.write(( os.write((
"POST /echo?charset=utf-8 HTTP/1.1\r\n" + "POST /echo/1?charset=utf-8 HTTP/1.1\r\n" +
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" + "host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
"content-type: text/plain; charset=utf-8\r\n" + "content-type: text/plain; charset=utf-8\r\n" +
"content-length: 10\r\n" + "content-length: 10\r\n" +
@ -1195,7 +1195,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
String content = "Wibble"; String content = "Wibble";
byte[] contentB = content.getBytes(StandardCharsets.UTF_16); byte[] contentB = content.getBytes(StandardCharsets.UTF_16);
os.write(( os.write((
"POST /echo?charset=utf-8 HTTP/1.1\r\n" + "POST /echo/2?charset=utf-8 HTTP/1.1\r\n" +
"host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" + "host: " + _serverURI.getHost() + ":" + _serverURI.getPort() + "\r\n" +
"content-type: text/plain; charset=utf-16\r\n" + "content-type: text/plain; charset=utf-16\r\n" +
"content-length: " + contentB.length + "\r\n" + "content-length: " + contentB.length + "\r\n" +

View File

@ -35,6 +35,7 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.HotSwapHandler; import org.eclipse.jetty.server.handler.HotSwapHandler;
import org.eclipse.jetty.toolchain.test.PropertyFlag; import org.eclipse.jetty.toolchain.test.PropertyFlag;
import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
@ -114,6 +115,7 @@ public class HttpServerTestFixture
@Override @Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{ {
Log.getRootLogger().info("handle "+target);
baseRequest.setHandled(true); baseRequest.setHandled(true);
if (request.getContentType()!=null) if (request.getContentType()!=null)
@ -154,6 +156,8 @@ public class HttpServerTestFixture
if (reader.read()>=0) if (reader.read()>=0)
throw new IllegalStateException("Not closed"); throw new IllegalStateException("Not closed");
Log.getRootLogger().info("handled "+target);
} }
} }

View File

@ -1,5 +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.io.ssl.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