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:
parent
9eca404da2
commit
17b6eee5ac
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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" +
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue