Skip to content

Commit c9e08c5

Browse files
committed
improve code
1 parent 8e3ffdf commit c9e08c5

File tree

5 files changed

+137
-104
lines changed

5 files changed

+137
-104
lines changed

httpclient5/src/main/java/org/apache/hc/client5/http/config/RequestConfig.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.apache.hc.core5.annotation.Contract;
3535
import org.apache.hc.core5.annotation.ThreadingBehavior;
3636
import org.apache.hc.core5.http.HttpHost;
37+
import org.apache.hc.core5.http2.priority.PriorityValue;
3738
import org.apache.hc.core5.util.Args;
3839
import org.apache.hc.core5.util.TimeValue;
3940
import org.apache.hc.core5.util.Timeout;
@@ -69,13 +70,18 @@ public class RequestConfig implements Cloneable {
6970

7071
private final ExpectContinueTrigger expectContinueTrigger;
7172

73+
/**
74+
* HTTP/2 Priority header value to emit when using H2+. Null means “don’t emit”.
75+
*/
76+
private final PriorityValue h2Priority;
77+
7278
/**
7379
* Intended for CDI compatibility
7480
*/
7581
protected RequestConfig() {
7682
this(false, null, null, false, false, 0, false, null, null,
7783
DEFAULT_CONNECTION_REQUEST_TIMEOUT, null, null, DEFAULT_CONN_KEEP_ALIVE, false, false, false, null,
78-
ExpectContinueTrigger.ALWAYS);
84+
ExpectContinueTrigger.ALWAYS, null);
7985
}
8086

8187
RequestConfig(
@@ -96,7 +102,8 @@ protected RequestConfig() {
96102
final boolean hardCancellationEnabled,
97103
final boolean protocolUpgradeEnabled,
98104
final Path unixDomainSocket,
99-
final ExpectContinueTrigger expectContinueTrigger) {
105+
final ExpectContinueTrigger expectContinueTrigger,
106+
final PriorityValue h2Priority) {
100107
super();
101108
this.expectContinueEnabled = expectContinueEnabled;
102109
this.proxy = proxy;
@@ -116,6 +123,7 @@ protected RequestConfig() {
116123
this.protocolUpgradeEnabled = protocolUpgradeEnabled;
117124
this.unixDomainSocket = unixDomainSocket;
118125
this.expectContinueTrigger = expectContinueTrigger;
126+
this.h2Priority = h2Priority;
119127
}
120128

121129
/**
@@ -244,6 +252,14 @@ public ExpectContinueTrigger getExpectContinueTrigger() {
244252
return expectContinueTrigger;
245253
}
246254

255+
/**
256+
* Returns the HTTP/2+ priority preference for this request or {@code null} if unset.
257+
* @since 5.6
258+
*/
259+
public PriorityValue getH2Priority() {
260+
return h2Priority;
261+
}
262+
247263
@Override
248264
protected RequestConfig clone() throws CloneNotSupportedException {
249265
return (RequestConfig) super.clone();
@@ -296,7 +312,8 @@ public static RequestConfig.Builder copy(final RequestConfig config) {
296312
.setContentCompressionEnabled(config.isContentCompressionEnabled())
297313
.setHardCancellationEnabled(config.isHardCancellationEnabled())
298314
.setProtocolUpgradeEnabled(config.isProtocolUpgradeEnabled())
299-
.setUnixDomainSocket(config.getUnixDomainSocket());
315+
.setUnixDomainSocket(config.getUnixDomainSocket())
316+
.setH2Priority(config.getH2Priority());
300317
}
301318

302319
public static class Builder {
@@ -319,6 +336,7 @@ public static class Builder {
319336
private boolean protocolUpgradeEnabled;
320337
private Path unixDomainSocket;
321338
private ExpectContinueTrigger expectContinueTrigger;
339+
private PriorityValue h2Priority;
322340

323341
Builder() {
324342
super();
@@ -691,6 +709,15 @@ public Builder setExpectContinueTrigger(final ExpectContinueTrigger trigger) {
691709
return this;
692710
}
693711

712+
/**
713+
* Sets HTTP/2+ request priority (RFC 9218). If {@code null}, the header is not emitted.
714+
* @since 5.6
715+
*/
716+
public Builder setH2Priority(final PriorityValue priority) {
717+
this.h2Priority = priority;
718+
return this;
719+
}
720+
694721
public RequestConfig build() {
695722
return new RequestConfig(
696723
expectContinueEnabled,
@@ -710,7 +737,8 @@ public RequestConfig build() {
710737
hardCancellationEnabled,
711738
protocolUpgradeEnabled,
712739
unixDomainSocket,
713-
expectContinueTrigger);
740+
expectContinueTrigger,
741+
h2Priority);
714742
}
715743

716744
}

httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ public final H2AsyncClientBuilder evictIdleConnections(final TimeValue maxIdleTi
701701
}
702702

703703
/**
704-
* Enables emission of the RFC 9218 {@code Priority} request header for HTTP/2 requests.
704+
* Enables emission of the {@code Priority} request header for HTTP/2 requests.
705705
* <p>
706706
* When enabled, {@link org.apache.hc.client5.http.protocol.H2RequestPriority} is added as the
707707
* last request interceptor. The header is omitted when the effective value equals the RFC

httpclient5/src/main/java/org/apache/hc/client5/http/protocol/H2RequestPriority.java

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import java.io.IOException;
3131

32+
import org.apache.hc.client5.http.config.RequestConfig;
3233
import org.apache.hc.core5.annotation.Contract;
3334
import org.apache.hc.core5.annotation.Experimental;
3435
import org.apache.hc.core5.annotation.ThreadingBehavior;
@@ -47,14 +48,13 @@
4748
import org.apache.hc.core5.util.Args;
4849

4950
/**
50-
* Emits {@code Priority} request header for HTTP/2+.
51+
* Adds the {@code Priority} request header to HTTP/2+ requests when a per-request
52+
* priority is configured.
5153
* <p>
52-
* The priority value is taken from the request context attribute
53-
* {@link #ATTR_HTTP2_PRIORITY_VALUE}. If the formatted value equals
54-
* RFC defaults (u=3, i=false) the header is omitted.
55-
* <p>
56-
* If {@code overwrite} is {@code false} (default), an existing {@code Priority}
57-
* header set by the caller is preserved.
54+
* The priority is taken from {@link RequestConfig#getH2Priority()}. If a {@code Priority}
55+
* header is already present on the request, it is left unchanged. If formatting the
56+
* configured value yields an empty string (e.g., because it encodes protocol defaults),
57+
* the header is not added.
5858
*
5959
* @since 5.6
6060
*/
@@ -63,59 +63,47 @@
6363
public final class H2RequestPriority implements HttpRequestInterceptor {
6464

6565
/**
66-
* Context attribute to carry a {@link PriorityValue}.
67-
*/
68-
public static final String ATTR_HTTP2_PRIORITY_VALUE = "http2.priority.value";
69-
70-
/**
71-
* Singleton with {@code overwrite=false}.
66+
* Singleton instance.
7267
*/
73-
public static final H2RequestPriority INSTANCE = new H2RequestPriority(false);
74-
75-
private final boolean overwrite;
76-
77-
public H2RequestPriority() {
78-
this(false);
79-
}
80-
81-
public H2RequestPriority(final boolean overwrite) {
82-
this.overwrite = overwrite;
83-
}
68+
public static final H2RequestPriority INSTANCE = new H2RequestPriority();
8469

8570
@Override
86-
public void process(final HttpRequest request, final EntityDetails entity,
87-
final HttpContext context) throws HttpException, IOException {
71+
public void process(
72+
final HttpRequest request,
73+
final EntityDetails entity,
74+
final HttpContext context) throws HttpException, IOException {
8875

8976
Args.notNull(request, "HTTP request");
9077
Args.notNull(context, "HTTP context");
9178

92-
final ProtocolVersion ver = HttpCoreContext.cast(context).getProtocolVersion();
93-
if (ver == null || ver.compareToVersion(HttpVersion.HTTP_2) < 0) {
79+
final ProtocolVersion pv = HttpCoreContext.cast(context).getProtocolVersion();
80+
if (pv == null || pv.compareToVersion(HttpVersion.HTTP_2) < 0) {
9481
return; // only for HTTP/2+
9582
}
9683

84+
// Do not override a header explicitly provided by the caller.
9785
final Header existing = request.getFirstHeader(HttpHeaders.PRIORITY);
98-
if (existing != null && !overwrite) {
99-
return; // respect caller-set header
86+
if (existing != null) {
87+
return;
10088
}
10189

102-
final PriorityValue pv = HttpCoreContext.cast(context)
103-
.getAttribute(ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.class);
104-
if (pv == null) {
90+
final RequestConfig requestConfig = org.apache.hc.client5.http.protocol.HttpClientContext
91+
.cast(context)
92+
.getRequestConfigOrDefault();
93+
if (requestConfig == null) {
10594
return;
10695
}
10796

108-
final String value = PriorityFormatter.format(pv);
109-
if (value == null || value.trim().isEmpty()) {
110-
if (overwrite && request.getFirstHeader(HttpHeaders.PRIORITY) != null) {
111-
request.removeHeaders(HttpHeaders.PRIORITY);
112-
}
97+
final PriorityValue pri = requestConfig.getH2Priority();
98+
if (pri == null) {
11399
return;
114100
}
115101

116-
if (overwrite && request.getFirstHeader(HttpHeaders.PRIORITY) != null) {
117-
request.removeHeaders(HttpHeaders.PRIORITY);
102+
final String value = PriorityFormatter.format(pri);
103+
if (value.isEmpty()) {
104+
return;
118105
}
106+
119107
request.addHeader(HttpHeaders.PRIORITY, value);
120108
}
121-
}
109+
}
Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,45 +31,84 @@
3131
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
3232
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
3333
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
34+
import org.apache.hc.client5.http.config.RequestConfig;
3435
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
3536
import org.apache.hc.client5.http.impl.async.H2AsyncClientBuilder;
36-
import org.apache.hc.client5.http.protocol.H2RequestPriority;
3737
import org.apache.hc.client5.http.protocol.HttpClientContext;
3838
import org.apache.hc.core5.annotation.Experimental;
3939
import org.apache.hc.core5.http2.config.H2Config;
4040
import org.apache.hc.core5.http2.priority.PriorityValue;
4141

42+
/**
43+
* Demonstrates sending the HTTP/2 {@code Priority} request header using per-request configuration.
44+
*
45+
* <p><strong>How it works</strong>:
46+
* <ul>
47+
* <li>Call {@code enablePriorityHeader()} on the H2 client builder to register
48+
* {@link org.apache.hc.client5.http.protocol.H2RequestPriority}.</li>
49+
* <li>For each request, set a priority on the {@link RequestConfig} via
50+
* {@link RequestConfig.Builder#setH2Priority(PriorityValue)} and attach it to the
51+
* {@link HttpClientContext} passed to {@code execute}.</li>
52+
* </ul>
53+
*
54+
* <p><strong>Notes</strong>:
55+
* <ul>
56+
* <li>If a {@code Priority} header is already present on the request, it is preserved.</li>
57+
* <li>If the configured value encodes protocol defaults, the header is omitted.</li>
58+
* <li>Applies to HTTP/2+ only; HTTP/1.1 requests are unaffected.</li>
59+
* </ul>
60+
*
61+
* @since 5.6
62+
*/
4263
@Experimental
43-
public class ClientH2PriorityHeaderExample {
64+
public class AsyncClientH2Priority {
4465

4566
public static void main(final String[] args) throws Exception {
4667
try (CloseableHttpAsyncClient client = H2AsyncClientBuilder.create()
4768
.setH2Config(H2Config.custom()
4869
.setPushEnabled(false)
4970
.build())
50-
.enablePriorityHeader() // wires H2RequestPriority into the chain
71+
.enablePriorityHeader() // registers H2RequestPriority in the chain
5172
.build()) {
5273

5374
client.start();
5475

55-
// --- Request 1: non-default priority -> header sent
76+
// --- Request 1: non-default priority -> header sent (e.g., "u=0, i")
5677
final HttpClientContext ctx1 = HttpClientContext.create();
57-
ctx1.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.of(0, true));
78+
ctx1.setRequestConfig(RequestConfig.custom()
79+
.setH2Priority(PriorityValue.of(0, true))
80+
.build());
5881

5982
final SimpleHttpRequest req1 = SimpleRequestBuilder.get("https://nghttp2.org/httpbin/headers").build();
6083
final Future<SimpleHttpResponse> f1 = client.execute(req1, ctx1, null);
6184
final SimpleHttpResponse r1 = f1.get();
6285
System.out.println("[/httpbin/headers] -> " + r1.getCode());
86+
System.out.println("Negotiated protocol (req1): " + ctx1.getProtocolVersion());
6387
System.out.println(r1.getBodyText());
6488

6589
// --- Request 2: defaults -> header omitted
6690
final HttpClientContext ctx2 = HttpClientContext.create();
67-
ctx2.setAttribute(H2RequestPriority.ATTR_HTTP2_PRIORITY_VALUE, PriorityValue.defaults());
91+
ctx2.setRequestConfig(RequestConfig.custom()
92+
.setH2Priority(PriorityValue.defaults())
93+
.build());
6894

6995
final SimpleHttpRequest req2 = SimpleRequestBuilder.get("https://nghttp2.org/httpbin/user-agent").build();
7096
final SimpleHttpResponse r2 = client.execute(req2, ctx2, null).get();
7197
System.out.println("[/httpbin/user-agent] -> " + r2.getCode());
98+
System.out.println("Negotiated protocol (req2): " + ctx2.getProtocolVersion());
7299
System.out.println(r2.getBodyText());
100+
101+
// --- Request 3: user-provided header -> preserved (no overwrite)
102+
final HttpClientContext ctx3 = HttpClientContext.create();
103+
ctx3.setRequestConfig(RequestConfig.custom()
104+
.setH2Priority(PriorityValue.of(5, false))
105+
.build());
106+
final SimpleHttpRequest req3 = SimpleRequestBuilder.get("https://nghttp2.org/httpbin/headers").build();
107+
req3.addHeader("Priority", "u=2");
108+
final SimpleHttpResponse r3 = client.execute(req3, ctx3, null).get();
109+
System.out.println("[/httpbin/headers with user header] -> " + r3.getCode());
110+
System.out.println("Negotiated protocol (req3): " + ctx3.getProtocolVersion());
111+
System.out.println(r3.getBodyText());
73112
}
74113
}
75114
}

0 commit comments

Comments
 (0)