mirror of
https://github.com/apache/httpcomponents-client.git
synced 2025-02-13 13:35:54 +00:00
HTTPCLIENT-1655: HttpClient sends RST instead of FIN ACK sequence when using non-persistant connections
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1683685 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
1eb2553ddb
commit
ce86e3ed81
@ -31,8 +31,7 @@
|
|||||||
/**
|
/**
|
||||||
* Interface for releasing a connection. This can be implemented by various
|
* Interface for releasing a connection. This can be implemented by various
|
||||||
* "trigger" objects which are associated with a connection, for example
|
* "trigger" objects which are associated with a connection, for example
|
||||||
* a {@link EofSensorInputStream stream} or an {@link BasicManagedEntity entity}
|
* a {@link EofSensorInputStream} or the {@link ManagedHttpClientConnection} itself.
|
||||||
* or the {@link ManagedClientConnection connection} itself.
|
|
||||||
* <p>
|
* <p>
|
||||||
* The methods in this interface can safely be called multiple times.
|
* The methods in this interface can safely be called multiple times.
|
||||||
* The first invocation releases the connection, subsequent calls
|
* The first invocation releases the connection, subsequent calls
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.http.HttpClientConnection;
|
import org.apache.http.HttpClientConnection;
|
||||||
@ -50,13 +51,12 @@ class ConnectionHolder implements ConnectionReleaseTrigger, Cancellable, Closeab
|
|||||||
|
|
||||||
private final HttpClientConnectionManager manager;
|
private final HttpClientConnectionManager manager;
|
||||||
private final HttpClientConnection managedConn;
|
private final HttpClientConnection managedConn;
|
||||||
|
private final AtomicBoolean released;
|
||||||
private volatile boolean reusable;
|
private volatile boolean reusable;
|
||||||
private volatile Object state;
|
private volatile Object state;
|
||||||
private volatile long validDuration;
|
private volatile long validDuration;
|
||||||
private volatile TimeUnit tunit;
|
private volatile TimeUnit tunit;
|
||||||
|
|
||||||
private volatile boolean released;
|
|
||||||
|
|
||||||
public ConnectionHolder(
|
public ConnectionHolder(
|
||||||
final Log log,
|
final Log log,
|
||||||
final HttpClientConnectionManager manager,
|
final HttpClientConnectionManager manager,
|
||||||
@ -65,6 +65,7 @@ public ConnectionHolder(
|
|||||||
this.log = log;
|
this.log = log;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.managedConn = managedConn;
|
this.managedConn = managedConn;
|
||||||
|
this.released = new AtomicBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReusable() {
|
public boolean isReusable() {
|
||||||
@ -90,14 +91,10 @@ public void setValidFor(final long duration, final TimeUnit tunit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void releaseConnection(final boolean reusable) {
|
||||||
public void releaseConnection() {
|
if (this.released.compareAndSet(false, true)) {
|
||||||
synchronized (this.managedConn) {
|
synchronized (this.managedConn) {
|
||||||
if (this.released) {
|
if (reusable) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.released = true;
|
|
||||||
if (this.reusable) {
|
|
||||||
this.manager.releaseConnection(this.managedConn,
|
this.manager.releaseConnection(this.managedConn,
|
||||||
this.state, this.validDuration, this.tunit);
|
this.state, this.validDuration, this.tunit);
|
||||||
} else {
|
} else {
|
||||||
@ -115,14 +112,17 @@ public void releaseConnection() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void releaseConnection() {
|
||||||
|
releaseConnection(this.reusable);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void abortConnection() {
|
public void abortConnection() {
|
||||||
|
if (this.released.compareAndSet(false, true)) {
|
||||||
synchronized (this.managedConn) {
|
synchronized (this.managedConn) {
|
||||||
if (this.released) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.released = true;
|
|
||||||
try {
|
try {
|
||||||
this.managedConn.shutdown();
|
this.managedConn.shutdown();
|
||||||
log.debug("Connection discarded");
|
log.debug("Connection discarded");
|
||||||
@ -136,22 +136,23 @@ public void abortConnection() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean cancel() {
|
public boolean cancel() {
|
||||||
final boolean alreadyReleased = this.released;
|
final boolean alreadyReleased = this.released.get();
|
||||||
log.debug("Cancelling request execution");
|
log.debug("Cancelling request execution");
|
||||||
abortConnection();
|
abortConnection();
|
||||||
return !alreadyReleased;
|
return !alreadyReleased;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReleased() {
|
public boolean isReleased() {
|
||||||
return this.released;
|
return this.released.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
abortConnection();
|
releaseConnection(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ public HttpResponseProxy(final HttpResponse original, final ConnectionHolder con
|
|||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
if (this.connHolder != null) {
|
if (this.connHolder != null) {
|
||||||
this.connHolder.abortConnection();
|
this.connHolder.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,13 @@ public static void enchance(final HttpResponse response, final ConnectionHolder
|
|||||||
this.connHolder = connHolder;
|
this.connHolder = connHolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanup() {
|
private void cleanup() throws IOException {
|
||||||
|
if (this.connHolder != null) {
|
||||||
|
this.connHolder.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void abortConnection() throws IOException {
|
||||||
if (this.connHolder != null) {
|
if (this.connHolder != null) {
|
||||||
this.connHolder.abortConnection();
|
this.connHolder.abortConnection();
|
||||||
}
|
}
|
||||||
@ -69,14 +75,8 @@ private void cleanup() {
|
|||||||
|
|
||||||
public void releaseConnection() throws IOException {
|
public void releaseConnection() throws IOException {
|
||||||
if (this.connHolder != null) {
|
if (this.connHolder != null) {
|
||||||
try {
|
|
||||||
if (this.connHolder.isReusable()) {
|
|
||||||
this.connHolder.releaseConnection();
|
this.connHolder.releaseConnection();
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -100,6 +100,12 @@ public void writeTo(final OutputStream outstream) throws IOException {
|
|||||||
try {
|
try {
|
||||||
this.wrappedEntity.writeTo(outstream);
|
this.wrappedEntity.writeTo(outstream);
|
||||||
releaseConnection();
|
releaseConnection();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
abortConnection();
|
||||||
|
throw ex;
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
abortConnection();
|
||||||
|
throw ex;
|
||||||
} finally {
|
} finally {
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
@ -112,6 +118,12 @@ public boolean eofDetected(final InputStream wrapped) throws IOException {
|
|||||||
// reading trailers after the response body:
|
// reading trailers after the response body:
|
||||||
wrapped.close();
|
wrapped.close();
|
||||||
releaseConnection();
|
releaseConnection();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
abortConnection();
|
||||||
|
throw ex;
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
abortConnection();
|
||||||
|
throw ex;
|
||||||
} finally {
|
} finally {
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
@ -132,6 +144,12 @@ public boolean streamClosed(final InputStream wrapped) throws IOException {
|
|||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
abortConnection();
|
||||||
|
throw ex;
|
||||||
|
} catch (RuntimeException ex) {
|
||||||
|
abortConnection();
|
||||||
|
throw ex;
|
||||||
} finally {
|
} finally {
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
|
@ -305,7 +305,7 @@ public void testExecRequestConnectionRelease() throws Exception {
|
|||||||
|
|
||||||
Mockito.verify(connManager, Mockito.times(1)).releaseConnection(
|
Mockito.verify(connManager, Mockito.times(1)).releaseConnection(
|
||||||
managedConn, null, 0, TimeUnit.MILLISECONDS);
|
managedConn, null, 0, TimeUnit.MILLISECONDS);
|
||||||
Mockito.verify(managedConn, Mockito.times(1)).shutdown();
|
Mockito.verify(managedConn, Mockito.times(1)).close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -202,7 +202,7 @@ public void testExecRequestConnectionRelease() throws Exception {
|
|||||||
|
|
||||||
Mockito.verify(connManager, Mockito.times(1)).releaseConnection(
|
Mockito.verify(connManager, Mockito.times(1)).releaseConnection(
|
||||||
managedConn, null, 0, TimeUnit.MILLISECONDS);
|
managedConn, null, 0, TimeUnit.MILLISECONDS);
|
||||||
Mockito.verify(managedConn, Mockito.times(1)).shutdown();
|
Mockito.verify(managedConn, Mockito.times(1)).close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -85,7 +85,7 @@ public void testEntityStreamClosedIOErrorAlreadyReleased() throws Exception {
|
|||||||
Mockito.when(connHolder.isReleased()).thenReturn(true);
|
Mockito.when(connHolder.isReleased()).thenReturn(true);
|
||||||
Mockito.doThrow(new SocketException()).when(instream).close();
|
Mockito.doThrow(new SocketException()).when(instream).close();
|
||||||
EntityUtils.consume(wrapper);
|
EntityUtils.consume(wrapper);
|
||||||
Mockito.verify(connHolder).abortConnection();
|
Mockito.verify(connHolder).close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user