Skip to content
Commits on Source (5)
Apache HttpComponents Core
Copyright 2005-2019 The Apache Software Foundation
Copyright 2005-2020 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
Release 4.4.13
-------------------
This is a maintenance release that corrects a number of defects discovered since release 4.4.12.
Changelog
-------------------
* HTTPCORE-612: DefaultConnectionReuseStrategy incorrectly used int to represent Content-Length value
instead of long.
Contributed by Oleg Kalnichevski <olegk at apache.org>
* HTTPASYNC-152: Non-blocking connection pools to automatically close all newly created sessions when
being shut down.
Contributed by Oleg Kalnichevski <olegk at apache.org>
* HTTPASYNC-152: Better session state representation in SessionRequestImpl.
Contributed by Anurag Agarwal <anurag.agarwal561994 at gmail.com>
* HTTPCORE-609: Non-blocking session requests to immediately cancel the associated key if already
marked as completed.
Contributed by Anurag Agarwal <anurag.agarwal561994 at gmail.com>
* HTTPCORE-607: HttpAsyncService incorrectly attempts to submit a response over idle connection in case
of a timeout (no prior request).
Contributed by Oleg Kalnichevski <olegk at apache.org>
* Bug fix: SSLEngine#closeOutbound() called prematurely in SSLIOSession#close().
Contributed by Oleg Kalnichevski <olegk at apache.org>
* HTTPCORE-601: Work-around for SSL session spin on outbound session closure with Conscrypt 2.2.1
(back-ported from master).
Contributed by Oleg Kalnichevski <olegk at apache.org>
* HTTPCLIENT-2016, regression: Tab chars are replaced by question marks in header values.
Contributed by Oleg Kalnichevski <olegk at apache.org>
* HTTPCORE-600: SSLIOSession incorrectly disables input interest when there is still decrypted data
available in the session input buffer.
Contributed by Oleg Kalnichevski <olegk at apache.org>
* HTTPCORE-596: Connection pools to reduce socket timeout to 1 second when closing managed connections
Contributed by Oleg Kalnichevski <olegk at apache.org>
Release 4.4.12
-------------------
......
httpcomponents-core (4.4.13-1) unstable; urgency=medium
* New upstream release
- Ignore the new test dependencies
* Standards-Version updated to 4.5.0
-- Emmanuel Bourg <ebourg@apache.org> Sat, 25 Jan 2020 23:28:04 +0100
httpcomponents-core (4.4.12-1) unstable; urgency=medium
* New upstream release
......
......@@ -10,7 +10,7 @@ Build-Depends:
libmaven-antrun-plugin-java,
libmaven-bundle-plugin-java,
maven-debian-helper
Standards-Version: 4.4.1
Standards-Version: 4.5.0
Vcs-Git: https://salsa.debian.org/java-team/httpcomponents-core.git
Vcs-Browser: https://salsa.debian.org/java-team/httpcomponents-core
Homepage: http://hc.apache.org/httpcomponents-core-ga/index.html
......
......@@ -33,5 +33,6 @@ org.apache.maven.plugins maven-surefire-plugin * * * *
org.apache.maven.plugins maven-surefire-report-plugin * * * *
org.apache.rat apache-rat-plugin * * * *
org.codehaus.mojo clirr-maven-plugin * * * *
org.conscrypt conscrypt-openjdk-uber * * * *
org.docbook docbook-xml * * * *
org.mockito mockito-core * * * *
......@@ -28,7 +28,7 @@
<parent>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcomponents-core</artifactId>
<version>4.4.12</version>
<version>4.4.13</version>
</parent>
<artifactId>httpcore-ab</artifactId>
<name>Apache HttpCore Benchmarking Tool</name>
......
......@@ -28,7 +28,7 @@
<parent>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcomponents-core</artifactId>
<version>4.4.12</version>
<version>4.4.13</version>
</parent>
<artifactId>httpcore-nio</artifactId>
<name>Apache HttpCore NIO</name>
......@@ -46,6 +46,11 @@
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.conscrypt</groupId>
<artifactId>conscrypt-openjdk-uber</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
......
......@@ -29,6 +29,7 @@ package org.apache.http.examples.nio;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.security.cert.CertificateException;
......@@ -43,9 +44,7 @@ import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.config.ConnectionConfig;
......@@ -71,9 +70,10 @@ import org.apache.http.nio.IOControl;
import org.apache.http.nio.NHttpClientConnection;
import org.apache.http.nio.NHttpConnection;
import org.apache.http.nio.NHttpServerConnection;
import org.apache.http.nio.NHttpServerEventHandler;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.http.nio.pool.NIOConnFactory;
import org.apache.http.nio.protocol.BasicAsyncResponseProducer;
import org.apache.http.nio.protocol.ErrorResponseProducer;
import org.apache.http.nio.protocol.HttpAsyncExchange;
import org.apache.http.nio.protocol.HttpAsyncRequestConsumer;
import org.apache.http.nio.protocol.HttpAsyncRequestExecutor;
......@@ -143,32 +143,30 @@ public class NHttpReverseProxy {
System.out.println("Reverse proxy to " + targetHost);
final IOReactorConfig config = IOReactorConfig.custom()
final ConnectingIOReactor connectingIOReactor = new DefaultConnectingIOReactor(IOReactorConfig.custom()
.setIoThreadCount(1)
.setSoTimeout(3000)
.setConnectTimeout(3000)
.build();
final ConnectingIOReactor connectingIOReactor = new DefaultConnectingIOReactor(config);
final ListeningIOReactor listeningIOReactor = new DefaultListeningIOReactor(config);
.setSoTimeout(5000)
.setConnectTimeout(5000)
.build());
final ListeningIOReactor listeningIOReactor = new DefaultListeningIOReactor(IOReactorConfig.custom()
.setIoThreadCount(1)
.setSoTimeout(30000)
.build());
// Set up HTTP protocol processor for incoming connections
final HttpProcessor inhttpproc = new ImmutableHttpProcessor(
new HttpResponseInterceptor[] {
new ResponseDate(),
new ResponseServer("Test/1.1"),
new ResponseContent(),
new ResponseConnControl()
});
new ResponseConnControl());
// Set up HTTP protocol processor for outgoing connections
final HttpProcessor outhttpproc = new ImmutableHttpProcessor(
new HttpRequestInterceptor[] {
new RequestContent(),
new RequestTargetHost(),
new RequestConnControl(),
new RequestUserAgent("Test/1.1"),
new RequestExpectContinue(true)
});
new RequestExpectContinue(true));
final ProxyClientProtocolHandler clientHandler = new ProxyClientProtocolHandler();
final HttpAsyncRequester executor = new HttpAsyncRequester(
......@@ -193,7 +191,7 @@ public class NHttpReverseProxy {
final IOEventDispatch connectingEventDispatch = DefaultHttpClientIODispatch.create(
clientHandler, sslContext, ConnectionConfig.DEFAULT);
final IOEventDispatch listeningEventDispatch = new DefaultHttpServerIODispatch(
final IOEventDispatch listeningEventDispatch = new DefaultHttpServerIODispatch<NHttpServerEventHandler>(
serviceHandler, ConnectionConfig.DEFAULT);
final Thread t = new Thread(new Runnable() {
......@@ -403,14 +401,14 @@ public class NHttpReverseProxy {
if (ex != null) {
System.out.println("[client<-proxy] " + httpExchange.getId() + " " + ex);
final int status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_0, status,
final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, status,
EnglishReasonPhraseCatalog.INSTANCE.getReason(status, Locale.US));
String message = ex.getMessage();
if (message == null) {
message = "Unexpected error";
}
response.setEntity(new NStringEntity(message, ContentType.DEFAULT_TEXT));
responseTrigger.submitResponse(new BasicAsyncResponseProducer(response));
responseTrigger.submitResponse(new ErrorResponseProducer(response,
new NStringEntity(message, ContentType.DEFAULT_TEXT), false));
System.out.println("[client<-proxy] " + httpExchange.getId() + " error response triggered");
}
final HttpResponse response = httpExchange.getResponse();
......@@ -679,15 +677,16 @@ public class NHttpReverseProxy {
final HttpAsyncExchange responseTrigger = this.httpExchange.getResponseTrigger();
if (responseTrigger != null && !responseTrigger.isCompleted()) {
System.out.println("[client<-proxy] " + this.httpExchange.getId() + " " + ex);
final int status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_0, status,
final int status = ex instanceof SocketTimeoutException ?
HttpStatus.SC_GATEWAY_TIMEOUT : HttpStatus.SC_INTERNAL_SERVER_ERROR;
final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, status,
EnglishReasonPhraseCatalog.INSTANCE.getReason(status, Locale.US));
String message = ex.getMessage();
if (message == null) {
message = "Unexpected error";
}
response.setEntity(new NStringEntity(message, ContentType.DEFAULT_TEXT));
responseTrigger.submitResponse(new BasicAsyncResponseProducer(response));
responseTrigger.submitResponse(new ErrorResponseProducer(response,
new NStringEntity(message, ContentType.DEFAULT_TEXT), false));
}
}
}
......
......@@ -290,7 +290,6 @@ public class DefaultNHttpServerConnection
}
}
} catch (final HttpException ex) {
resetInput();
handler.exception(this, ex);
} catch (final Exception ex) {
handler.exception(this, ex);
......
......@@ -29,8 +29,8 @@ package org.apache.http.impl.nio.pool;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.apache.http.annotation.ThreadingBehavior;
import org.apache.http.annotation.Contract;
import org.apache.http.annotation.ThreadingBehavior;
import org.apache.http.nio.NHttpClientConnection;
import org.apache.http.pool.PoolEntry;
......@@ -54,7 +54,16 @@ public class BasicNIOPoolEntry extends PoolEntry<HttpHost, NHttpClientConnection
@Override
public void close() {
try {
getConnection().close();
final NHttpClientConnection connection = getConnection();
try {
final int socketTimeout = connection.getSocketTimeout();
if (socketTimeout <= 0 || socketTimeout > 1000) {
connection.setSocketTimeout(1000);
}
connection.close();
} catch (final IOException ex) {
connection.shutdown();
}
} catch (final IOException ignore) {
}
}
......
......@@ -425,7 +425,7 @@ public abstract class AbstractIOReactor implements IOReactor {
if (!sessionRequest.isTerminated()) {
sessionRequest.completed(session);
}
if (!sessionRequest.isTerminated()) {
if (!sessionRequest.isTerminated() && !session.isClosed()) {
sessionCreated(key, session);
}
if (sessionRequest.isTerminated()) {
......
......@@ -210,11 +210,7 @@ public class DefaultConnectingIOReactor extends AbstractMultiworkerIOReactor
final int timeout = sessionRequest.getConnectTimeout();
if (timeout > 0) {
if (handle.getRequestTime() + timeout < now) {
try {
sessionRequest.timeout();
} finally {
key.attach(null);
}
}
}
}
......
......@@ -31,7 +31,7 @@ import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.http.annotation.Contract;
import org.apache.http.annotation.ThreadingBehavior;
......@@ -48,15 +48,22 @@ import org.apache.http.util.Args;
@Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
public class SessionRequestImpl implements SessionRequest {
enum SessionRequestState {
ACTIVE,
SUCCESSFUL,
TIMEDOUT,
CANCELLED,
FAILED,
}
private final SocketAddress remoteAddress;
private final SocketAddress localAddress;
private final Object attachment;
private final SessionRequestCallback callback;
private final AtomicBoolean completed;
private final AtomicReference<SessionRequestState> state;
private volatile SelectionKey key;
private volatile boolean terminated;
private volatile int connectTimeout;
private volatile IOSession session = null;
private volatile IOException exception = null;
......@@ -72,7 +79,7 @@ public class SessionRequestImpl implements SessionRequest {
this.localAddress = localAddress;
this.attachment = attachment;
this.callback = callback;
this.completed = new AtomicBoolean(false);
this.state = new AtomicReference<SessionRequestState>(SessionRequestState.ACTIVE);
}
@Override
......@@ -92,24 +99,33 @@ public class SessionRequestImpl implements SessionRequest {
@Override
public boolean isCompleted() {
return this.completed.get();
return this.state.get().compareTo(SessionRequestState.ACTIVE) != 0;
}
boolean isTerminated() {
return this.terminated;
return this.state.get().compareTo(SessionRequestState.SUCCESSFUL) > 0;
}
protected void setKey(final SelectionKey key) {
this.key = key;
if (this.isCompleted()) {
key.cancel();
final Channel channel = key.channel();
if (channel.isOpen()) {
try {
channel.close();
} catch (final IOException ignore) {}
}
}
}
@Override
public void waitFor() throws InterruptedException {
if (this.completed.get()) {
if (this.isCompleted()) {
return;
}
synchronized (this) {
while (!this.completed.get()) {
while (!this.isCompleted()) {
wait();
}
}
......@@ -131,7 +147,7 @@ public class SessionRequestImpl implements SessionRequest {
public void completed(final IOSession session) {
Args.notNull(session, "Session");
if (this.completed.compareAndSet(false, true)) {
if (this.state.compareAndSet(SessionRequestState.ACTIVE, SessionRequestState.SUCCESSFUL)) {
synchronized (this) {
this.session = session;
if (this.callback != null) {
......@@ -146,8 +162,7 @@ public class SessionRequestImpl implements SessionRequest {
if (exception == null) {
return;
}
if (this.completed.compareAndSet(false, true)) {
this.terminated = true;
if (this.state.compareAndSet(SessionRequestState.ACTIVE, SessionRequestState.FAILED)) {
final SelectionKey key = this.key;
if (key != null) {
key.cancel();
......@@ -167,8 +182,7 @@ public class SessionRequestImpl implements SessionRequest {
}
public void timeout() {
if (this.completed.compareAndSet(false, true)) {
this.terminated = true;
if (this.state.compareAndSet(SessionRequestState.ACTIVE, SessionRequestState.TIMEDOUT)) {
final SelectionKey key = this.key;
if (key != null) {
key.cancel();
......@@ -205,8 +219,7 @@ public class SessionRequestImpl implements SessionRequest {
@Override
public void cancel() {
if (this.completed.compareAndSet(false, true)) {
this.terminated = true;
if (this.state.compareAndSet(SessionRequestState.ACTIVE, SessionRequestState.CANCELLED)) {
final SelectionKey key = this.key;
if (key != null) {
key.cancel();
......
......@@ -543,6 +543,10 @@ public abstract class AbstractNIOConnPool<T, C, E extends PoolEntry<T, C>>
protected void requestCompleted(final SessionRequest request) {
if (this.isShutDown.get()) {
final IOSession session = request.getSession();
if (session != null) {
session.close();
}
return;
}
@SuppressWarnings("unchecked")
......
......@@ -28,6 +28,7 @@
package org.apache.http.nio.protocol;
import java.io.IOException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
......@@ -285,28 +286,53 @@ public class HttpAsyncService implements NHttpServerEventHandler {
}
state.setTerminated();
closeHandlers(state, cause);
try {
final Cancellable cancellable = state.getCancellable();
if (cancellable != null) {
cancellable.cancel();
}
final Queue<PipelineEntry> pipeline = state.getPipeline();
if (!pipeline.isEmpty()
|| conn.isResponseSubmitted()
|| state.getResponseState().compareTo(MessageState.INIT) > 0) {
// There is not much that we can do if a response
// has already been submitted or pipelining is being used.
shutdownConnection(conn);
} else {
try {
if (cause instanceof SocketException) {
// Transport layer is likely unreliable.
conn.shutdown();
return;
}
if (cause instanceof SocketTimeoutException) {
// Connection timed out due to inactivity.
conn.close();
return;
}
if (conn.isResponseSubmitted() || state.getResponseState().compareTo(MessageState.INIT) > 0) {
// There is not much that we can do if a response has already been submitted.
conn.close();
return;
}
HttpRequest request = conn.getHttpRequest();
if (request == null) {
final Incoming incoming = state.getIncoming();
final HttpRequest request = incoming != null ? incoming.getRequest() : null;
final HttpContext context = incoming != null ? incoming.getContext() : new BasicHttpContext();
if (incoming != null) {
request = incoming.getRequest();
}
}
if (request == null) {
final Queue<PipelineEntry> pipeline = state.getPipeline();
final PipelineEntry pipelineEntry = pipeline.poll();
if (pipelineEntry != null) {
request = pipelineEntry.getRequest();
}
}
if (request != null) {
conn.resetInput();
final HttpCoreContext context = HttpCoreContext.create();
final HttpAsyncResponseProducer responseProducer = handleException(cause, context);
final HttpResponse response = responseProducer.generateResponse();
final Outgoing outgoing = new Outgoing(request, response, responseProducer, context);
state.setResponseState(MessageState.INIT);
state.setOutgoing(outgoing);
commitFinalResponse(conn, state);
return;
}
conn.close();
} catch (final Exception ex) {
shutdownConnection(conn);
closeHandlers(state);
......@@ -316,6 +342,9 @@ public class HttpAsyncService implements NHttpServerEventHandler {
log(ex);
}
}
protected HttpResponse createHttpResponse(final int status, final HttpContext context) {
return this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1, status, context);
}
@Override
......@@ -350,8 +379,7 @@ public class HttpAsyncService implements NHttpServerEventHandler {
&& !(conn instanceof SessionBufferStatus && ((SessionBufferStatus) conn).hasBufferedInput())) {
state.setRequestState(MessageState.ACK_EXPECTED);
final HttpResponse ack = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_CONTINUE, context);
final HttpResponse ack = createHttpResponse(HttpStatus.SC_CONTINUE, context);
if (this.expectationVerifier != null) {
conn.suspendInput();
conn.suspendOutput();
......@@ -442,11 +470,10 @@ public class HttpAsyncService implements NHttpServerEventHandler {
final Object result = pipelineEntry.getResult();
final HttpRequest request = pipelineEntry.getRequest();
final HttpContext context = pipelineEntry.getContext();
if (result != null) {
final HttpResponse response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, context);
final HttpResponse response = createHttpResponse(HttpStatus.SC_OK, context);
final HttpAsyncExchangeImpl httpExchange = new HttpAsyncExchangeImpl(
request, response, state, conn, context);
if (result != null) {
final HttpAsyncRequestHandler<Object> handler = pipelineEntry.getHandler();
conn.suspendOutput();
try {
......@@ -454,14 +481,12 @@ public class HttpAsyncService implements NHttpServerEventHandler {
} catch (final RuntimeException ex) {
throw ex;
} catch (final Exception ex) {
pipeline.add(new PipelineEntry(
request,
null,
ex,
handler,
context));
state.setResponseState(MessageState.READY);
responseReady(conn);
if (!httpExchange.isCompleted()) {
httpExchange.submitResponse(handleException(ex, context));
} else {
log(ex);
conn.close();
}
return;
}
} else {
......@@ -469,8 +494,7 @@ public class HttpAsyncService implements NHttpServerEventHandler {
final HttpAsyncResponseProducer responseProducer = handleException(
exception != null ? exception : new HttpException("Internal error processing request"),
context);
final HttpResponse error = responseProducer.generateResponse();
state.setOutgoing(new Outgoing(request, error, responseProducer, context));
httpExchange.submitResponse(responseProducer);
}
}
if (state.getResponseState() == MessageState.INIT) {
......@@ -529,7 +553,7 @@ public class HttpAsyncService implements NHttpServerEventHandler {
public void timeout(final NHttpServerConnection conn) throws IOException {
final State state = getState(conn);
if (state != null) {
exception(conn, new SocketTimeoutException(
closeHandlers(state, new SocketTimeoutException(
String.format("%,d milliseconds timeout on connection %s", conn.getSocketTimeout(), conn)));
}
if (conn.getStatus() == NHttpConnection.ACTIVE) {
......@@ -623,10 +647,8 @@ public class HttpAsyncService implements NHttpServerEventHandler {
if (message == null) {
message = ex.toString();
}
final HttpResponse response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1,
toStatusCode(ex, context), context);
return new ErrorResponseProducer(response,
new NStringEntity(message, ContentType.DEFAULT_TEXT), false);
final HttpResponse response = createHttpResponse(toStatusCode(ex, context), context);
return new ErrorResponseProducer(response, new NStringEntity(message, ContentType.DEFAULT_TEXT), false);
}
protected int toStatusCode(final Exception ex, final HttpContext context) {
......@@ -637,8 +659,6 @@ public class HttpAsyncService implements NHttpServerEventHandler {
code = HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED;
} else if (ex instanceof ProtocolException) {
code = HttpStatus.SC_BAD_REQUEST;
} else if (ex instanceof SocketTimeoutException) {
code = HttpStatus.SC_GATEWAY_TIMEOUT;
} else {
code = HttpStatus.SC_INTERNAL_SERVER_ERROR;
}
......
......@@ -35,6 +35,7 @@ import java.nio.channels.ByteChannel;
import java.nio.channels.CancelledKeyException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
......@@ -89,6 +90,7 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
private final SSLBuffer inPlain;
private final InternalByteChannel channel;
private final SSLSetupHandler handler;
private final AtomicInteger outboundClosedCount;
private int appEventMask;
private SessionBufferStatus appBufferStatus;
......@@ -163,6 +165,7 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
// Allocate buffers for application (unencrypted) data
final int appBuffersize = this.sslEngine.getSession().getApplicationBufferSize();
this.inPlain = bufferManagementStrategy.constructBuffer(appBuffersize);
this.outboundClosedCount = new AtomicInteger(0);
}
/**
......@@ -294,7 +297,15 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
SSLEngineResult result = null;
while (handshaking) {
switch (this.sslEngine.getHandshakeStatus()) {
HandshakeStatus handshakeStatus = this.sslEngine.getHandshakeStatus();
// Work-around for what appears to be a bug in Conscrypt SSLEngine that does not
// transition into the handshaking state upon #closeOutbound() call but still
// has some handshake data stuck in its internal buffer.
if (handshakeStatus == HandshakeStatus.NOT_HANDSHAKING && outboundClosedCount.get() > 0) {
handshakeStatus = HandshakeStatus.NEED_WRAP;
}
switch (handshakeStatus) {
case NEED_WRAP:
// Generate outgoing handshake data
......@@ -370,6 +381,7 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
}
if (this.status == CLOSING && !this.outEncrypted.hasData()) {
this.sslEngine.closeOutbound();
this.outboundClosedCount.incrementAndGet();
}
if (this.status == CLOSING && this.sslEngine.isOutboundDone()
&& (this.endOfStream || this.sslEngine.isInboundDone())
......@@ -405,7 +417,9 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
break;
}
if (this.endOfStream && (this.appBufferStatus == null || !this.appBufferStatus.hasBufferedInput())) {
if (this.endOfStream &&
!this.inPlain.hasData() &&
(this.appBufferStatus == null || !this.appBufferStatus.hasBufferedInput())) {
newMask = newMask & ~EventMask.READ;
}
......@@ -624,7 +638,6 @@ public class SSLIOSession implements IOSession, SessionBufferStatus, SocketAcces
if (this.session.getSocketTimeout() == 0) {
this.session.setSocketTimeout(1000);
}
this.sslEngine.closeOutbound();
try {
updateEventMask();
} catch (final CancelledKeyException ex) {
......
......@@ -37,6 +37,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.http.HttpException;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
......@@ -67,6 +68,7 @@ import org.apache.http.protocol.RequestConnControl;
import org.apache.http.protocol.RequestExpectContinue;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;
import org.apache.http.protocol.ResponseServer;
import org.apache.http.util.EntityUtils;
import org.junit.After;
import org.junit.Assert;
......@@ -82,6 +84,7 @@ import org.junit.runners.Parameterized;
public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
private final static long RESULT_TIMEOUT_SEC = 30;
private final static int REQ_NUM = 25;
@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> protocols() {
......@@ -144,7 +147,65 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
final String expectedPattern = createExpectedString(pattern, count);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < 30; i++) {
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("GET", createRequestUri(pattern, count));
final Future<HttpResponse> future = this.client.execute(target, request);
queue.add(future);
}
while (!queue.isEmpty()) {
final Future<HttpResponse> future = queue.remove();
final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
Assert.assertNotNull(response);
Assert.assertEquals(expectedPattern, EntityUtils.toString(response.getEntity()));
}
}
@Test
public void testHttpGetsCloseConnection() throws Exception {
this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
final HttpHost target = start();
this.client.setMaxPerRoute(3);
this.client.setMaxTotal(3);
final String pattern = RndTestPatternGenerator.generateText();
final int count = RndTestPatternGenerator.generateCount(1000);
final String expectedPattern = createExpectedString(pattern, count);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("GET", createRequestUri(pattern, count));
request.addHeader(HttpHeaders.CONNECTION, "Close");
final Future<HttpResponse> future = this.client.execute(target, request);
queue.add(future);
}
while (!queue.isEmpty()) {
final Future<HttpResponse> future = queue.remove();
final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
Assert.assertNotNull(response);
Assert.assertEquals(expectedPattern, EntityUtils.toString(response.getEntity()));
}
}
@Test
public void testHttpGetIdentityTransfer() throws Exception {
this.server.setHttpProcessor(new ImmutableHttpProcessor(new ResponseServer("TEST-SERVER/1.1")));
this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
final HttpHost target = start();
this.client.setMaxPerRoute(3);
this.client.setMaxTotal(3);
final String pattern = RndTestPatternGenerator.generateText();
final int count = RndTestPatternGenerator.generateCount(1000);
final String expectedPattern = createExpectedString(pattern, count);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("GET", createRequestUri(pattern, count));
final Future<HttpResponse> future = this.client.execute(target, request);
queue.add(future);
......@@ -170,8 +231,35 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
final int count = RndTestPatternGenerator.generateCount(1000);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < 30; i++) {
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("HEAD", createRequestUri(pattern, count));
final Future<HttpResponse> future = this.client.execute(target, request);
queue.add(future);
}
while (!queue.isEmpty()) {
final Future<HttpResponse> future = queue.remove();
final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
Assert.assertNotNull(response);
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
}
}
@Test
public void testHttpHeadsCloseConnection() throws Exception {
this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
final HttpHost target = start();
this.client.setMaxPerRoute(3);
this.client.setMaxTotal(3);
final String pattern = RndTestPatternGenerator.generateText();
final int count = RndTestPatternGenerator.generateCount(1000);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("HEAD", createRequestUri(pattern, count));
request.addHeader(HttpHeaders.CONNECTION, "Close");
final Future<HttpResponse> future = this.client.execute(target, request);
queue.add(future);
}
......@@ -181,6 +269,7 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
Assert.assertNotNull(response);
Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
Assert.assertNull(response.getEntity());
}
}
......@@ -198,7 +287,7 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
final String expectedPattern = createExpectedString(pattern, count);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < 30; i++) {
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest(
"POST", createRequestUri(pattern, count));
final NStringEntity entity = new NStringEntity(expectedPattern, ContentType.DEFAULT_TEXT);
......@@ -229,7 +318,7 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
final String expectedPattern = createExpectedString(pattern, count);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < 30; i++) {
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest(
"POST", createRequestUri(pattern, count));
final NStringEntity entity = new NStringEntity(expectedPattern, ContentType.DEFAULT_TEXT);
......@@ -261,7 +350,7 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
final String expectedPattern = createExpectedString(pattern, count);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < 30; i++) {
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest(
"POST", createRequestUri(pattern, count), HttpVersion.HTTP_1_0);
final NStringEntity entity = new NStringEntity(expectedPattern, ContentType.DEFAULT_TEXT);
......@@ -290,7 +379,7 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
final int count = RndTestPatternGenerator.generateCount(1000);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < 30; i++) {
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest(
"POST", createRequestUri(pattern, count));
request.setEntity(null);
......@@ -388,7 +477,7 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
final String expectedPattern = createExpectedString(pattern, count);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < 30; i++) {
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest(
"POST", createRequestUri(pattern, count));
final NStringEntity entity = new NStringEntity(expectedPattern, ContentType.DEFAULT_TEXT);
......@@ -525,7 +614,7 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
final int count = RndTestPatternGenerator.generateCount(1000);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < 30; i++) {
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("HEAD", createRequestUri(pattern, count));
final Future<HttpResponse> future = this.client.execute(target, request);
queue.add(future);
......@@ -671,7 +760,7 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
final int count = RndTestPatternGenerator.generateCount(1000);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < 30; i++) {
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("GET", createRequestUri(pattern, count));
final Future<HttpResponse> future = this.client.execute(target, request);
queue.add(future);
......@@ -704,7 +793,7 @@ public class TestHttpAsyncHandlers extends HttpCoreNIOTestBase {
this.client.setMaxTotal(3);
final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
for (int i = 0; i < 30; i++) {
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("GET", "/");
final Future<HttpResponse> future = this.client.execute(target, request);
queue.add(future);
......
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.nio.integration;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URL;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.impl.nio.pool.BasicNIOConnFactory;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
import org.apache.http.nio.reactor.IOSession;
import org.apache.http.nio.reactor.ListenerEndpoint;
import org.apache.http.nio.reactor.ssl.SSLSetupHandler;
import org.apache.http.nio.testserver.HttpClientNio;
import org.apache.http.nio.testserver.HttpServerNio;
import org.apache.http.nio.testserver.ServerConnectionFactory;
import org.apache.http.nio.util.TestingSupport;
import org.apache.http.protocol.ImmutableHttpProcessor;
import org.apache.http.protocol.ResponseServer;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.conscrypt.Conscrypt;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestJSSEProviderIntegration {
private final static long RESULT_TIMEOUT_SEC = 30;
private final static int REQ_NUM = 25;
@Parameterized.Parameters(name = "{0} {1}")
public static Collection<Object[]> protocols() {
return Arrays.asList(new Object[][]{
{"Oracle", null},
{"Conscrypt", "TLSv1.2"},
{"Conscrypt", "TLSv1.3"}
});
}
private final String securityProviderName;
private final String protocolVersion;
private Provider securityProvider;
private HttpServerNio server;
private HttpClientNio client;
public TestJSSEProviderIntegration(final String securityProviderName, final String protocolVersion) {
super();
this.securityProviderName = securityProviderName;
this.protocolVersion = protocolVersion;
}
@BeforeClass
public static void determineJavaVersion() {
Assume.assumeTrue("Java version must be 8 or greater", TestingSupport.determineJRELevel() >= 8);
}
@Rule
public TestRule resourceRules = RuleChain.outerRule(new ExternalResource() {
@Override
protected void before() throws Throwable {
if ("Conscrypt".equalsIgnoreCase(securityProviderName)) {
securityProvider = Conscrypt.newProviderBuilder().provideTrustManager(true).build();
} else {
securityProvider = null;
}
if (securityProvider != null) {
Security.insertProviderAt(securityProvider, 1);
}
}
@Override
protected void after() {
if (securityProvider != null) {
Security.removeProvider(securityProvider.getName());
securityProvider = null;
}
}
}).around(new ExternalResource() {
@Override
protected void before() throws Throwable {
final URL keyStoreURL = TestJSSEProviderIntegration.class.getResource("/test-server.p12");
final String storePassword = "nopassword";
final SSLContext sslContext = SSLContextBuilder.create()
.setKeyStoreType("pkcs12")
.loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
.loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
.setSecureRandom(new SecureRandom())
.build();
server = new HttpServerNio();
server.setConnectionFactory(new ServerConnectionFactory(sslContext, new SSLSetupHandler() {
@Override
public void initalize(final SSLEngine sslEngine) throws SSLException {
if (protocolVersion != null) {
sslEngine.setEnabledProtocols(new String[]{protocolVersion});
}
}
@Override
public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
}
}));
server.setTimeout(5000);
}
@Override
protected void after() {
if (server != null) {
try {
server.shutdown();
} catch (final Exception ignore) {
}
}
}
}).around(new ExternalResource() {
@Override
protected void before() throws Throwable {
final URL keyStoreURL = TestJSSEProviderIntegration.class.getResource("/test-client.p12");
final String storePassword = "nopassword";
final SSLContext sslContext = SSLContextBuilder.create()
.setKeyStoreType("pkcs12")
.loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
.setSecureRandom(new SecureRandom())
.build();
client = new HttpClientNio(new BasicNIOConnFactory(sslContext, new SSLSetupHandler() {
@Override
public void initalize(final SSLEngine sslEngine) throws SSLException {
if (protocolVersion != null) {
sslEngine.setEnabledProtocols(new String[]{protocolVersion});
}
}
@Override
public void verify(final IOSession ioSession, final SSLSession sslSession) throws SSLException {
}
}, ConnectionConfig.DEFAULT));
client.setTimeout(5000);
}
@Override
protected void after() {
if (client != null) {
try {
client.shutdown();
} catch (final Exception ignore) {
}
}
}
});
private HttpHost start() throws IOException, InterruptedException {
this.server.start();
this.client.start();
final ListenerEndpoint endpoint = this.server.getListenerEndpoint();
endpoint.waitFor();
final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
return new HttpHost("localhost", address.getPort(), "https");
}
@Test
public void testHttpGets() throws Exception {
this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
final HttpHost target = start();
this.client.setMaxPerRoute(3);
this.client.setMaxTotal(3);
final String pattern = RndTestPatternGenerator.generateText();
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("GET", pattern + "x1");
final Future<HttpResponse> future = this.client.execute(target, request);
final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
Assert.assertNotNull(response);
Assert.assertEquals(pattern, EntityUtils.toString(response.getEntity()));
}
}
@Test
public void testHttpGetsCloseConnection() throws Exception {
this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
final HttpHost target = start();
this.client.setMaxPerRoute(3);
this.client.setMaxTotal(3);
final String pattern = RndTestPatternGenerator.generateText();
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("GET", pattern + "x1");
request.addHeader(HttpHeaders.CONNECTION, "Close");
final Future<HttpResponse> future = this.client.execute(target, request);
final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
Assert.assertNotNull(response);
Assert.assertEquals(pattern, EntityUtils.toString(response.getEntity()));
}
}
@Test
public void testHttpGetIdentityTransfer() throws Exception {
this.server.setHttpProcessor(new ImmutableHttpProcessor(new ResponseServer("TEST-SERVER/1.1")));
this.server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
final HttpHost target = start();
this.client.setMaxPerRoute(3);
this.client.setMaxTotal(3);
final String pattern = RndTestPatternGenerator.generateText();
for (int i = 0; i < REQ_NUM; i++) {
final BasicHttpRequest request = new BasicHttpRequest("GET", pattern + "x1");
final Future<HttpResponse> future = this.client.execute(target, request);
final HttpResponse response = future.get(RESULT_TIMEOUT_SEC, TimeUnit.SECONDS);
Assert.assertNotNull(response);
Assert.assertEquals(pattern, EntityUtils.toString(response.getEntity()));
}
}
}
......@@ -58,6 +58,7 @@ import org.apache.http.nio.testserver.ClientConnectionFactory;
import org.apache.http.nio.testserver.HttpClientNio;
import org.apache.http.nio.testserver.HttpServerNio;
import org.apache.http.nio.testserver.ServerConnectionFactory;
import org.apache.http.nio.util.TestingSupport;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
import org.apache.http.protocol.HttpRequestHandler;
......@@ -73,6 +74,8 @@ public class TestTLSIntegration {
private final static long RESULT_TIMEOUT_SEC = 30;
private static int JRE_LEVEL = TestingSupport.determineJRELevel();
private HttpServerNio server;
@Rule
......@@ -108,22 +111,40 @@ public class TestTLSIntegration {
};
private static SSLContext createServerSSLContext() throws Exception {
if (JRE_LEVEL >= 8) {
final URL keyStoreURL = TestTLSIntegration.class.getResource("/test-server.p12");
final String storePassword = "nopassword";
return SSLContextBuilder.create()
.setKeyStoreType("pkcs12")
.loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
.loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
.build();
} else {
final URL keyStoreURL = TestTLSIntegration.class.getResource("/test.keystore");
final String storePassword = "nopassword";
return SSLContextBuilder.create()
.loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
.loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
.build();
}
}
private static SSLContext createClientSSLContext() throws Exception {
if (JRE_LEVEL >= 8) {
final URL keyStoreURL = TestTLSIntegration.class.getResource("/test-client.p12");
final String storePassword = "nopassword";
return SSLContextBuilder.create()
.setKeyStoreType("pkcs12")
.loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
.build();
} else {
final URL keyStoreURL = TestTLSIntegration.class.getResource("/test.keystore");
final String storePassword = "nopassword";
return SSLContextBuilder.create()
.loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
.build();
}
}
@Test
public void testTLSSuccess() throws Exception {
......@@ -165,9 +186,14 @@ public class TestTLSIntegration {
Assert.assertThat(response.getStatusLine().getStatusCode(), CoreMatchers.equalTo(200));
final SSLSession sslSession = sslSessionRef.getAndSet(null);
if (JRE_LEVEL >= 8) {
Assert.assertThat(sslSession.getPeerPrincipal().getName(),
CoreMatchers.equalTo("CN=Test Server,OU=HttpComponents Project,O=Apache Software Foundation"));
} else {
Assert.assertThat(sslSession.getPeerPrincipal().getName(),
CoreMatchers.equalTo("CN=localhost,OU=Apache HttpComponents,O=Apache Software Foundation"));
}
}
@Test
public void testTLSTrustFailure() throws Exception {
......@@ -177,7 +203,7 @@ public class TestTLSIntegration {
server.registerHandler("*", new BasicAsyncRequestHandler(new SimpleRequestHandler()));
server.start();
this.client = new HttpClientNio(new BasicNIOConnFactory(SSLContexts.createDefault(), null, ConnectionConfig.DEFAULT));
client = new HttpClientNio(new BasicNIOConnFactory(SSLContexts.createDefault(), null, ConnectionConfig.DEFAULT));
client.setTimeout(5000);
client.start();
......
......@@ -163,6 +163,7 @@ public class TestHttpAsyncService {
state.setIncoming(incoming);
state.setCancellable(this.cancellable);
this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
final HttpException httpex = new HttpException();
this.protocolHandler.exception(this.conn, httpex);
......@@ -210,6 +211,7 @@ public class TestHttpAsyncService {
state.setOutgoing(outgoing);
state.setCancellable(this.cancellable);
this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
Mockito.doThrow(new RuntimeException()).when(this.httpProcessor).process(
Matchers.any(HttpResponse.class), Matchers.any(HttpContext.class));
......@@ -243,6 +245,7 @@ public class TestHttpAsyncService {
state.setOutgoing(outgoing);
state.setCancellable(this.cancellable);
this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
Mockito.doThrow(new IOException()).when(this.httpProcessor).process(
Matchers.any(HttpResponse.class), Matchers.any(HttpContext.class));
......@@ -280,7 +283,7 @@ public class TestHttpAsyncService {
Assert.assertEquals(MessageState.READY, state.getRequestState());
Assert.assertEquals(MessageState.READY, state.getResponseState());
Mockito.verify(this.conn).shutdown();
Mockito.verify(this.conn).close();
Mockito.verify(this.requestConsumer).failed(httpex);
Mockito.verify(this.requestConsumer).close();
Mockito.verify(this.responseProducer).failed(httpex);
......@@ -1329,19 +1332,31 @@ public class TestHttpAsyncService {
@Test
public void testTimeoutActiveConnection() throws Exception {
final State state = new State();
final HttpContext exchangeContext = new BasicHttpContext();
final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
final Incoming incoming = new Incoming(
request, this.requestHandler, this.requestConsumer, exchangeContext);
state.setIncoming(incoming);
this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
Mockito.when(this.conn.getStatus()).thenReturn(NHttpConnection.ACTIVE, NHttpConnection.CLOSED);
this.protocolHandler.timeout(this.conn);
Mockito.verify(this.conn).close();
Mockito.verify(this.conn, Mockito.atLeastOnce()).close();
Mockito.verify(this.conn, Mockito.never()).setSocketTimeout(Matchers.anyInt());
}
@Test
public void testTimeoutActiveConnectionBufferedData() throws Exception {
final State state = new State();
final HttpContext exchangeContext = new BasicHttpContext();
final HttpRequest request = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
final Incoming incoming = new Incoming(
request, this.requestHandler, this.requestConsumer, exchangeContext);
state.setIncoming(incoming);
this.connContext.setAttribute(HttpAsyncService.HTTP_EXCHANGE_STATE, state);
Mockito.when(this.conn.getHttpRequest()).thenReturn(request);
Mockito.when(this.conn.getStatus()).thenReturn(NHttpConnection.ACTIVE, NHttpConnection.CLOSING);
this.protocolHandler.timeout(this.conn);
......