[httpclient] HTTP2: Memory Leak with Proxy
Chris Hegarty
chris.hegarty at oracle.com
Tue Aug 7 16:53:09 UTC 2018
Albert,
I haven’t yet looked at what happens in JDK 10, but just to say, since things have moved on a lot in JDK 11 EA, that the same test runs with a reasonable amount of memory and CPU with JDK 11 EA [1]. I updated the test a little, since some the names have been changed with the standardisation ( see javadoc for more details [2] ).
-Chris.
[1] http://jdk.java.net/11/
[2] https://download.java.net/java/early_access/jdk11/docs/api/java.net.http/java/net/http/package-summary.html
---
$ cat BadProxyLeak.java
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.TimeUnit;
public class BadProxyLeak {
public static void main(String[] args) throws InterruptedException {
// 1st heap dump
// Thread.sleep(10000);
{
HttpClient client = HttpClient.newBuilder()
// malicious proxy?
.proxy(ProxySelector.of(new InetSocketAddress("165.165.248.90", 8080)))
.build();
HttpRequest req = HttpRequest
// target is not relevant
.newBuilder(URI.create("https://www.google.de"))
// leak occurs only with HTTP_2
.version(HttpClient.Version.HTTP_2)
// .version(HttpClient.Version.HTTP_1_1)
.GET()
.build();
// use same client for every request
// most likely happens on the first retry, if not try again or increase i
for (int i = 0; i < 10; i++) {
System.out.println("Request " + i);
// body handler is not relevant
HttpResponse.BodyHandler<?> t = HttpResponse.BodyHandlers.ofString();
try {
// happens with both async and sync send
client.sendAsync(req, t).get(15, TimeUnit.SECONDS);
// client.send(req, handler);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 3rd heap dump
System.out.println("Generating heap dump manually");
// space is not released
Thread.sleep(60000);
}
}
> On 7 Aug 2018, at 17:05, Chris Hegarty <chris.hegarty at oracle.com> wrote:
>
>
>> On 7 Aug 2018, at 16:55, Albert Schimpf <albi646 at gmx.de> wrote:
>>
>> Hi,
>>
>> by bad I mean that this proxy is the only one out of ~1000 proxies which causes this behavior. It's also the only one which causes SSLExceptions (General SSL engine problem) and EOFExceptions. I don't think that particular proxy is a valid proxy. Using curl indicates that this is a SSL handshake problem (https):
>>
>> * Rebuilt URL to: www.google.de/
>> * Trying 165.165.248.90…
>
> Oh, you mean the proxy at that actual IP address. Ok got it.
>
>
>> * TCP_NODELAY set
>> * Connected to 165.165.248.90 (165.165.248.90) port 8080 (#0)
>> * successfully set certificate verify locations:
>> * CAfile: /etc/ssl/certs/ca-certificates.crt
>> CApath: /etc/ssl/certs
>> * TLSv1.2 (OUT), TLS handshake, Client hello (1):
>> * OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to 165.165.248.90:8080
>> * Closing connection 0
>> curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to 165.165.248.90:8080
>>
>> I don't know that much about the protocol itself, so I don't think I can help very much.
>>
>> Thank you for looking into this!
>
>
> -Chris.
>
>
>> Best,
>> Albert
>>
>>
>> On 07.08.2018 12:05, Chris Hegarty wrote:
>>> Hi Albert,
>>>
>>> Very strange indeed. Thanks for reporting it, I’ll investigate.
>>>
>>> What do your mean by “bad proxy”. What is bad about it, and how does it behave?
>>>
>>> -Chris.
>>>
>>>
>>>> On 7 Aug 2018, at 10:25, Albert Schimpf <albi646 at gmx.de> wrote:
>>>>
>>>> Hi,
>>>>
>>>> I stumbled upon some strange behavior when using the new Java httpclient.
>>>>
>>>> The issue is very simple to reproduce. Send a GET request via a known bad proxy:
>>>>
>>>> HttpClient client = HttpClient.newBuilder()
>>>> .proxy(ProxySelector.of(BAD_PROXY))
>>>> .build();
>>>>
>>>> HttpRequest req = HttpRequest
>>>> // target is not relevant
>>>> .newBuilder(...)
>>>> .GET()
>>>> .build();
>>>>
>>>> // body handler is not relevant
>>>> HttpResponse.BodyHandler<?> t = HttpResponse.BodyHandler.asString();
>>>>
>>>> // happens with both async and sync send
>>>> client.sendAsync(req, t).get(30, TimeUnit.SECONDS);
>>>>
>>>> The result is that the heap size increases dramatically (to about 1.5GB) and resources are not released. CPU consumption increases by a constant factor, too. I have tried many variations of the above code, and the only thing which seems to work (i.e. heap size does not explode) is to set the HTTP version to 1.1.
>>>>
>>>> In my main application this leads to both memory and CPU starvation (4GB memory limit, 100% CPU usage). It usually uses only 5% CPU and 200MB memory at worst.
>>>>
>>>> I have attached a working example code with a bad proxy. I uploaded the generated garbage collection log and three heap dumps (before, during, and after the request) to dropbox:
>>>>
>>>> https://www.dropbox.com/s/ulqnmrmgr58rrul/debug.zip
>>>>
>>>> I tried the 10.0.0-openjdk and 10.0.1-zulu version. I can reproduce the issue 100% of times.
>>>>
>>>> Am I doing something wrong? Is this to be expected if one somehow happens to use a bad proxy? If this is to be expected, how can I protect my application against such behavior?
>>>>
>>>>
>>>> Best,
>>>> Albert
>>>> <mon.png><Main.java>
>>
>
More information about the net-dev
mailing list