Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.

This commit is contained in:
Simone Bordet 2019-09-30 09:01:57 +02:00
commit 1cb09db75a
7 changed files with 112 additions and 54 deletions

View File

@ -27,6 +27,7 @@ import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.Server;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
@ -53,6 +54,7 @@ public class ProxyServerTest extends AbstractEmbeddedTest
server.stop();
}
@Tag("external")
@Test
public void testGetProxiedRFC() throws Exception
{

View File

@ -410,6 +410,9 @@ public abstract class HttpReceiver
if (exchange == null)
return false;
if (LOG.isDebugEnabled())
LOG.debug("Response failure " + exchange.getResponse(), failure);
// Mark atomically the response as completed, with respect
// to concurrency between response success and response failure.
if (exchange.responseComplete(failure))
@ -514,7 +517,7 @@ public abstract class HttpReceiver
HttpResponse response = exchange.getResponse();
if (LOG.isDebugEnabled())
LOG.debug("Response failure {} {} on {}: {}", response, exchange, getHttpChannel(), failure);
LOG.debug("Response abort {} {} on {}: {}", response, exchange, getHttpChannel(), failure);
List<Response.ResponseListener> listeners = exchange.getConversation().getResponseListeners();
ResponseNotifier notifier = getHttpDestination().getResponseNotifier();
notifier.notifyFailure(listeners, response, failure);

View File

@ -344,6 +344,9 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
if (exchange == null)
return;
if (LOG.isDebugEnabled())
LOG.debug("Request failure " + exchange.getRequest(), failure);
// Mark atomically the request as completed, with respect
// to concurrency between request success and request failure.
if (exchange.requestComplete(failure))
@ -559,7 +562,7 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
Request request = exchange.getRequest();
if (LOG.isDebugEnabled())
LOG.debug("Request failure {} {} on {}: {}", request, exchange, getHttpChannel(), failure);
LOG.debug("Request abort {} {} on {}: {}", request, exchange, getHttpChannel(), failure);
HttpDestination destination = getHttpChannel().getHttpDestination();
destination.getRequestNotifier().notifyFailure(request, failure);

View File

@ -288,7 +288,6 @@ public class HttpSenderOverHTTP extends HttpSender
public void failed(Throwable x)
{
release();
callback.failed(x);
super.failed(x);
}
@ -299,6 +298,13 @@ public class HttpSenderOverHTTP extends HttpSender
callback.succeeded();
}
@Override
protected void onCompleteFailure(Throwable cause)
{
super.onCompleteFailure(cause);
callback.failed(cause);
}
private void release()
{
ByteBufferPool bufferPool = httpClient.getByteBufferPool();

View File

@ -37,7 +37,6 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers;
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.hamcrest.MatcherAssert.assertThat;
@ -48,7 +47,6 @@ import static org.junit.jupiter.api.Assertions.fail;
* This test class runs tests to make sure that hostname verification (http://www.ietf.org/rfc/rfc2818.txt
* section 3.1) is configurable in SslContextFactory and works as expected.
*/
@Disabled
public class HostnameVerificationTest
{
private SslContextFactory.Client clientSslContextFactory = new SslContextFactory.Client();
@ -100,18 +98,15 @@ public class HostnameVerificationTest
{
client.stop();
server.stop();
server.join();
}
/**
* This test is supposed to verify that hostname verification works as described in:
* http://www.ietf.org/rfc/rfc2818.txt section 3.1. It uses a certificate with a common name different to localhost
* and sends a request to localhost. This should fail with an SSLHandshakeException.
*
* @throws Exception on test failure
*/
@Test
public void simpleGetWithHostnameVerificationEnabledTest() throws Exception
public void simpleGetWithHostnameVerificationEnabledTest()
{
clientSslContextFactory.setEndpointIdentificationAlgorithm("HTTPS");
String uri = "https://localhost:" + connector.getLocalPort() + "/";
@ -119,8 +114,16 @@ public class HostnameVerificationTest
ExecutionException x = assertThrows(ExecutionException.class, () -> client.GET(uri));
Throwable cause = x.getCause();
assertThat(cause, Matchers.instanceOf(SSLHandshakeException.class));
Throwable root = cause.getCause().getCause();
assertThat(root, Matchers.instanceOf(CertificateException.class));
// Search for the CertificateException.
Throwable certificateException = cause.getCause();
while (certificateException != null)
{
if (certificateException instanceof CertificateException)
break;
certificateException = certificateException.getCause();
}
assertThat(certificateException, Matchers.instanceOf(CertificateException.class));
}
/**

View File

@ -146,7 +146,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
@Override
public InvocationType getInvocationType()
{
return getDecryptedEndPoint().getFillInterest().getCallbackInvocationType();
return _decryptedEndPoint.getFillInterest().getCallbackInvocationType();
}
};
@ -364,6 +364,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
public class DecryptedEndPoint extends AbstractEndPoint
{
private final Callback _incompleteWriteCallback = new IncompleteWriteCallback();
private Throwable _failure;
public DecryptedEndPoint()
{
@ -451,14 +452,10 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
LOG.debug("onFillableFail {}", SslConnection.this, failure);
_fillState = FillState.IDLE;
switch (_flushState)
if (_flushState == FlushState.WAIT_FOR_FILL)
{
case WAIT_FOR_FILL:
_flushState = FlushState.IDLE;
fail = true;
break;
default:
break;
_flushState = FlushState.IDLE;
fail = true;
}
}
@ -529,12 +526,14 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
case NEED_WRAP:
if (_flushState == FlushState.IDLE && flush(BufferUtil.EMPTY_BUFFER))
{
Throwable failure = _failure;
if (failure != null)
rethrow(failure);
if (_sslEngine.isInboundDone())
// TODO this is probably a JVM bug, work around it by -1
return -1;
return filled = -1;
continue;
}
// handle in needsFillInterest
// Handle in needsFillInterest().
return filled = 0;
default:
@ -598,6 +597,9 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
switch (unwrap)
{
case CLOSED:
Throwable failure = _failure;
if (failure != null)
rethrow(failure);
return filled = -1;
case BUFFER_UNDERFLOW:
@ -606,7 +608,14 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
_underflown = true;
if (netFilled < 0 && _sslEngine.getUseClientMode())
{
closeInbound();
Throwable closeFailure = closeInbound();
if (_flushState == FlushState.WAIT_FOR_FILL)
{
Throwable handshakeFailure = new SSLHandshakeException("Abruptly closed by peer");
if (closeFailure != null)
handshakeFailure.initCause(closeFailure);
throw handshakeFailure;
}
return filled = -1;
}
return filled = netFilled;
@ -639,15 +648,14 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
}
catch (Throwable x)
{
handshakeFailed(x);
Throwable failure = handleException(x, "fill");
handshakeFailed(failure);
if (_flushState == FlushState.WAIT_FOR_FILL)
{
_flushState = FlushState.IDLE;
getExecutor().execute(() -> _decryptedEndPoint.getWriteFlusher().onFail(x));
getExecutor().execute(() -> _decryptedEndPoint.getWriteFlusher().onFail(failure));
}
throw x;
throw failure;
}
finally
{
@ -676,10 +684,10 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug(SslConnection.this.toString(), x);
close(x);
throw x;
rethrow(x);
// Never reached.
throw new AssertionError();
}
}
@ -694,15 +702,18 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
synchronized (_decryptedEndPoint)
{
if (LOG.isDebugEnabled())
{
LOG.debug(">needFillInterest uf={} {}", _underflown, SslConnection.this);
LOG.debug("ei={} di={}", BufferUtil.toDetailString(_encryptedInput), BufferUtil.toDetailString(_decryptedInput));
}
LOG.debug(">needFillInterest s={}/{} uf={} ei={} di={} {}",
_flushState,
_fillState,
_underflown,
BufferUtil.toDetailString(_encryptedInput),
BufferUtil.toDetailString(_decryptedInput),
SslConnection.this);
if (_fillState != FillState.IDLE)
return;
// Fillable if we have decrypted Input OR encrypted input that has not yet been underflown.
// Fillable if we have decrypted input OR enough encrypted input.
fillable = BufferUtil.hasContent(_decryptedInput) || (BufferUtil.hasContent(_encryptedInput) && !_underflown);
HandshakeStatus status = _sslEngine.getHandshakeStatus();
@ -799,23 +810,25 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
}
}
private void closeInbound() throws SSLException
private Throwable closeInbound() throws SSLException
{
HandshakeStatus handshakeStatus = _sslEngine.getHandshakeStatus();
try
{
_sslEngine.closeInbound();
return null;
}
catch (SSLException x)
{
if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING && !isAllowMissingCloseMessage())
throw x;
else
LOG.ignore(x);
LOG.ignore(x);
return x;
}
catch (Throwable x)
{
LOG.ignore(x);
return x;
}
}
@ -966,8 +979,9 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
}
catch (Throwable x)
{
handshakeFailed(x);
throw x;
Throwable failure = handleException(x, "flush");
handshakeFailed(failure);
throw failure;
}
finally
{
@ -979,10 +993,10 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug(SslConnection.this.toString(), x);
close(x);
throw x;
rethrow(x);
// Never reached.
throw new AssertionError();
}
}
@ -1092,7 +1106,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
boolean ishut = endp.isInputShutdown();
boolean oshut = endp.isOutputShutdown();
if (LOG.isDebugEnabled())
LOG.debug("shutdownOutput: {} oshut={}, ishut={} {}", SslConnection.this, oshut, ishut);
LOG.debug("shutdownOutput: {} oshut={}, ishut={}", SslConnection.this, oshut, ishut);
closeOutbound();
@ -1110,15 +1124,11 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
{
if (!flush(BufferUtil.EMPTY_BUFFER) && !close)
{
Thread.yield();
// if we still can't flush, but we are not closing the endpoint,
// If we still can't flush, but we are not closing the endpoint,
// let's just flush the encrypted output in the background.
// and continue as if we are closed. The assumption here is that
// the encrypted buffer will contain the entire close handshake
// and that a call to flush(EMPTY_BUFFER) is not needed.
endp.write(Callback.from(() ->
{
}, t -> endp.close()), _encryptedOutput);
ByteBuffer write = _encryptedOutput;
if (BufferUtil.hasContent(write))
endp.write(Callback.from(Callback.NOOP::succeeded, t -> endp.close()), write);
}
}
@ -1284,6 +1294,37 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
return TLS_1_3.equals(protocol);
}
private Throwable handleException(Throwable x, String context)
{
synchronized (_decryptedEndPoint)
{
if (_failure == null)
{
_failure = x;
if (LOG.isDebugEnabled())
LOG.debug(this + " stored " + context + " exception", x);
}
else if (x != _failure)
{
_failure.addSuppressed(x);
if (LOG.isDebugEnabled())
LOG.debug(this + " suppressed " + context + " exception", x);
}
return _failure;
}
}
private void rethrow(Throwable x) throws IOException
{
if (x instanceof RuntimeException)
throw (RuntimeException)x;
if (x instanceof Error)
throw (Error)x;
if (x instanceof IOException)
throw (IOException)x;
throw new IOException(x);
}
@Override
public String toString()
{

View File

@ -1,5 +1,5 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.LEVEL=INFO
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.io.AbstractConnection.LEVEL=DEBUG
#org.eclipse.jetty.io.ManagedSelector.LEVEL=DEBUG
#org.eclipse.jetty.io.ssl.SslConnection.LEVEL=DEBUG