Skip to content

Commit fff3ab4

Browse files
committed
performance improvement.
1 parent 5889705 commit fff3ab4

4 files changed

Lines changed: 84 additions & 64 deletions

File tree

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestRedirects.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -646,8 +646,8 @@ public void handle(final ClassicHttpRequest request,
646646
Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/100").build(),
647647
reqWrapper.getUri());
648648

649-
assertThat(values.poll(), CoreMatchers.equalTo("gzip, x-gzip, deflate"));
650-
assertThat(values.poll(), CoreMatchers.equalTo("gzip, x-gzip, deflate"));
649+
assertThat(values.poll(), CoreMatchers.equalTo("gzip, deflate, lz4-framed, lz4-block, bzip2, pack200, deflate64, x-gzip"));
650+
assertThat(values.poll(), CoreMatchers.equalTo("gzip, deflate, lz4-framed, lz4-block, bzip2, pack200, deflate64, x-gzip"));
651651
assertThat(values.poll(), CoreMatchers.nullValue());
652652
}
653653

httpclient5/src/main/java/org/apache/hc/client5/http/entity/compress/ContentCoding.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@
2727

2828
package org.apache.hc.client5.http.entity.compress;
2929

30+
import java.util.Collections;
31+
import java.util.HashMap;
3032
import java.util.Locale;
33+
import java.util.Map;
3134

3235
/**
3336
* Enumeration of the canonical IANA content-coding tokens supported by HttpClient for
@@ -92,6 +95,15 @@ public enum ContentCoding {
9295
*/
9396
DEFLATE64("deflate64");
9497

98+
private static final Map<String, ContentCoding> TOKEN_LOOKUP;
99+
static {
100+
final Map<String, ContentCoding> map = new HashMap<>(values().length, 1f);
101+
for (final ContentCoding contentCoding : values()) {
102+
map.put(contentCoding.token, contentCoding);
103+
}
104+
TOKEN_LOOKUP = Collections.unmodifiableMap(map);
105+
}
106+
95107
private final String token;
96108

97109
ContentCoding(final String token) {
@@ -108,15 +120,17 @@ public String token() {
108120
}
109121

110122
/**
111-
* Lookup the enum by token (case-insensitive).
123+
* Lookup an enum by its token (case‐insensitive), or {@code null} if none matches.
124+
* <p>
125+
* This method is backed by a static, pre‐populated map so the lookup is O(1)
126+
* instead of O(n).</p>
127+
*
128+
* @param token the content‐coding token to look up
129+
* @return the matching enum constant, or {@code null} if none
112130
*/
113131
public static ContentCoding fromToken(final String token) {
114-
final String copyToken = token.toLowerCase(Locale.ROOT);
115-
for (final ContentCoding coding : values()) {
116-
if (coding.token.equals(copyToken)) {
117-
return coding;
118-
}
119-
}
120-
return null;
132+
return TOKEN_LOOKUP.get(
133+
token == null ? null : token.toLowerCase(Locale.ROOT)
134+
);
121135
}
122136
}

httpclient5/src/main/java/org/apache/hc/client5/http/entity/compress/ContentDecoderRegistry.java

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727

2828
package org.apache.hc.client5.http.entity.compress;
2929

30+
import java.util.Arrays;
3031
import java.util.Collections;
3132
import java.util.LinkedHashMap;
32-
import java.util.Locale;
3333
import java.util.Map;
3434

3535
import org.apache.hc.client5.http.entity.BrotliDecompressingEntity;
@@ -72,56 +72,59 @@ public final class ContentDecoderRegistry {
7272
"org.apache.commons.compress.compressors.CompressorStreamFactory";
7373

7474

75-
private static final Map<String, InputStreamFactory> REGISTRY = buildRegistry();
75+
private static final Map<ContentCoding, InputStreamFactory> REGISTRY = buildRegistry();
7676

7777

7878
/**
7979
* Returns the unmodifiable codec map (key = canonical token, value = factory).
8080
*/
81-
public static Map<String, InputStreamFactory> getRegistry() {
81+
public static Map<ContentCoding, InputStreamFactory> getRegistry() {
8282
return REGISTRY;
8383
}
8484

8585

86-
private static Map<String, InputStreamFactory> buildRegistry() {
87-
final LinkedHashMap<String, InputStreamFactory> m = new LinkedHashMap<>();
86+
private static Map<ContentCoding, InputStreamFactory> buildRegistry() {
87+
final LinkedHashMap<ContentCoding, InputStreamFactory> m = new LinkedHashMap<>();
8888

89-
/* 1. Built-ins (always succeed) */
90-
register(m, new GZIPInputStreamFactory());
91-
register(m, new DeflateInputStreamFactory());
89+
// 1. Built-ins
90+
register(m, ContentCoding.GZIP, new GZIPInputStreamFactory());
91+
register(m, ContentCoding.DEFLATE, new DeflateInputStreamFactory());
9292

93-
/* 2. Commons-Compress (optional, but preferred) */
93+
// 2. Commons-Compress (optional)
9494
if (commonsCompressPresent()) {
95-
addCommons(m, ContentCoding.BROTLI.token());
96-
addCommons(m, ContentCoding.ZSTD.token());
97-
addCommons(m, ContentCoding.XZ.token());
98-
addCommons(m, ContentCoding.LZMA.token());
99-
addCommons(m, ContentCoding.LZ4_FRAMED.token());
100-
addCommons(m, ContentCoding.LZ4_BLOCK.token());
101-
addCommons(m, ContentCoding.BZIP2.token());
102-
addCommons(m, ContentCoding.PACK200.token());
103-
addCommons(m, ContentCoding.DEFLATE64.token());
95+
for (final ContentCoding coding : Arrays.asList(
96+
ContentCoding.BROTLI,
97+
ContentCoding.ZSTD,
98+
ContentCoding.XZ,
99+
ContentCoding.LZMA,
100+
ContentCoding.LZ4_FRAMED,
101+
ContentCoding.LZ4_BLOCK,
102+
ContentCoding.BZIP2,
103+
ContentCoding.PACK200,
104+
ContentCoding.DEFLATE64)) {
105+
addCommons(m, coding);
106+
}
104107
}
105108

106-
/* 3. Native Brotli fallback (only if Commons has not supplied it) */
107-
if (!m.containsKey("br")) {
108-
if (BrotliDecompressingEntity.isAvailable()) {
109-
register(m, new BrotliInputStreamFactory());
110-
}
109+
// 3. Native Brotli fallback if Commons did not register it
110+
if (!m.containsKey(ContentCoding.BROTLI)
111+
&& BrotliDecompressingEntity.isAvailable()) {
112+
register(m, ContentCoding.BROTLI, new BrotliInputStreamFactory());
111113
}
112114

113115
return Collections.unmodifiableMap(m);
114116
}
115117

116-
private static void register(final Map<String, InputStreamFactory> map,
117-
final InputStreamFactory f) {
118-
map.put(f.getContentEncoding().toLowerCase(Locale.ROOT), f);
118+
private static void register(final Map<ContentCoding, InputStreamFactory> map,
119+
final ContentCoding coding,
120+
final InputStreamFactory factory) {
121+
map.put(coding, factory);
119122
}
120123

121-
private static void addCommons(final Map<String, InputStreamFactory> map,
122-
final String enc) {
123-
if (CommonsCompressDecoderFactory.runtimeAvailable(enc)) {
124-
register(map, new CommonsCompressDecoderFactory(enc));
124+
private static void addCommons(final Map<ContentCoding, InputStreamFactory> map,
125+
final ContentCoding coding) {
126+
if (CommonsCompressDecoderFactory.runtimeAvailable(coding.token())) {
127+
register(map, coding, new CommonsCompressDecoderFactory(coding.token()));
125128
}
126129
}
127130

httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ContentCompressionExec.java

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

3030
import java.io.IOException;
3131
import java.util.ArrayList;
32+
import java.util.Collections;
3233
import java.util.List;
3334
import java.util.Locale;
3435
import java.util.Map;
@@ -80,34 +81,47 @@ public final class ContentCompressionExec implements ExecChainHandler {
8081
private final Lookup<InputStreamFactory> decoderRegistry;
8182
private final boolean ignoreUnknown;
8283

83-
private static final Map<String, InputStreamFactory> DECODERS = ContentDecoderRegistry.getRegistry();
84+
private static final Map<ContentCoding, InputStreamFactory> DECODERS = ContentDecoderRegistry.getRegistry();
85+
86+
/**
87+
* Pre-built list of all supported tokens (plus X-GZIP alias) for
88+
* the Accept-Encoding header, to avoid reconstructing it every time.
89+
*/
90+
private static final List<String> DEFAULT_ACCEPT_ENCODINGS;
91+
static {
92+
final List<String> tmp = new ArrayList<>(DECODERS.size() + 1);
93+
for (final ContentCoding coding : DECODERS.keySet()) {
94+
tmp.add(coding.token());
95+
}
96+
// add x-gzip alias if gzip is present
97+
if (DECODERS.containsKey(ContentCoding.GZIP)) {
98+
tmp.add(ContentCoding.X_GZIP.token());
99+
}
100+
DEFAULT_ACCEPT_ENCODINGS = Collections.unmodifiableList(tmp);
101+
}
84102

85103
public ContentCompressionExec(
86104
final List<String> acceptEncoding,
87105
final Lookup<InputStreamFactory> decoderRegistry,
88106
final boolean ignoreUnknown) {
89107

90-
final List<String> encodingsHeader = acceptEncoding != null
91-
? acceptEncoding
92-
: buildDefaultAcceptEncoding();
108+
final List<String> encodingsHeader = acceptEncoding != null ? acceptEncoding : DEFAULT_ACCEPT_ENCODINGS;
93109

94-
this.acceptEncoding = MessageSupport
95-
.headerOfTokens(HttpHeaders.ACCEPT_ENCODING, encodingsHeader);
110+
this.acceptEncoding = MessageSupport.headerOfTokens(HttpHeaders.ACCEPT_ENCODING, encodingsHeader);
96111

97112
if (decoderRegistry != null) {
98113
this.decoderRegistry = decoderRegistry;
99114
} else {
100-
final Map<String, InputStreamFactory> decoders =
101-
ContentDecoderRegistry.getRegistry();
102-
103115
final RegistryBuilder<InputStreamFactory> builder = RegistryBuilder.create();
104-
decoders.forEach(builder::register);
105-
builder.register(ContentCoding.X_GZIP.token(), decoders.get(ContentCoding.GZIP.token()));
106-
116+
DECODERS.forEach((coding, factory) ->
117+
builder.register(coding.token(), factory));
118+
// register the x-gzip alias again
119+
if (DECODERS.containsKey(ContentCoding.GZIP)) {
120+
builder.register(ContentCoding.X_GZIP.token(), DECODERS.get(ContentCoding.GZIP));
121+
}
107122
this.decoderRegistry = builder.build();
108123
}
109124

110-
111125
this.ignoreUnknown = ignoreUnknown;
112126
}
113127

@@ -173,15 +187,4 @@ public ClassicHttpResponse execute(
173187
}
174188
return response;
175189
}
176-
177-
private static List<String> buildDefaultAcceptEncoding() {
178-
final List<String> list = new ArrayList<>(4);
179-
list.add(ContentCoding.GZIP.token());
180-
list.add(ContentCoding.X_GZIP.token());
181-
list.add(ContentCoding.DEFLATE.token());
182-
if (DECODERS.containsKey(ContentCoding.BROTLI.token())) { // Brotli only if decoder exists
183-
list.add(ContentCoding.BROTLI.token());
184-
}
185-
return list;
186-
}
187190
}

0 commit comments

Comments
 (0)