JDK-8238270 : java.net HTTP/2 client does not decrease stream count when receives 204 response
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 11.0.5
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux_redhat_7.0
  • CPU: x86
  • Submitted: 2020-01-30
  • Updated: 2020-11-12
  • Resolved: 2020-04-20
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 13 JDK 15
11.0.9-oracleFixed 13.0.6Fixed 15 b20Fixed
Description
ADDITIONAL SYSTEM INFORMATION :
REH 7,6, Java 11.0.5

A DESCRIPTION OF THE PROBLEM :
java.net HTTP/2 client does not decrease stream count when receives 204 response and consequently the IOException "too many concurrent streams" will be thrown

It is noted that 11.0.1 works with no problems

REGRESSION : Last worked in version 11

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) Create an HTTP/2 server that returns 204 response
2) Create a client that call the server continuously until an exception is thrown
3) Start the server
4) Start the client
5) Observe the exception when the calls reaches the limit set by the server during HTTP/2 handshake

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The client to call the server with no problems after reaching the limit
ACTUAL -
The IOException "too many concurrent streams" will be thrown

---------- BEGIN SOURCE ----------
public class Http2clientApplicationTests {

	@Test
	public void test204() throws KeyManagementException, NoSuchAlgorithmException, IOException, InterruptedException {
        // Disable host name and cert verification.
        
        final Properties sysProps = System.getProperties(); 
        sysProps.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());
        sysProps.setProperty("jdk.internal.httpclient.debug",Boolean.TRUE.toString());
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{
        	    new X509TrustManager() {
        	        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
        	            return null;
        	        }
        	        public void checkClientTrusted(
        	            java.security.cert.X509Certificate[] certs, String authType) {
        	        }
        	        public void checkServerTrusted(
        	            java.security.cert.X509Certificate[] certs, String authType) {
        	        }
        	    }
        	}, new SecureRandom());
        
        HttpClient client = HttpClient.newBuilder()
        		.version(Version.HTTP_2)
        		.sslContext(sslContext)
        		.build();
        
        HttpRequest request = HttpRequest.newBuilder()
				.uri(URI.create("https://localhost:8443/"))
				.DELETE()
				.build();
		
        try {
        	while (true) {
	            HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
	    		assertThat(response.statusCode() == 204);        	
        	}
        }
        catch (IOException ex) {
        	assertThat(ex.getMessage().equals("too many concurrent streams"));
        }
		
	}
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
use 11.0.1

FREQUENCY : always

Comments
Fix request (13u) I'd like to backport it to 13u as well. The patch applies without changes. All network tests pass after the fix.
12-11-2020

Fix request (11u) -- will label after testing completed. I would like to downport this for parity with 11.0.9-oracle. Applies clean except for the Copyright in Stream.java, but I had to adapt the bytecode version in the bytecode assembly of a test: http://mail.openjdk.java.net/pipermail/jdk-updates-dev/2020-June/003357.html
29-06-2020

URL: https://hg.openjdk.java.net/jdk/jdk/rev/fd72cd98180e User: dfuchs Date: 2020-04-20 12:25:14 +0000
20-04-2020

The good news is that I have now a test that shows the issue. The bad news is that I can confirm that there is an issue. The streams are not closed. But I see the code that should close them. Something funny is going on alright.
10-04-2020

Tested with JDK 11.0.1 on Oracle Linux: DEBUG: [main] [56ms] HttpClientImpl(1) proxySelector is sun.net.spi.DefaultProxySelector@7920ba90 (user-supplied=false) DEBUG: [main] [208ms] HttpClientImpl(1) ClientImpl (async) send https://localhost:8443/ DELETE DEBUG: [main] [231ms] Exchange establishing exchange for https://localhost:8443/ DELETE, proxy=null DEBUG: [main] [440ms] PlainHttpConnection(?) Initial receive buffer size is: 43690 DEBUG: [main] [472ms] PlainHttpConnection(SocketTube(1)) registering connect event DEBUG: [main] [475ms] ExchangeImpl get: Trying to get HTTP/2 connection DEBUG: [HttpClient-1-SelectorManager] [490ms] SelectorAttachment Registering jdk.internal.net.http.PlainHttpConnection$ConnectEvent@2c7af92f for 8 (true) DEBUG: [HttpClient-1-SelectorManager] [517ms] PlainHttpConnection(SocketTube(1)) ConnectEvent: finishing connect DEBUG: [HttpClient-1-SelectorManager] [579ms] PlainHttpConnection(SocketTube(1)) Closing channel: channel registered with selector, invalid key, sa.interestOps=8 DEBUG: [HttpClient-1-SelectorManager] [580ms] SocketTube(1) got read error: java.io.IOException: connection closed locally DEBUG: [HttpClient-1-SelectorManager] [580ms] SocketTube(1) error raised before subscriber subscribed: java.io.IOException: connection closed locally DEBUG: [HttpClient-1-Worker-0] [595ms] ExchangeImpl handling HTTP/2 connection creation result DEBUG: [HttpClient-1-Worker-0] [596ms] ExchangeImpl handling HTTP/2 connection creation failed: java.util.concurrent.CompletionException: java.net.ConnectException: Connection refused DEBUG: [HttpClient-1-Worker-0] [596ms] ExchangeImpl HTTP/2 connection creation failed with unexpected exception: java.net.ConnectException: Connection refused DEBUG: [HttpClient-1-Worker-0] [598ms] Exchange checkFor407: no response - java.net.ConnectException: Connection refused DEBUG: [HttpClient-1-Worker-0] [678ms] HttpClientImpl(1) ClientImpl (async) elapsed 410 millis for DELETE to https://localhost:8443/ java.lang.AssertionError at Http2clientApplicationTests.test204(Http2clientApplicationTests.java:61) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58) Process finished with exit code 255
31-01-2020