diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java index 6ae2572c7f..9b39d4b080 100644 --- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java +++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractH2StreamMultiplexer.java @@ -52,6 +52,7 @@ import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; import org.apache.hc.core5.http.HttpException; +import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpStreamResetException; import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.ProtocolException; @@ -71,7 +72,6 @@ import org.apache.hc.core5.http.nio.command.StaleCheckCommand; import org.apache.hc.core5.http.protocol.HttpContext; import org.apache.hc.core5.http.protocol.HttpProcessor; -import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http2.H2ConnectionException; import org.apache.hc.core5.http2.H2Error; import org.apache.hc.core5.http2.H2StreamResetException; @@ -91,7 +91,6 @@ import org.apache.hc.core5.http2.nio.command.PushResponseCommand; import org.apache.hc.core5.http2.priority.PriorityParamsParser; import org.apache.hc.core5.http2.priority.PriorityValue; -import org.apache.hc.core5.http2.priority.PriorityFormatter; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.reactor.Command; import org.apache.hc.core5.reactor.ProtocolIOSession; @@ -1369,25 +1368,6 @@ H2Stream createStream(final H2StreamChannel channel, final H2StreamHandler strea return streams.createActive(channel, streamHandler); } - public final void sendPriorityUpdate(final int prioritizedStreamId, final PriorityValue value) throws IOException { - if (value == null) { - return; - } - final String field = PriorityFormatter.format(value); - if (field == null) { - return; - } - final byte[] ascii = field.getBytes(StandardCharsets.US_ASCII); - final ByteArrayBuffer buf = new ByteArrayBuffer(4 + ascii.length); - buf.append((byte) (prioritizedStreamId >> 24)); - buf.append((byte) (prioritizedStreamId >> 16)); - buf.append((byte) (prioritizedStreamId >> 8)); - buf.append((byte) prioritizedStreamId); - buf.append(ascii, 0, ascii.length); - final RawFrame frame = frameFactory.createPriorityUpdate(ByteBuffer.wrap(buf.array(), 0, buf.length())); - commitFrame(frame); - } - private void recordPriorityFromHeaders(final int streamId, final List headers) { if (headers == null || headers.isEmpty()) { return; diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/priority/PriorityFormatter.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/priority/PriorityFormatter.java index 635b0dead3..2f70b5baca 100644 --- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/priority/PriorityFormatter.java +++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/priority/PriorityFormatter.java @@ -27,10 +27,12 @@ package org.apache.hc.core5.http2.priority; -import java.util.ArrayList; -import java.util.List; - import org.apache.hc.core5.annotation.Internal; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpHeaders; +import org.apache.hc.core5.http.message.BasicHeader; +import org.apache.hc.core5.http.message.BufferedHeader; +import org.apache.hc.core5.util.CharArrayBuffer; /** * Formats PriorityValue as RFC 9218 Structured Fields Dictionary. @@ -43,21 +45,44 @@ public final class PriorityFormatter { private PriorityFormatter() { } - public static String format(final PriorityValue value) { + public static void format(final CharArrayBuffer dst, final PriorityValue value) { if (value == null) { - return null; + return; } - final List parts = new ArrayList<>(2); + boolean urgencyPresent = false; if (value.getUrgency() != PriorityValue.DEFAULT_URGENCY) { - parts.add("u=" + value.getUrgency()); + dst.append("u="); + dst.append(value.getUrgency()); + urgencyPresent = true; } if (value.isIncremental()) { // In SF Dictionary, boolean true can be represented by key without value (per RFC 8941). - parts.add("i"); + if (urgencyPresent) { + dst.append(", "); + } + dst.append("i"); + } + + } + + public static String format(final PriorityValue value) { + if (value == null) { + return null; } - if (parts.isEmpty()) { - return null; // omit header when all defaults + final CharArrayBuffer buf = new CharArrayBuffer(16); + format(buf, value); + return buf.toString(); + } + + public static Header formatHeader(final PriorityValue value) { + if (value == null) { + return new BasicHeader(HttpHeaders.PRIORITY, null); } - return String.join(", ", parts); + final CharArrayBuffer buf = new CharArrayBuffer(16); + buf.append(HttpHeaders.PRIORITY); + buf.append(": "); + format(buf, value); + return BufferedHeader.create(buf); } + } \ No newline at end of file diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/H2RequestPriority.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/H2RequestPriority.java deleted file mode 100644 index f0d0367c1a..0000000000 --- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/protocol/H2RequestPriority.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - * - */ -package org.apache.hc.core5.http2.protocol; - -import java.io.IOException; - -import org.apache.hc.core5.annotation.Contract; -import org.apache.hc.core5.annotation.Experimental; -import org.apache.hc.core5.annotation.ThreadingBehavior; -import org.apache.hc.core5.http.EntityDetails; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.HttpException; -import org.apache.hc.core5.http.HttpHeaders; -import org.apache.hc.core5.http.HttpRequest; -import org.apache.hc.core5.http.HttpRequestInterceptor; -import org.apache.hc.core5.http.HttpVersion; -import org.apache.hc.core5.http.ProtocolVersion; -import org.apache.hc.core5.http.protocol.HttpContext; -import org.apache.hc.core5.http.protocol.HttpCoreContext; -import org.apache.hc.core5.http2.priority.PriorityFormatter; -import org.apache.hc.core5.http2.priority.PriorityValue; -import org.apache.hc.core5.util.Args; - -/** - * Emits RFC 9218 {@code Priority} request header for HTTP/2+. - *

- * The priority value is taken from the request context attribute - * {@link #ATTR_HTTP2_PRIORITY_VALUE}. If the formatted value equals - * RFC defaults (u=3, i=false) the header is omitted. - *

- * If {@code overwrite} is {@code false} (default), an existing {@code Priority} - * header set by the caller is preserved. - * - * @since 5.4 - */ -@Experimental -@Contract(threading = ThreadingBehavior.IMMUTABLE) -public final class H2RequestPriority implements HttpRequestInterceptor { - - /** - * Context attribute to carry a {@link PriorityValue}. - */ - public static final String ATTR_HTTP2_PRIORITY_VALUE = "http2.priority.value"; - - /** - * Singleton with {@code overwrite=false}. - */ - public static final H2RequestPriority INSTANCE = new H2RequestPriority(false); - - private final boolean overwrite; - - public H2RequestPriority() { - this(false); - } - - public H2RequestPriority(final boolean overwrite) { - this.overwrite = overwrite; - } - - @Override - public void process(final HttpRequest request, final EntityDetails entity, - final HttpContext context) throws HttpException, IOException { - - Args.notNull(request, "HTTP request"); - Args.notNull(context, "HTTP context"); - - final ProtocolVersion ver = HttpCoreContext.cast(context).getProtocolVersion(); - if (ver == null || ver.compareToVersion(HttpVersion.HTTP_2) < 0) { - return; // only for HTTP/2+ - } - - final Header existing = request.getFirstHeader(HttpHeaders.PRIORITY); - if (existing != null && !overwrite) { - return; // respect caller-set header - } - - final PriorityValue pv = HttpCoreContext.cast(context) - .getAttribute(ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.class); - if (pv == null) { - return; - } - - final String value = PriorityFormatter.format(pv); - if (value == null) { - // defaults (u=3, i=false) -> omit header - if (overwrite && existing != null) { - request.removeHeaders(HttpHeaders.PRIORITY); - } - return; - } - - if (overwrite && existing != null) { - request.removeHeaders(HttpHeaders.PRIORITY); - } - request.addHeader(HttpHeaders.PRIORITY, value); - } -} diff --git a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/ClassicH2PriorityExample.java b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2RequestExecutionWithPriorityExample.java similarity index 68% rename from httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/ClassicH2PriorityExample.java rename to httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2RequestExecutionWithPriorityExample.java index c235db0a18..53dc597db2 100644 --- a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/ClassicH2PriorityExample.java +++ b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/examples/H2RequestExecutionWithPriorityExample.java @@ -26,34 +26,31 @@ */ package org.apache.hc.core5.http2.examples; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import org.apache.hc.core5.annotation.Experimental; -import org.apache.hc.core5.http.ClassicHttpRequest; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpConnection; -import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.Message; import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester; -import org.apache.hc.core5.http.io.support.ClassicRequestBuilder; +import org.apache.hc.core5.http.message.BasicHttpRequest; import org.apache.hc.core5.http.nio.AsyncClientEndpoint; -import org.apache.hc.core5.http.nio.support.classic.ClassicToAsyncRequestProducer; -import org.apache.hc.core5.http.nio.support.classic.ClassicToAsyncResponseConsumer; -import org.apache.hc.core5.http.protocol.HttpCoreContext; +import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer; +import org.apache.hc.core5.http.nio.support.BasicRequestProducer; +import org.apache.hc.core5.http.nio.support.BasicResponseConsumer; +import org.apache.hc.core5.http.support.BasicRequestBuilder; import org.apache.hc.core5.http2.HttpVersionPolicy; import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.frame.RawFrame; import org.apache.hc.core5.http2.impl.H2Processors; import org.apache.hc.core5.http2.impl.nio.H2StreamListener; +import org.apache.hc.core5.http2.priority.PriorityFormatter; import org.apache.hc.core5.http2.priority.PriorityValue; -import org.apache.hc.core5.http2.protocol.H2RequestPriority; import org.apache.hc.core5.io.CloseMode; import org.apache.hc.core5.util.Timeout; @@ -63,7 +60,7 @@ * Requires H2Processors to include H2RequestPriority (client chain) and an HTTP/2 connection. */ @Experimental -public class ClassicH2PriorityExample { +public class H2RequestExecutionWithPriorityExample { public static void main(final String[] args) throws Exception { @@ -120,50 +117,62 @@ public void onOutputFlowControl(final HttpConnection c, final int id, final int final Future future = requester.connect(target, Timeout.ofSeconds(30)); final AsyncClientEndpoint clientEndpoint = future.get(); + final CountDownLatch latch = new CountDownLatch(2); + // ---- Request 1: Explicit non-default priority -> header MUST be emitted - executeWithPriority(clientEndpoint, target, "/httpbin/headers", PriorityValue.of(0, true)); + executeWithPriority(clientEndpoint, target, "/httpbin/headers", PriorityValue.of(0, true), latch); // ---- Request 2: RFC defaults -> header MUST be omitted by the interceptor - executeWithPriority(clientEndpoint, target, "/httpbin/user-agent", PriorityValue.defaults()); + executeWithPriority(clientEndpoint, target, "/httpbin/user-agent", PriorityValue.defaults(), latch); System.out.println("Shutting down I/O reactor"); requester.initiateShutdown(); } private static void executeWithPriority( - final AsyncClientEndpoint endpoint, + final AsyncClientEndpoint clientEndpoint, final HttpHost target, - final String path, - final PriorityValue priorityValue) throws Exception { + final String requestUri, + final PriorityValue priorityValue, + final CountDownLatch latch) throws Exception { - final ClassicHttpRequest request = ClassicRequestBuilder.get() + final BasicHttpRequest request = BasicRequestBuilder.get() .setHttpHost(target) - .setPath(path) + .setPath(requestUri) .build(); + if (!PriorityValue.defaults().equals(priorityValue)) { + request.addHeader(PriorityFormatter.formatHeader(priorityValue)); + } + + clientEndpoint.execute( + new BasicRequestProducer(request, null), + new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), + new FutureCallback>() { - // Place the PriorityValue into the context so H2RequestPriority can format the header - final HttpCoreContext ctx = HttpCoreContext.create(); - ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, priorityValue); - - final ClassicToAsyncRequestProducer requestProducer = new ClassicToAsyncRequestProducer(request, Timeout.ofMinutes(1)); - final ClassicToAsyncResponseConsumer responseConsumer = new ClassicToAsyncResponseConsumer(Timeout.ofMinutes(1)); - - endpoint.execute(requestProducer, responseConsumer, ctx, null); - - requestProducer.blockWaiting().execute(); - try (ClassicHttpResponse response = responseConsumer.blockWaiting()) { - System.out.println(path + " -> " + response.getCode()); - final HttpEntity entity = response.getEntity(); - if (entity != null) { - final ContentType ct = ContentType.parse(entity.getContentType()); - final Charset cs = ContentType.getCharset(ct, StandardCharsets.UTF_8); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), cs))) { - String line; - while ((line = reader.readLine()) != null) { - System.out.println(line); + @Override + public void completed(final Message message) { + clientEndpoint.releaseAndReuse(); + final HttpResponse response = message.getHead(); + final String body = message.getBody(); + System.out.println(requestUri + "->" + response.getCode()); + System.out.println(body); + latch.countDown(); } - } - } - } + + @Override + public void failed(final Exception ex) { + clientEndpoint.releaseAndDiscard(); + System.out.println(requestUri + "->" + ex); + latch.countDown(); + } + + @Override + public void cancelled() { + clientEndpoint.releaseAndDiscard(); + System.out.println(requestUri + " cancelled"); + latch.countDown(); + } + + }); } } \ No newline at end of file diff --git a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/priority/TestPriorityParserAndFormatter.java b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/priority/TestPriorityParserAndFormatter.java index ae70440f01..b193bbc1c5 100644 --- a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/priority/TestPriorityParserAndFormatter.java +++ b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/priority/TestPriorityParserAndFormatter.java @@ -71,9 +71,6 @@ void defaults_when_empty_or_null() { final PriorityValue v2 = PriorityParser.parse((Header) null); assertEquals(3, v2.getUrgency()); assertFalse(v2.isIncremental()); - - // Formatter omits header if all defaults - assertNull(PriorityFormatter.format(PriorityValue.defaults())); } // ---- Boolean variants per RFC 8941 -------------------------------------- @@ -134,11 +131,6 @@ void formatter_emits_only_non_defaults_in_canonical_order() { assertEquals("u=2, i", PriorityFormatter.format(PriorityValue.of(2, true))); } - @Test - void formatter_returns_null_for_defaults() { - assertNull(PriorityFormatter.format(PriorityValue.of(3, false))); - } - // ---- Round-trips --------------------------------------------------------- @Test diff --git a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/protocol/H2RequestPriorityTest.java b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/protocol/H2RequestPriorityTest.java deleted file mode 100644 index b6d8524dfc..0000000000 --- a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/protocol/H2RequestPriorityTest.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * ==================================================================== - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - * - */ -package org.apache.hc.core5.http2.protocol; - -import org.apache.hc.core5.http.HttpHeaders; -import org.apache.hc.core5.http.HttpHost; -import org.apache.hc.core5.http.HttpVersion; -import org.apache.hc.core5.http.message.BasicHttpRequest; -import org.apache.hc.core5.http.protocol.HttpCoreContext; -import org.apache.hc.core5.http2.priority.PriorityValue; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class H2RequestPriorityTest { - - private HttpCoreContext h2ctx; - - @BeforeEach - void setUp() { - h2ctx = HttpCoreContext.create(); - h2ctx.setProtocolVersion(HttpVersion.HTTP_2); - } - - @Test - void testH2RequestPriority_noopOnHttp11() throws Exception { - final HttpCoreContext ctx11 = HttpCoreContext.create(); - ctx11.setProtocolVersion(HttpVersion.HTTP_1_1); - - final BasicHttpRequest request = new BasicHttpRequest("GET", new HttpHost("host"), "/"); - ctx11.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.of(0, true)); - - final H2RequestPriority interceptor = H2RequestPriority.INSTANCE; - interceptor.process(request, null, ctx11); - - Assertions.assertNull(request.getFirstHeader(HttpHeaders.PRIORITY), - "No Priority header should be added for HTTP/1.1"); - } - - @Test - void adds_u_only_when_nonDefault_urgency() throws Exception { - final BasicHttpRequest req = new BasicHttpRequest("GET", new HttpHost("h"), "/"); - h2ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.of(5, false)); - - H2RequestPriority.INSTANCE.process(req, null, h2ctx); - - Assertions.assertNotNull(req.getFirstHeader(HttpHeaders.PRIORITY)); - Assertions.assertEquals("u=5", req.getFirstHeader(HttpHeaders.PRIORITY).getValue()); - Assertions.assertEquals(1, req.getHeaders(HttpHeaders.PRIORITY).length); - } - - @Test - void adds_i_only_when_incremental_true() throws Exception { - final BasicHttpRequest req = new BasicHttpRequest("GET", new HttpHost("h"), "/"); - h2ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.of(3, true)); - - H2RequestPriority.INSTANCE.process(req, null, h2ctx); - - Assertions.assertEquals("i", req.getFirstHeader(HttpHeaders.PRIORITY).getValue()); - } - - @Test - void adds_both_with_expected_format_and_order() throws Exception { - final BasicHttpRequest req = new BasicHttpRequest("GET", new HttpHost("h"), "/"); - h2ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.of(1, true)); - - H2RequestPriority.INSTANCE.process(req, null, h2ctx); - - Assertions.assertEquals("u=1, i", req.getFirstHeader(HttpHeaders.PRIORITY).getValue()); - } - - @Test - void omits_header_when_defaults() throws Exception { - final BasicHttpRequest req = new BasicHttpRequest("GET", new HttpHost("h"), "/"); - h2ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.defaults()); - - H2RequestPriority.INSTANCE.process(req, null, h2ctx); - - Assertions.assertNull(req.getFirstHeader(HttpHeaders.PRIORITY)); - } - - @Test - void preserves_existing_when_overwrite_false() throws Exception { - final BasicHttpRequest req = new BasicHttpRequest("GET", new HttpHost("h"), "/"); - req.addHeader(HttpHeaders.PRIORITY, "u=0"); - h2ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.of(5, true)); - - H2RequestPriority.INSTANCE.process(req, null, h2ctx); - - Assertions.assertEquals("u=0", req.getFirstHeader(HttpHeaders.PRIORITY).getValue(), - "Existing header must be preserved when overwrite=false"); - } - - @Test - void overwrites_existing_when_overwrite_true() throws Exception { - final BasicHttpRequest req = new BasicHttpRequest("GET", new HttpHost("h"), "/"); - req.addHeader(HttpHeaders.PRIORITY, "u=7"); - h2ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.of(0, true)); - - new H2RequestPriority(true).process(req, null, h2ctx); - - Assertions.assertEquals("u=0, i", req.getFirstHeader(HttpHeaders.PRIORITY).getValue()); - Assertions.assertEquals(1, req.getHeaders(HttpHeaders.PRIORITY).length); - } - - @Test - void removes_existing_when_overwrite_true_and_defaults() throws Exception { - final BasicHttpRequest req = new BasicHttpRequest("GET", new HttpHost("h"), "/"); - req.addHeader(HttpHeaders.PRIORITY, "u=7"); - h2ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.defaults()); - - new H2RequestPriority(true).process(req, null, h2ctx); - - Assertions.assertNull(req.getFirstHeader(HttpHeaders.PRIORITY), - "Defaults format to null; overwrite=true should remove any existing header"); - } - - @Test - void noop_when_no_context_value() throws Exception { - final BasicHttpRequest req = new BasicHttpRequest("GET", new HttpHost("h"), "/"); - - H2RequestPriority.INSTANCE.process(req, null, h2ctx); - - Assertions.assertNull(req.getFirstHeader(HttpHeaders.PRIORITY)); - } - - @Test - void respects_case_insensitive_existing_header_name() throws Exception { - final BasicHttpRequest req = new BasicHttpRequest("GET", new HttpHost("h"), "/"); - req.addHeader("priority", "u=6"); // lower-case, should still be found - h2ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.of(0, true)); - - H2RequestPriority.INSTANCE.process(req, null, h2ctx); - - Assertions.assertEquals("u=6", req.getFirstHeader(HttpHeaders.PRIORITY).getValue()); - } - - @Test - void dedups_multiple_existing_headers_on_overwrite_true() throws Exception { - final BasicHttpRequest req = new BasicHttpRequest("GET", new HttpHost("h"), "/"); - req.addHeader(HttpHeaders.PRIORITY, "u=7"); - req.addHeader(HttpHeaders.PRIORITY, "i"); - h2ctx.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.of(2, false)); - - new H2RequestPriority(true).process(req, null, h2ctx); - - Assertions.assertEquals(1, req.getHeaders(HttpHeaders.PRIORITY).length); - Assertions.assertEquals("u=2", req.getFirstHeader(HttpHeaders.PRIORITY).getValue()); - } -} \ No newline at end of file