You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@hc.apache.org by "David Koski (JIRA)" <ji...@apache.org> on 2009/09/24 07:53:16 UTC
[jira] Created: (HTTPCLIENT-879) DefaultClientConnection can leaked
CLOSED connection
DefaultClientConnection can leaked CLOSED connection
----------------------------------------------------
Key: HTTPCLIENT-879
URL: https://issues.apache.org/jira/browse/HTTPCLIENT-879
Project: HttpComponents HttpClient
Issue Type: Bug
Components: HttpClient
Affects Versions: 4.0.1
Environment: Mac OS X 10.6.1
gorgatron% java -version
java version "1.6.0_15"
Java(TM) SE Runtime Environment (build 1.6.0_15-b03-219)
Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02-90, mixed mode)
Reporter: David Koski
Unit test below. Basically what happens is this:
* turn off stale check
* GET request to X
* wait for the connection to go to CLOSE_WAIT from idle
* wait for the connection to go to CLOSED
* GET request to X
* tries that request
* throws IOException
* calls connection.close()
* close() sets the open = false
* calls doFlush()
* that throws
* caught and close is called again
* but open = false, so it returns -- the Socket object is never closed
Unit test below with proposed fix (hack in style, but you get the picture). I use a connection:close to make it run in less than 10 minutes :-)
After it gets to:
System.out.println("second call done");
You can do something like this:
gorgatron% lsof | grep java | grep TCP
If you see two connections to www.apple.com:80, one CLOSED, that is the bug.
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URI;
import java.util.Arrays;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.RequestAddCookies;
import org.apache.http.client.protocol.RequestClientConnControl;
import org.apache.http.client.protocol.RequestDefaultHeaders;
import org.apache.http.client.protocol.RequestProxyAuthentication;
import org.apache.http.client.protocol.RequestTargetAuthentication;
import org.apache.http.client.protocol.ResponseProcessCookies;
import org.apache.http.conn.ClientConnectionOperator;
import org.apache.http.conn.OperatedClientConnection;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.SocketHttpClientConnection;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.DefaultClientConnection;
import org.apache.http.impl.conn.DefaultClientConnectionOperator;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.RequestExpectContinue;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
public class LeakTest extends TestCase {
public void testLeak() throws Exception {
Logger.getLogger("org.apache.http.impl.client.DefaultRequestDirector").setLevel(Level.DEBUG);
/*
* Trigger #1: turn off stale check.
*/
final HttpParams params = new BasicHttpParams();
HttpConnectionParams.setStaleCheckingEnabled(params, false);
final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
final Field f = SocketHttpClientConnection.class.getDeclaredField("open");
f.setAccessible(true);
final ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(params, schemeRegistry) {
@Override
protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) {
return new DefaultClientConnectionOperator(schreg) {
@Override
public OperatedClientConnection createConnection() {
return new DefaultClientConnection() {
@Override
public void close() throws IOException {
if (true) {
// this case will fail
super.close();
} else {
// this is the proposed fix
if (!isOpen()) {
return;
}
try {
f.set(this, false);
} catch (final Exception e) {
// eat it
}
try {
doFlush();
try {
try {
getSocket().shutdownOutput();
} catch (final IOException ignore) {
}
try {
getSocket().shutdownInput();
} catch (final IOException ignore) {
}
} catch (final UnsupportedOperationException ignore) {
// if one isn't supported, the other one isn't either
}
} finally {
getSocket().close();
}
}
}
};
}
};
}
};
final DefaultHttpClient client = new DefaultHttpClient(connectionManager, params);
final HttpGet method = new HttpGet(new URI("http://www.apple.com"));
/*
* Trigger #2: tell it connection: close. This is talking to a netscaler and it responds
* with:
*
* connection: keep-alive
*
* but it actually closes the connection. I am not sure this is very important -- if we do
* not have this we would have to wait for the connection to go idle and get closed on the
* remote side.
*
* This command shows an example of this behavior:
*
* curl -v -k -H connection:close "http://www.apple.com" "http://www.apple.com"
*/
method.setHeader("connection", "close");
client.execute(method, new ResponseHandler<Object>() {
public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
System.out.println("First request headers = " + Arrays.toString(response.getAllHeaders()));
return null;
}
});
System.out.println("waiting...");
/*
* At this point the connection is in CLOSE_WAIT:
*
* java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
* (CLOSE_WAIT)
*
* Now wait for it to go into CLOSED.
*/
waitForMilliseconds(1000 * 65);
/*
* Now that connection is CLOSED:
*
* java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
* (CLOSED)
*
* Execute the next request.
*/
client.execute(method, new ResponseHandler<Object>() {
public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
System.out.println("Second request headers = " + Arrays.toString(response.getAllHeaders()));
return null;
}
});
/**
* And we seem to have leaked the first connection (at this point perhaps not much more than
* an fd) and the second connection is going into CLOSE_WAIT:
*
* java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
* (CLOSED)
*
* java 15681 dkoski 69u IPv6 0x117a11f0 0t0 TCP [::x.x.x.x]:53936->[::x.x.x.x]:8501
* (CLOSE_WAIT)
*
* What happens inside DefaultRequestDirector:
*
* throws an IOException (broken pipe) -- right
*
* org.apache.http.impl.conn.tsccm.BasicPooledConnAdapter@3414a97b -> close()
*
* org.apache.http.impl.conn.DefaultClientConnection@70d9cbcb -> close()
*
* open = false
*
* SocketHttpClientConnection has:
*
* if (!this.open) { return;
*
* however, socket.closed = false (and truly it has not been closed)
*
* The reason it is not closed is:
*
* <pre>
* java.net.SocketException: Broken pipe
* at java.net.SocketOutputStream.socketWrite0(Native Method)
* at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
* at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
* at org.apache.http.impl.io.AbstractSessionOutputBuffer.flushBuffer(AbstractSessionOutputBuffer.java:106)
* at org.apache.http.impl.io.AbstractSessionOutputBuffer.flush(AbstractSessionOutputBuffer.java:113)
* at org.apache.http.impl.AbstractHttpClientConnection.doFlush(AbstractHttpClientConnection.java:260)
* at org.apache.http.impl.AbstractHttpClientConnection.flush(AbstractHttpClientConnection.java:265)
* at org.apache.http.impl.conn.AbstractClientConnAdapter.flush(AbstractClientConnAdapter.java:197)
* at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:252)
* at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
* at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:447)
* at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
* at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:730)
* at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:708)
* at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:699)
* </pre>
*
* the code at fault seems to be:
*
* <pre>
*
* public void close() throws IOException {
* if (!this.open) {
* return;
* }
* this.open = false;
* doFlush(); HERE: throws here and leaves the open = false but doesn't actually close it
*
* try {
* try {
* this.socket.shutdownOutput();
* } catch (IOException ignore) {
* }
* try {
* this.socket.shutdownInput();
* } catch (IOException ignore) {
* }
* } catch (UnsupportedOperationException ignore) {
* // if one isn't supported, the other one isn't either
* }
* this.socket.close();
* }
* </pre>
*/
System.out.println("second call done");
}
public void testWait() {
waitForMilliseconds(1000 * 1000);
}
}
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org
[jira] Commented: (HTTPCORE-207) SocketHttp*Connection classes can
leak sockets if the connection is half-closed
Posted by "David Koski (JIRA)" <ji...@apache.org>.
[ https://issues.apache.org/jira/browse/HTTPCORE-207?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12759140#action_12759140 ]
David Koski commented on HTTPCORE-207:
--------------------------------------
sock.close() moves into a finally block which looks good to me.
> SocketHttp*Connection classes can leak sockets if the connection is half-closed
> -------------------------------------------------------------------------------
>
> Key: HTTPCORE-207
> URL: https://issues.apache.org/jira/browse/HTTPCORE-207
> Project: HttpComponents HttpCore
> Issue Type: Bug
> Components: HttpCore
> Affects Versions: 4.0.1
> Environment: Mac OS X 10.6.1
> gorgatron% java -version
> java version "1.6.0_15"
> Java(TM) SE Runtime Environment (build 1.6.0_15-b03-219)
> Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02-90, mixed mode)
> Reporter: David Koski
> Attachments: HTTPCORE-207.patch
>
>
> Unit test below. Basically what happens is this:
> * turn off stale check
> * GET request to X
> * wait for the connection to go to CLOSE_WAIT from idle
> * wait for the connection to go to CLOSED
> * GET request to X
> * tries that request
> * throws IOException
> * calls connection.close()
> * close() sets the open = false
> * calls doFlush()
> * that throws
> * caught and close is called again
> * but open = false, so it returns -- the Socket object is never closed
> Unit test below with proposed fix (hack in style, but you get the picture). I use a connection:close to make it run in less than 10 minutes :-)
> After it gets to:
> System.out.println("second call done");
> You can do something like this:
> gorgatron% lsof | grep java | grep TCP
> If you see two connections to www.apple.com:80, one CLOSED, that is the bug.
> import java.io.IOException;
> import java.lang.reflect.Field;
> import java.net.URI;
> import java.util.Arrays;
> import org.apache.http.HttpResponse;
> import org.apache.http.client.ClientProtocolException;
> import org.apache.http.client.ResponseHandler;
> import org.apache.http.client.methods.HttpGet;
> import org.apache.http.client.protocol.RequestAddCookies;
> import org.apache.http.client.protocol.RequestClientConnControl;
> import org.apache.http.client.protocol.RequestDefaultHeaders;
> import org.apache.http.client.protocol.RequestProxyAuthentication;
> import org.apache.http.client.protocol.RequestTargetAuthentication;
> import org.apache.http.client.protocol.ResponseProcessCookies;
> import org.apache.http.conn.ClientConnectionOperator;
> import org.apache.http.conn.OperatedClientConnection;
> import org.apache.http.conn.scheme.PlainSocketFactory;
> import org.apache.http.conn.scheme.Scheme;
> import org.apache.http.conn.scheme.SchemeRegistry;
> import org.apache.http.impl.SocketHttpClientConnection;
> import org.apache.http.impl.client.DefaultHttpClient;
> import org.apache.http.impl.conn.DefaultClientConnection;
> import org.apache.http.impl.conn.DefaultClientConnectionOperator;
> import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
> import org.apache.http.params.BasicHttpParams;
> import org.apache.http.params.HttpConnectionParams;
> import org.apache.http.params.HttpParams;
> import org.apache.http.protocol.BasicHttpProcessor;
> import org.apache.http.protocol.RequestContent;
> import org.apache.http.protocol.RequestExpectContinue;
> import org.apache.http.protocol.RequestTargetHost;
> import org.apache.http.protocol.RequestUserAgent;
> import org.apache.log4j.Level;
> import org.apache.log4j.Logger;
> public class LeakTest extends TestCase {
> public void testLeak() throws Exception {
> Logger.getLogger("org.apache.http.impl.client.DefaultRequestDirector").setLevel(Level.DEBUG);
> /*
> * Trigger #1: turn off stale check.
> */
> final HttpParams params = new BasicHttpParams();
> HttpConnectionParams.setStaleCheckingEnabled(params, false);
> final SchemeRegistry schemeRegistry = new SchemeRegistry();
> schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
> final Field f = SocketHttpClientConnection.class.getDeclaredField("open");
> f.setAccessible(true);
> final ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(params, schemeRegistry) {
> @Override
> protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) {
> return new DefaultClientConnectionOperator(schreg) {
> @Override
> public OperatedClientConnection createConnection() {
> return new DefaultClientConnection() {
> @Override
> public void close() throws IOException {
> if (true) {
> // this case will fail
> super.close();
> } else {
> // this is the proposed fix
> if (!isOpen()) {
> return;
> }
> try {
> f.set(this, false);
> } catch (final Exception e) {
> // eat it
> }
> try {
> doFlush();
> try {
> try {
> getSocket().shutdownOutput();
> } catch (final IOException ignore) {
> }
> try {
> getSocket().shutdownInput();
> } catch (final IOException ignore) {
> }
> } catch (final UnsupportedOperationException ignore) {
> // if one isn't supported, the other one isn't either
> }
> } finally {
> getSocket().close();
> }
> }
> }
> };
> }
> };
> }
> };
> final DefaultHttpClient client = new DefaultHttpClient(connectionManager, params);
> final HttpGet method = new HttpGet(new URI("http://www.apple.com"));
> /*
> * Trigger #2: tell it connection: close. This is talking to a netscaler and it responds
> * with:
> *
> * connection: keep-alive
> *
> * but it actually closes the connection. I am not sure this is very important -- if we do
> * not have this we would have to wait for the connection to go idle and get closed on the
> * remote side.
> *
> * This command shows an example of this behavior:
> *
> * curl -v -k -H connection:close "http://www.apple.com" "http://www.apple.com"
> */
> method.setHeader("connection", "close");
> client.execute(method, new ResponseHandler<Object>() {
> public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
> System.out.println("First request headers = " + Arrays.toString(response.getAllHeaders()));
> return null;
> }
> });
> System.out.println("waiting...");
> /*
> * At this point the connection is in CLOSE_WAIT:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSE_WAIT)
> *
> * Now wait for it to go into CLOSED.
> */
> waitForMilliseconds(1000 * 65);
> /*
> * Now that connection is CLOSED:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSED)
> *
> * Execute the next request.
> */
> client.execute(method, new ResponseHandler<Object>() {
> public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
> System.out.println("Second request headers = " + Arrays.toString(response.getAllHeaders()));
> return null;
> }
> });
> /**
> * And we seem to have leaked the first connection (at this point perhaps not much more than
> * an fd) and the second connection is going into CLOSE_WAIT:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSED)
> *
> * java 15681 dkoski 69u IPv6 0x117a11f0 0t0 TCP [::x.x.x.x]:53936->[::x.x.x.x]:8501
> * (CLOSE_WAIT)
> *
> * What happens inside DefaultRequestDirector:
> *
> * throws an IOException (broken pipe) -- right
> *
> * org.apache.http.impl.conn.tsccm.BasicPooledConnAdapter@3414a97b -> close()
> *
> * org.apache.http.impl.conn.DefaultClientConnection@70d9cbcb -> close()
> *
> * open = false
> *
> * SocketHttpClientConnection has:
> *
> * if (!this.open) { return;
> *
> * however, socket.closed = false (and truly it has not been closed)
> *
> * The reason it is not closed is:
> *
> * <pre>
> * java.net.SocketException: Broken pipe
> * at java.net.SocketOutputStream.socketWrite0(Native Method)
> * at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
> * at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
> * at org.apache.http.impl.io.AbstractSessionOutputBuffer.flushBuffer(AbstractSessionOutputBuffer.java:106)
> * at org.apache.http.impl.io.AbstractSessionOutputBuffer.flush(AbstractSessionOutputBuffer.java:113)
> * at org.apache.http.impl.AbstractHttpClientConnection.doFlush(AbstractHttpClientConnection.java:260)
> * at org.apache.http.impl.AbstractHttpClientConnection.flush(AbstractHttpClientConnection.java:265)
> * at org.apache.http.impl.conn.AbstractClientConnAdapter.flush(AbstractClientConnAdapter.java:197)
> * at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:252)
> * at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
> * at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:447)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:730)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:708)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:699)
> * </pre>
> *
> * the code at fault seems to be:
> *
> * <pre>
> *
> * public void close() throws IOException {
> * if (!this.open) {
> * return;
> * }
> * this.open = false;
> * doFlush(); HERE: throws here and leaves the open = false but doesn't actually close it
> *
> * try {
> * try {
> * this.socket.shutdownOutput();
> * } catch (IOException ignore) {
> * }
> * try {
> * this.socket.shutdownInput();
> * } catch (IOException ignore) {
> * }
> * } catch (UnsupportedOperationException ignore) {
> * // if one isn't supported, the other one isn't either
> * }
> * this.socket.close();
> * }
> * </pre>
> */
> System.out.println("second call done");
> }
> public void testWait() {
> waitForMilliseconds(1000 * 1000);
> }
> }
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org
[jira] Updated: (HTTPCORE-207) SocketHttp*Connection classes can
leak sockets if the connection is half-closed
Posted by "Oleg Kalnichevski (JIRA)" <ji...@apache.org>.
[ https://issues.apache.org/jira/browse/HTTPCORE-207?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Oleg Kalnichevski updated HTTPCORE-207:
---------------------------------------
Summary: SocketHttp*Connection classes can leak sockets if the connection is half-closed (was: DefaultClientConnection can leaked CLOSED connection)
> SocketHttp*Connection classes can leak sockets if the connection is half-closed
> -------------------------------------------------------------------------------
>
> Key: HTTPCORE-207
> URL: https://issues.apache.org/jira/browse/HTTPCORE-207
> Project: HttpComponents HttpCore
> Issue Type: Bug
> Components: HttpCore
> Affects Versions: 4.0.1
> Environment: Mac OS X 10.6.1
> gorgatron% java -version
> java version "1.6.0_15"
> Java(TM) SE Runtime Environment (build 1.6.0_15-b03-219)
> Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02-90, mixed mode)
> Reporter: David Koski
> Attachments: HTTPCORE-207.patch
>
>
> Unit test below. Basically what happens is this:
> * turn off stale check
> * GET request to X
> * wait for the connection to go to CLOSE_WAIT from idle
> * wait for the connection to go to CLOSED
> * GET request to X
> * tries that request
> * throws IOException
> * calls connection.close()
> * close() sets the open = false
> * calls doFlush()
> * that throws
> * caught and close is called again
> * but open = false, so it returns -- the Socket object is never closed
> Unit test below with proposed fix (hack in style, but you get the picture). I use a connection:close to make it run in less than 10 minutes :-)
> After it gets to:
> System.out.println("second call done");
> You can do something like this:
> gorgatron% lsof | grep java | grep TCP
> If you see two connections to www.apple.com:80, one CLOSED, that is the bug.
> import java.io.IOException;
> import java.lang.reflect.Field;
> import java.net.URI;
> import java.util.Arrays;
> import org.apache.http.HttpResponse;
> import org.apache.http.client.ClientProtocolException;
> import org.apache.http.client.ResponseHandler;
> import org.apache.http.client.methods.HttpGet;
> import org.apache.http.client.protocol.RequestAddCookies;
> import org.apache.http.client.protocol.RequestClientConnControl;
> import org.apache.http.client.protocol.RequestDefaultHeaders;
> import org.apache.http.client.protocol.RequestProxyAuthentication;
> import org.apache.http.client.protocol.RequestTargetAuthentication;
> import org.apache.http.client.protocol.ResponseProcessCookies;
> import org.apache.http.conn.ClientConnectionOperator;
> import org.apache.http.conn.OperatedClientConnection;
> import org.apache.http.conn.scheme.PlainSocketFactory;
> import org.apache.http.conn.scheme.Scheme;
> import org.apache.http.conn.scheme.SchemeRegistry;
> import org.apache.http.impl.SocketHttpClientConnection;
> import org.apache.http.impl.client.DefaultHttpClient;
> import org.apache.http.impl.conn.DefaultClientConnection;
> import org.apache.http.impl.conn.DefaultClientConnectionOperator;
> import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
> import org.apache.http.params.BasicHttpParams;
> import org.apache.http.params.HttpConnectionParams;
> import org.apache.http.params.HttpParams;
> import org.apache.http.protocol.BasicHttpProcessor;
> import org.apache.http.protocol.RequestContent;
> import org.apache.http.protocol.RequestExpectContinue;
> import org.apache.http.protocol.RequestTargetHost;
> import org.apache.http.protocol.RequestUserAgent;
> import org.apache.log4j.Level;
> import org.apache.log4j.Logger;
> public class LeakTest extends TestCase {
> public void testLeak() throws Exception {
> Logger.getLogger("org.apache.http.impl.client.DefaultRequestDirector").setLevel(Level.DEBUG);
> /*
> * Trigger #1: turn off stale check.
> */
> final HttpParams params = new BasicHttpParams();
> HttpConnectionParams.setStaleCheckingEnabled(params, false);
> final SchemeRegistry schemeRegistry = new SchemeRegistry();
> schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
> final Field f = SocketHttpClientConnection.class.getDeclaredField("open");
> f.setAccessible(true);
> final ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(params, schemeRegistry) {
> @Override
> protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) {
> return new DefaultClientConnectionOperator(schreg) {
> @Override
> public OperatedClientConnection createConnection() {
> return new DefaultClientConnection() {
> @Override
> public void close() throws IOException {
> if (true) {
> // this case will fail
> super.close();
> } else {
> // this is the proposed fix
> if (!isOpen()) {
> return;
> }
> try {
> f.set(this, false);
> } catch (final Exception e) {
> // eat it
> }
> try {
> doFlush();
> try {
> try {
> getSocket().shutdownOutput();
> } catch (final IOException ignore) {
> }
> try {
> getSocket().shutdownInput();
> } catch (final IOException ignore) {
> }
> } catch (final UnsupportedOperationException ignore) {
> // if one isn't supported, the other one isn't either
> }
> } finally {
> getSocket().close();
> }
> }
> }
> };
> }
> };
> }
> };
> final DefaultHttpClient client = new DefaultHttpClient(connectionManager, params);
> final HttpGet method = new HttpGet(new URI("http://www.apple.com"));
> /*
> * Trigger #2: tell it connection: close. This is talking to a netscaler and it responds
> * with:
> *
> * connection: keep-alive
> *
> * but it actually closes the connection. I am not sure this is very important -- if we do
> * not have this we would have to wait for the connection to go idle and get closed on the
> * remote side.
> *
> * This command shows an example of this behavior:
> *
> * curl -v -k -H connection:close "http://www.apple.com" "http://www.apple.com"
> */
> method.setHeader("connection", "close");
> client.execute(method, new ResponseHandler<Object>() {
> public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
> System.out.println("First request headers = " + Arrays.toString(response.getAllHeaders()));
> return null;
> }
> });
> System.out.println("waiting...");
> /*
> * At this point the connection is in CLOSE_WAIT:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSE_WAIT)
> *
> * Now wait for it to go into CLOSED.
> */
> waitForMilliseconds(1000 * 65);
> /*
> * Now that connection is CLOSED:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSED)
> *
> * Execute the next request.
> */
> client.execute(method, new ResponseHandler<Object>() {
> public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
> System.out.println("Second request headers = " + Arrays.toString(response.getAllHeaders()));
> return null;
> }
> });
> /**
> * And we seem to have leaked the first connection (at this point perhaps not much more than
> * an fd) and the second connection is going into CLOSE_WAIT:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSED)
> *
> * java 15681 dkoski 69u IPv6 0x117a11f0 0t0 TCP [::x.x.x.x]:53936->[::x.x.x.x]:8501
> * (CLOSE_WAIT)
> *
> * What happens inside DefaultRequestDirector:
> *
> * throws an IOException (broken pipe) -- right
> *
> * org.apache.http.impl.conn.tsccm.BasicPooledConnAdapter@3414a97b -> close()
> *
> * org.apache.http.impl.conn.DefaultClientConnection@70d9cbcb -> close()
> *
> * open = false
> *
> * SocketHttpClientConnection has:
> *
> * if (!this.open) { return;
> *
> * however, socket.closed = false (and truly it has not been closed)
> *
> * The reason it is not closed is:
> *
> * <pre>
> * java.net.SocketException: Broken pipe
> * at java.net.SocketOutputStream.socketWrite0(Native Method)
> * at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
> * at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
> * at org.apache.http.impl.io.AbstractSessionOutputBuffer.flushBuffer(AbstractSessionOutputBuffer.java:106)
> * at org.apache.http.impl.io.AbstractSessionOutputBuffer.flush(AbstractSessionOutputBuffer.java:113)
> * at org.apache.http.impl.AbstractHttpClientConnection.doFlush(AbstractHttpClientConnection.java:260)
> * at org.apache.http.impl.AbstractHttpClientConnection.flush(AbstractHttpClientConnection.java:265)
> * at org.apache.http.impl.conn.AbstractClientConnAdapter.flush(AbstractClientConnAdapter.java:197)
> * at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:252)
> * at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
> * at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:447)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:730)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:708)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:699)
> * </pre>
> *
> * the code at fault seems to be:
> *
> * <pre>
> *
> * public void close() throws IOException {
> * if (!this.open) {
> * return;
> * }
> * this.open = false;
> * doFlush(); HERE: throws here and leaves the open = false but doesn't actually close it
> *
> * try {
> * try {
> * this.socket.shutdownOutput();
> * } catch (IOException ignore) {
> * }
> * try {
> * this.socket.shutdownInput();
> * } catch (IOException ignore) {
> * }
> * } catch (UnsupportedOperationException ignore) {
> * // if one isn't supported, the other one isn't either
> * }
> * this.socket.close();
> * }
> * </pre>
> */
> System.out.println("second call done");
> }
> public void testWait() {
> waitForMilliseconds(1000 * 1000);
> }
> }
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org
[jira] Updated: (HTTPCORE-207) SocketHttp*Connection classes can
leak sockets if the connection is half-closed
Posted by "Oleg Kalnichevski (JIRA)" <ji...@apache.org>.
[ https://issues.apache.org/jira/browse/HTTPCORE-207?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Oleg Kalnichevski updated HTTPCORE-207:
---------------------------------------
Attachment: HTTPCORE-207.patch
Good catch, David. Please review / test the patch.
Oleg
> SocketHttp*Connection classes can leak sockets if the connection is half-closed
> -------------------------------------------------------------------------------
>
> Key: HTTPCORE-207
> URL: https://issues.apache.org/jira/browse/HTTPCORE-207
> Project: HttpComponents HttpCore
> Issue Type: Bug
> Components: HttpCore
> Affects Versions: 4.0.1
> Environment: Mac OS X 10.6.1
> gorgatron% java -version
> java version "1.6.0_15"
> Java(TM) SE Runtime Environment (build 1.6.0_15-b03-219)
> Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02-90, mixed mode)
> Reporter: David Koski
> Attachments: HTTPCORE-207.patch
>
>
> Unit test below. Basically what happens is this:
> * turn off stale check
> * GET request to X
> * wait for the connection to go to CLOSE_WAIT from idle
> * wait for the connection to go to CLOSED
> * GET request to X
> * tries that request
> * throws IOException
> * calls connection.close()
> * close() sets the open = false
> * calls doFlush()
> * that throws
> * caught and close is called again
> * but open = false, so it returns -- the Socket object is never closed
> Unit test below with proposed fix (hack in style, but you get the picture). I use a connection:close to make it run in less than 10 minutes :-)
> After it gets to:
> System.out.println("second call done");
> You can do something like this:
> gorgatron% lsof | grep java | grep TCP
> If you see two connections to www.apple.com:80, one CLOSED, that is the bug.
> import java.io.IOException;
> import java.lang.reflect.Field;
> import java.net.URI;
> import java.util.Arrays;
> import org.apache.http.HttpResponse;
> import org.apache.http.client.ClientProtocolException;
> import org.apache.http.client.ResponseHandler;
> import org.apache.http.client.methods.HttpGet;
> import org.apache.http.client.protocol.RequestAddCookies;
> import org.apache.http.client.protocol.RequestClientConnControl;
> import org.apache.http.client.protocol.RequestDefaultHeaders;
> import org.apache.http.client.protocol.RequestProxyAuthentication;
> import org.apache.http.client.protocol.RequestTargetAuthentication;
> import org.apache.http.client.protocol.ResponseProcessCookies;
> import org.apache.http.conn.ClientConnectionOperator;
> import org.apache.http.conn.OperatedClientConnection;
> import org.apache.http.conn.scheme.PlainSocketFactory;
> import org.apache.http.conn.scheme.Scheme;
> import org.apache.http.conn.scheme.SchemeRegistry;
> import org.apache.http.impl.SocketHttpClientConnection;
> import org.apache.http.impl.client.DefaultHttpClient;
> import org.apache.http.impl.conn.DefaultClientConnection;
> import org.apache.http.impl.conn.DefaultClientConnectionOperator;
> import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
> import org.apache.http.params.BasicHttpParams;
> import org.apache.http.params.HttpConnectionParams;
> import org.apache.http.params.HttpParams;
> import org.apache.http.protocol.BasicHttpProcessor;
> import org.apache.http.protocol.RequestContent;
> import org.apache.http.protocol.RequestExpectContinue;
> import org.apache.http.protocol.RequestTargetHost;
> import org.apache.http.protocol.RequestUserAgent;
> import org.apache.log4j.Level;
> import org.apache.log4j.Logger;
> public class LeakTest extends TestCase {
> public void testLeak() throws Exception {
> Logger.getLogger("org.apache.http.impl.client.DefaultRequestDirector").setLevel(Level.DEBUG);
> /*
> * Trigger #1: turn off stale check.
> */
> final HttpParams params = new BasicHttpParams();
> HttpConnectionParams.setStaleCheckingEnabled(params, false);
> final SchemeRegistry schemeRegistry = new SchemeRegistry();
> schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
> final Field f = SocketHttpClientConnection.class.getDeclaredField("open");
> f.setAccessible(true);
> final ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(params, schemeRegistry) {
> @Override
> protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) {
> return new DefaultClientConnectionOperator(schreg) {
> @Override
> public OperatedClientConnection createConnection() {
> return new DefaultClientConnection() {
> @Override
> public void close() throws IOException {
> if (true) {
> // this case will fail
> super.close();
> } else {
> // this is the proposed fix
> if (!isOpen()) {
> return;
> }
> try {
> f.set(this, false);
> } catch (final Exception e) {
> // eat it
> }
> try {
> doFlush();
> try {
> try {
> getSocket().shutdownOutput();
> } catch (final IOException ignore) {
> }
> try {
> getSocket().shutdownInput();
> } catch (final IOException ignore) {
> }
> } catch (final UnsupportedOperationException ignore) {
> // if one isn't supported, the other one isn't either
> }
> } finally {
> getSocket().close();
> }
> }
> }
> };
> }
> };
> }
> };
> final DefaultHttpClient client = new DefaultHttpClient(connectionManager, params);
> final HttpGet method = new HttpGet(new URI("http://www.apple.com"));
> /*
> * Trigger #2: tell it connection: close. This is talking to a netscaler and it responds
> * with:
> *
> * connection: keep-alive
> *
> * but it actually closes the connection. I am not sure this is very important -- if we do
> * not have this we would have to wait for the connection to go idle and get closed on the
> * remote side.
> *
> * This command shows an example of this behavior:
> *
> * curl -v -k -H connection:close "http://www.apple.com" "http://www.apple.com"
> */
> method.setHeader("connection", "close");
> client.execute(method, new ResponseHandler<Object>() {
> public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
> System.out.println("First request headers = " + Arrays.toString(response.getAllHeaders()));
> return null;
> }
> });
> System.out.println("waiting...");
> /*
> * At this point the connection is in CLOSE_WAIT:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSE_WAIT)
> *
> * Now wait for it to go into CLOSED.
> */
> waitForMilliseconds(1000 * 65);
> /*
> * Now that connection is CLOSED:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSED)
> *
> * Execute the next request.
> */
> client.execute(method, new ResponseHandler<Object>() {
> public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
> System.out.println("Second request headers = " + Arrays.toString(response.getAllHeaders()));
> return null;
> }
> });
> /**
> * And we seem to have leaked the first connection (at this point perhaps not much more than
> * an fd) and the second connection is going into CLOSE_WAIT:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSED)
> *
> * java 15681 dkoski 69u IPv6 0x117a11f0 0t0 TCP [::x.x.x.x]:53936->[::x.x.x.x]:8501
> * (CLOSE_WAIT)
> *
> * What happens inside DefaultRequestDirector:
> *
> * throws an IOException (broken pipe) -- right
> *
> * org.apache.http.impl.conn.tsccm.BasicPooledConnAdapter@3414a97b -> close()
> *
> * org.apache.http.impl.conn.DefaultClientConnection@70d9cbcb -> close()
> *
> * open = false
> *
> * SocketHttpClientConnection has:
> *
> * if (!this.open) { return;
> *
> * however, socket.closed = false (and truly it has not been closed)
> *
> * The reason it is not closed is:
> *
> * <pre>
> * java.net.SocketException: Broken pipe
> * at java.net.SocketOutputStream.socketWrite0(Native Method)
> * at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
> * at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
> * at org.apache.http.impl.io.AbstractSessionOutputBuffer.flushBuffer(AbstractSessionOutputBuffer.java:106)
> * at org.apache.http.impl.io.AbstractSessionOutputBuffer.flush(AbstractSessionOutputBuffer.java:113)
> * at org.apache.http.impl.AbstractHttpClientConnection.doFlush(AbstractHttpClientConnection.java:260)
> * at org.apache.http.impl.AbstractHttpClientConnection.flush(AbstractHttpClientConnection.java:265)
> * at org.apache.http.impl.conn.AbstractClientConnAdapter.flush(AbstractClientConnAdapter.java:197)
> * at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:252)
> * at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
> * at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:447)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:730)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:708)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:699)
> * </pre>
> *
> * the code at fault seems to be:
> *
> * <pre>
> *
> * public void close() throws IOException {
> * if (!this.open) {
> * return;
> * }
> * this.open = false;
> * doFlush(); HERE: throws here and leaves the open = false but doesn't actually close it
> *
> * try {
> * try {
> * this.socket.shutdownOutput();
> * } catch (IOException ignore) {
> * }
> * try {
> * this.socket.shutdownInput();
> * } catch (IOException ignore) {
> * }
> * } catch (UnsupportedOperationException ignore) {
> * // if one isn't supported, the other one isn't either
> * }
> * this.socket.close();
> * }
> * </pre>
> */
> System.out.println("second call done");
> }
> public void testWait() {
> waitForMilliseconds(1000 * 1000);
> }
> }
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org
[jira] Resolved: (HTTPCORE-207) SocketHttp*Connection classes can
leak sockets if the connection is half-closed
Posted by "Oleg Kalnichevski (JIRA)" <ji...@apache.org>.
[ https://issues.apache.org/jira/browse/HTTPCORE-207?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Oleg Kalnichevski resolved HTTPCORE-207.
----------------------------------------
Resolution: Fixed
Fix Version/s: 4.1-beta1
4.0.2
Committed to trunk and 4.0.x branch.
Oleg
> SocketHttp*Connection classes can leak sockets if the connection is half-closed
> -------------------------------------------------------------------------------
>
> Key: HTTPCORE-207
> URL: https://issues.apache.org/jira/browse/HTTPCORE-207
> Project: HttpComponents HttpCore
> Issue Type: Bug
> Components: HttpCore
> Affects Versions: 4.0.1
> Environment: Mac OS X 10.6.1
> gorgatron% java -version
> java version "1.6.0_15"
> Java(TM) SE Runtime Environment (build 1.6.0_15-b03-219)
> Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02-90, mixed mode)
> Reporter: David Koski
> Fix For: 4.0.2, 4.1-beta1
>
> Attachments: HTTPCORE-207.patch
>
>
> Unit test below. Basically what happens is this:
> * turn off stale check
> * GET request to X
> * wait for the connection to go to CLOSE_WAIT from idle
> * wait for the connection to go to CLOSED
> * GET request to X
> * tries that request
> * throws IOException
> * calls connection.close()
> * close() sets the open = false
> * calls doFlush()
> * that throws
> * caught and close is called again
> * but open = false, so it returns -- the Socket object is never closed
> Unit test below with proposed fix (hack in style, but you get the picture). I use a connection:close to make it run in less than 10 minutes :-)
> After it gets to:
> System.out.println("second call done");
> You can do something like this:
> gorgatron% lsof | grep java | grep TCP
> If you see two connections to www.apple.com:80, one CLOSED, that is the bug.
> import java.io.IOException;
> import java.lang.reflect.Field;
> import java.net.URI;
> import java.util.Arrays;
> import org.apache.http.HttpResponse;
> import org.apache.http.client.ClientProtocolException;
> import org.apache.http.client.ResponseHandler;
> import org.apache.http.client.methods.HttpGet;
> import org.apache.http.client.protocol.RequestAddCookies;
> import org.apache.http.client.protocol.RequestClientConnControl;
> import org.apache.http.client.protocol.RequestDefaultHeaders;
> import org.apache.http.client.protocol.RequestProxyAuthentication;
> import org.apache.http.client.protocol.RequestTargetAuthentication;
> import org.apache.http.client.protocol.ResponseProcessCookies;
> import org.apache.http.conn.ClientConnectionOperator;
> import org.apache.http.conn.OperatedClientConnection;
> import org.apache.http.conn.scheme.PlainSocketFactory;
> import org.apache.http.conn.scheme.Scheme;
> import org.apache.http.conn.scheme.SchemeRegistry;
> import org.apache.http.impl.SocketHttpClientConnection;
> import org.apache.http.impl.client.DefaultHttpClient;
> import org.apache.http.impl.conn.DefaultClientConnection;
> import org.apache.http.impl.conn.DefaultClientConnectionOperator;
> import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
> import org.apache.http.params.BasicHttpParams;
> import org.apache.http.params.HttpConnectionParams;
> import org.apache.http.params.HttpParams;
> import org.apache.http.protocol.BasicHttpProcessor;
> import org.apache.http.protocol.RequestContent;
> import org.apache.http.protocol.RequestExpectContinue;
> import org.apache.http.protocol.RequestTargetHost;
> import org.apache.http.protocol.RequestUserAgent;
> import org.apache.log4j.Level;
> import org.apache.log4j.Logger;
> public class LeakTest extends TestCase {
> public void testLeak() throws Exception {
> Logger.getLogger("org.apache.http.impl.client.DefaultRequestDirector").setLevel(Level.DEBUG);
> /*
> * Trigger #1: turn off stale check.
> */
> final HttpParams params = new BasicHttpParams();
> HttpConnectionParams.setStaleCheckingEnabled(params, false);
> final SchemeRegistry schemeRegistry = new SchemeRegistry();
> schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
> final Field f = SocketHttpClientConnection.class.getDeclaredField("open");
> f.setAccessible(true);
> final ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(params, schemeRegistry) {
> @Override
> protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) {
> return new DefaultClientConnectionOperator(schreg) {
> @Override
> public OperatedClientConnection createConnection() {
> return new DefaultClientConnection() {
> @Override
> public void close() throws IOException {
> if (true) {
> // this case will fail
> super.close();
> } else {
> // this is the proposed fix
> if (!isOpen()) {
> return;
> }
> try {
> f.set(this, false);
> } catch (final Exception e) {
> // eat it
> }
> try {
> doFlush();
> try {
> try {
> getSocket().shutdownOutput();
> } catch (final IOException ignore) {
> }
> try {
> getSocket().shutdownInput();
> } catch (final IOException ignore) {
> }
> } catch (final UnsupportedOperationException ignore) {
> // if one isn't supported, the other one isn't either
> }
> } finally {
> getSocket().close();
> }
> }
> }
> };
> }
> };
> }
> };
> final DefaultHttpClient client = new DefaultHttpClient(connectionManager, params);
> final HttpGet method = new HttpGet(new URI("http://www.apple.com"));
> /*
> * Trigger #2: tell it connection: close. This is talking to a netscaler and it responds
> * with:
> *
> * connection: keep-alive
> *
> * but it actually closes the connection. I am not sure this is very important -- if we do
> * not have this we would have to wait for the connection to go idle and get closed on the
> * remote side.
> *
> * This command shows an example of this behavior:
> *
> * curl -v -k -H connection:close "http://www.apple.com" "http://www.apple.com"
> */
> method.setHeader("connection", "close");
> client.execute(method, new ResponseHandler<Object>() {
> public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
> System.out.println("First request headers = " + Arrays.toString(response.getAllHeaders()));
> return null;
> }
> });
> System.out.println("waiting...");
> /*
> * At this point the connection is in CLOSE_WAIT:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSE_WAIT)
> *
> * Now wait for it to go into CLOSED.
> */
> waitForMilliseconds(1000 * 65);
> /*
> * Now that connection is CLOSED:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSED)
> *
> * Execute the next request.
> */
> client.execute(method, new ResponseHandler<Object>() {
> public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
> System.out.println("Second request headers = " + Arrays.toString(response.getAllHeaders()));
> return null;
> }
> });
> /**
> * And we seem to have leaked the first connection (at this point perhaps not much more than
> * an fd) and the second connection is going into CLOSE_WAIT:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSED)
> *
> * java 15681 dkoski 69u IPv6 0x117a11f0 0t0 TCP [::x.x.x.x]:53936->[::x.x.x.x]:8501
> * (CLOSE_WAIT)
> *
> * What happens inside DefaultRequestDirector:
> *
> * throws an IOException (broken pipe) -- right
> *
> * org.apache.http.impl.conn.tsccm.BasicPooledConnAdapter@3414a97b -> close()
> *
> * org.apache.http.impl.conn.DefaultClientConnection@70d9cbcb -> close()
> *
> * open = false
> *
> * SocketHttpClientConnection has:
> *
> * if (!this.open) { return;
> *
> * however, socket.closed = false (and truly it has not been closed)
> *
> * The reason it is not closed is:
> *
> * <pre>
> * java.net.SocketException: Broken pipe
> * at java.net.SocketOutputStream.socketWrite0(Native Method)
> * at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
> * at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
> * at org.apache.http.impl.io.AbstractSessionOutputBuffer.flushBuffer(AbstractSessionOutputBuffer.java:106)
> * at org.apache.http.impl.io.AbstractSessionOutputBuffer.flush(AbstractSessionOutputBuffer.java:113)
> * at org.apache.http.impl.AbstractHttpClientConnection.doFlush(AbstractHttpClientConnection.java:260)
> * at org.apache.http.impl.AbstractHttpClientConnection.flush(AbstractHttpClientConnection.java:265)
> * at org.apache.http.impl.conn.AbstractClientConnAdapter.flush(AbstractClientConnAdapter.java:197)
> * at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:252)
> * at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
> * at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:447)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:730)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:708)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:699)
> * </pre>
> *
> * the code at fault seems to be:
> *
> * <pre>
> *
> * public void close() throws IOException {
> * if (!this.open) {
> * return;
> * }
> * this.open = false;
> * doFlush(); HERE: throws here and leaves the open = false but doesn't actually close it
> *
> * try {
> * try {
> * this.socket.shutdownOutput();
> * } catch (IOException ignore) {
> * }
> * try {
> * this.socket.shutdownInput();
> * } catch (IOException ignore) {
> * }
> * } catch (UnsupportedOperationException ignore) {
> * // if one isn't supported, the other one isn't either
> * }
> * this.socket.close();
> * }
> * </pre>
> */
> System.out.println("second call done");
> }
> public void testWait() {
> waitForMilliseconds(1000 * 1000);
> }
> }
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org
[jira] Moved: (HTTPCORE-207) DefaultClientConnection can leaked
CLOSED connection
Posted by "Oleg Kalnichevski (JIRA)" <ji...@apache.org>.
[ https://issues.apache.org/jira/browse/HTTPCORE-207?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Oleg Kalnichevski moved HTTPCLIENT-879 to HTTPCORE-207:
-------------------------------------------------------
Component/s: (was: HttpClient)
HttpCore
Affects Version/s: (was: 4.0.1)
4.0.1
Key: HTTPCORE-207 (was: HTTPCLIENT-879)
Project: HttpComponents HttpCore (was: HttpComponents HttpClient)
> DefaultClientConnection can leaked CLOSED connection
> ----------------------------------------------------
>
> Key: HTTPCORE-207
> URL: https://issues.apache.org/jira/browse/HTTPCORE-207
> Project: HttpComponents HttpCore
> Issue Type: Bug
> Components: HttpCore
> Affects Versions: 4.0.1
> Environment: Mac OS X 10.6.1
> gorgatron% java -version
> java version "1.6.0_15"
> Java(TM) SE Runtime Environment (build 1.6.0_15-b03-219)
> Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02-90, mixed mode)
> Reporter: David Koski
>
> Unit test below. Basically what happens is this:
> * turn off stale check
> * GET request to X
> * wait for the connection to go to CLOSE_WAIT from idle
> * wait for the connection to go to CLOSED
> * GET request to X
> * tries that request
> * throws IOException
> * calls connection.close()
> * close() sets the open = false
> * calls doFlush()
> * that throws
> * caught and close is called again
> * but open = false, so it returns -- the Socket object is never closed
> Unit test below with proposed fix (hack in style, but you get the picture). I use a connection:close to make it run in less than 10 minutes :-)
> After it gets to:
> System.out.println("second call done");
> You can do something like this:
> gorgatron% lsof | grep java | grep TCP
> If you see two connections to www.apple.com:80, one CLOSED, that is the bug.
> import java.io.IOException;
> import java.lang.reflect.Field;
> import java.net.URI;
> import java.util.Arrays;
> import org.apache.http.HttpResponse;
> import org.apache.http.client.ClientProtocolException;
> import org.apache.http.client.ResponseHandler;
> import org.apache.http.client.methods.HttpGet;
> import org.apache.http.client.protocol.RequestAddCookies;
> import org.apache.http.client.protocol.RequestClientConnControl;
> import org.apache.http.client.protocol.RequestDefaultHeaders;
> import org.apache.http.client.protocol.RequestProxyAuthentication;
> import org.apache.http.client.protocol.RequestTargetAuthentication;
> import org.apache.http.client.protocol.ResponseProcessCookies;
> import org.apache.http.conn.ClientConnectionOperator;
> import org.apache.http.conn.OperatedClientConnection;
> import org.apache.http.conn.scheme.PlainSocketFactory;
> import org.apache.http.conn.scheme.Scheme;
> import org.apache.http.conn.scheme.SchemeRegistry;
> import org.apache.http.impl.SocketHttpClientConnection;
> import org.apache.http.impl.client.DefaultHttpClient;
> import org.apache.http.impl.conn.DefaultClientConnection;
> import org.apache.http.impl.conn.DefaultClientConnectionOperator;
> import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
> import org.apache.http.params.BasicHttpParams;
> import org.apache.http.params.HttpConnectionParams;
> import org.apache.http.params.HttpParams;
> import org.apache.http.protocol.BasicHttpProcessor;
> import org.apache.http.protocol.RequestContent;
> import org.apache.http.protocol.RequestExpectContinue;
> import org.apache.http.protocol.RequestTargetHost;
> import org.apache.http.protocol.RequestUserAgent;
> import org.apache.log4j.Level;
> import org.apache.log4j.Logger;
> public class LeakTest extends TestCase {
> public void testLeak() throws Exception {
> Logger.getLogger("org.apache.http.impl.client.DefaultRequestDirector").setLevel(Level.DEBUG);
> /*
> * Trigger #1: turn off stale check.
> */
> final HttpParams params = new BasicHttpParams();
> HttpConnectionParams.setStaleCheckingEnabled(params, false);
> final SchemeRegistry schemeRegistry = new SchemeRegistry();
> schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
> final Field f = SocketHttpClientConnection.class.getDeclaredField("open");
> f.setAccessible(true);
> final ThreadSafeClientConnManager connectionManager = new ThreadSafeClientConnManager(params, schemeRegistry) {
> @Override
> protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) {
> return new DefaultClientConnectionOperator(schreg) {
> @Override
> public OperatedClientConnection createConnection() {
> return new DefaultClientConnection() {
> @Override
> public void close() throws IOException {
> if (true) {
> // this case will fail
> super.close();
> } else {
> // this is the proposed fix
> if (!isOpen()) {
> return;
> }
> try {
> f.set(this, false);
> } catch (final Exception e) {
> // eat it
> }
> try {
> doFlush();
> try {
> try {
> getSocket().shutdownOutput();
> } catch (final IOException ignore) {
> }
> try {
> getSocket().shutdownInput();
> } catch (final IOException ignore) {
> }
> } catch (final UnsupportedOperationException ignore) {
> // if one isn't supported, the other one isn't either
> }
> } finally {
> getSocket().close();
> }
> }
> }
> };
> }
> };
> }
> };
> final DefaultHttpClient client = new DefaultHttpClient(connectionManager, params);
> final HttpGet method = new HttpGet(new URI("http://www.apple.com"));
> /*
> * Trigger #2: tell it connection: close. This is talking to a netscaler and it responds
> * with:
> *
> * connection: keep-alive
> *
> * but it actually closes the connection. I am not sure this is very important -- if we do
> * not have this we would have to wait for the connection to go idle and get closed on the
> * remote side.
> *
> * This command shows an example of this behavior:
> *
> * curl -v -k -H connection:close "http://www.apple.com" "http://www.apple.com"
> */
> method.setHeader("connection", "close");
> client.execute(method, new ResponseHandler<Object>() {
> public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
> System.out.println("First request headers = " + Arrays.toString(response.getAllHeaders()));
> return null;
> }
> });
> System.out.println("waiting...");
> /*
> * At this point the connection is in CLOSE_WAIT:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSE_WAIT)
> *
> * Now wait for it to go into CLOSED.
> */
> waitForMilliseconds(1000 * 65);
> /*
> * Now that connection is CLOSED:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSED)
> *
> * Execute the next request.
> */
> client.execute(method, new ResponseHandler<Object>() {
> public Object handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
> System.out.println("Second request headers = " + Arrays.toString(response.getAllHeaders()));
> return null;
> }
> });
> /**
> * And we seem to have leaked the first connection (at this point perhaps not much more than
> * an fd) and the second connection is going into CLOSE_WAIT:
> *
> * java 15681 dkoski 67u IPv6 0x117a1e20 0t0 TCP [::x.x.x.x]:53935->[::x.x.x.x]:8501
> * (CLOSED)
> *
> * java 15681 dkoski 69u IPv6 0x117a11f0 0t0 TCP [::x.x.x.x]:53936->[::x.x.x.x]:8501
> * (CLOSE_WAIT)
> *
> * What happens inside DefaultRequestDirector:
> *
> * throws an IOException (broken pipe) -- right
> *
> * org.apache.http.impl.conn.tsccm.BasicPooledConnAdapter@3414a97b -> close()
> *
> * org.apache.http.impl.conn.DefaultClientConnection@70d9cbcb -> close()
> *
> * open = false
> *
> * SocketHttpClientConnection has:
> *
> * if (!this.open) { return;
> *
> * however, socket.closed = false (and truly it has not been closed)
> *
> * The reason it is not closed is:
> *
> * <pre>
> * java.net.SocketException: Broken pipe
> * at java.net.SocketOutputStream.socketWrite0(Native Method)
> * at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
> * at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
> * at org.apache.http.impl.io.AbstractSessionOutputBuffer.flushBuffer(AbstractSessionOutputBuffer.java:106)
> * at org.apache.http.impl.io.AbstractSessionOutputBuffer.flush(AbstractSessionOutputBuffer.java:113)
> * at org.apache.http.impl.AbstractHttpClientConnection.doFlush(AbstractHttpClientConnection.java:260)
> * at org.apache.http.impl.AbstractHttpClientConnection.flush(AbstractHttpClientConnection.java:265)
> * at org.apache.http.impl.conn.AbstractClientConnAdapter.flush(AbstractClientConnAdapter.java:197)
> * at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:252)
> * at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
> * at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:447)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:730)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:708)
> * at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:699)
> * </pre>
> *
> * the code at fault seems to be:
> *
> * <pre>
> *
> * public void close() throws IOException {
> * if (!this.open) {
> * return;
> * }
> * this.open = false;
> * doFlush(); HERE: throws here and leaves the open = false but doesn't actually close it
> *
> * try {
> * try {
> * this.socket.shutdownOutput();
> * } catch (IOException ignore) {
> * }
> * try {
> * this.socket.shutdownInput();
> * } catch (IOException ignore) {
> * }
> * } catch (UnsupportedOperationException ignore) {
> * // if one isn't supported, the other one isn't either
> * }
> * this.socket.close();
> * }
> * </pre>
> */
> System.out.println("second call done");
> }
> public void testWait() {
> waitForMilliseconds(1000 * 1000);
> }
> }
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org