diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamHandler.java index 6393e5991..68b22c24a 100644 --- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamHandler.java +++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2StreamHandler.java @@ -209,6 +209,9 @@ public void consumeHeader(final List
headers, final boolean endStream) t if (status < HttpStatus.SC_INFORMATIONAL) { throw new ProtocolException("Invalid response: " + new StatusLine(response)); } + if (status < HttpStatus.SC_SUCCESS && endStream) { + throw new ProtocolException("Informational response must not set END_STREAM"); + } if (status > HttpStatus.SC_CONTINUE && status < HttpStatus.SC_SUCCESS) { exchangeHandler.consumeInformation(response, context); } @@ -238,6 +241,9 @@ public void consumeHeader(final List
headers, final boolean endStream) t responseState.set(endStream ? MessageState.COMPLETE : MessageState.BODY); break; case BODY: + if (!endStream) { + throw new ProtocolException("Trailer headers must set END_STREAM"); + } TrailersValidationSupport.verify(headers); responseState.set(MessageState.COMPLETE); exchangeHandler.streamEnd(headers); diff --git a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestClientH2StreamHandler.java b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestClientH2StreamHandler.java index 1df777354..739d102a5 100644 --- a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestClientH2StreamHandler.java +++ b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/impl/nio/TestClientH2StreamHandler.java @@ -26,12 +26,14 @@ */ package org.apache.hc.core5.http2.impl.nio; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.impl.BasicHttpConnectionMetrics; import org.apache.hc.core5.http.impl.BasicHttpTransportMetrics; @@ -117,6 +119,24 @@ void consumeTrailersWithPseudoHeaderRejected() throws Exception { Mockito.verify(exchangeHandler, Mockito.never()).streamEnd(Mockito.anyList()); } + @Test + void informationalResponseWithEndStreamRejected() throws HttpException, IOException { + final List
responseHeaders = Collections.singletonList( + new BasicHeader(":status", "103")); + Assertions.assertThrows(ProtocolException.class, () -> handler.consumeHeader(responseHeaders, true)); + Mockito.verify(exchangeHandler, Mockito.never()).consumeInformation(Mockito.any(), Mockito.any()); + } + @Test + void consumeTrailersWithoutEndStreamRejected() throws Exception { + final List
responseHeaders = Collections.singletonList( + new BasicHeader(":status", "200")); + handler.consumeHeader(responseHeaders, false); + + final List
trailers = Collections.singletonList( + new BasicHeader("x-checksum", "abc123")); + Assertions.assertThrows(ProtocolException.class, () -> handler.consumeHeader(trailers, false)); + Mockito.verify(exchangeHandler, Mockito.never()).streamEnd(Mockito.anyList()); + } @Test void contentLengthValid() throws Exception { final List
responseHeaders = Arrays.asList(