Skip to content

Commit 64a559c

Browse files
committed
Add EncodingNegotiator
1 parent f221f24 commit 64a559c

4 files changed

Lines changed: 276 additions & 186 deletions

File tree

java/org/apache/coyote/CompressionConfig.java

Lines changed: 2 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ public OutputFilterFactory useCompression(Request request, Response response, Li
274274
return null;
275275
}
276276

277-
teFactory = negotiateTE(factories, tes);
277+
teFactory = EncodingNegotiator.negotiateTE(factories, tes);
278278
if(teFactory != null) {
279279
useTransferEncoding = true;
280280
break;
@@ -308,7 +308,7 @@ public OutputFilterFactory useCompression(Request request, Response response, Li
308308
return null;
309309
}
310310

311-
selectedFactory = negotiateAcceptEncoding(factories, acceptEncodings);
311+
selectedFactory = EncodingNegotiator.negotiateAcceptEncoding(factories, acceptEncodings);
312312
if (selectedFactory != null) {
313313
break;
314314
}
@@ -350,74 +350,6 @@ public OutputFilterFactory useCompression(Request request, Response response, Li
350350
return selectedFactory;
351351
}
352352

353-
/**
354-
* Negotiate the best encoding from TE header entries against available factories
355-
*/
356-
private OutputFilterFactory negotiateTE(List<OutputFilterFactory> factories, List<TE> entries) {
357-
OutputFilterFactory bestFactory = null;
358-
double bestQuality = 0;
359-
int bestServerPriority = Integer.MAX_VALUE;
360-
361-
for (int i = 0; i < factories.size(); i++) {
362-
OutputFilterFactory factory = factories.get(i);
363-
String factoryEncoding = factory.getEncodingName().toLowerCase(Locale.ENGLISH);
364-
365-
for (TE entry : entries) {
366-
String entryEncoding = entry.getEncoding().toLowerCase(Locale.ENGLISH);
367-
double quality = entry.getQuality();
368-
369-
if (quality <= 0) {
370-
continue;
371-
}
372-
373-
if (factoryEncoding.equals(entryEncoding) || "*".equals(entryEncoding)) {
374-
if (quality > bestQuality || (quality == bestQuality && i < bestServerPriority)) {
375-
bestFactory = factory;
376-
bestQuality = quality;
377-
bestServerPriority = i;
378-
}
379-
}
380-
}
381-
}
382-
383-
return bestFactory;
384-
}
385-
386-
/**
387-
* Negotiate the best encoding from Accept-Encoding entries against available factories.
388-
*/
389-
private OutputFilterFactory negotiateAcceptEncoding(
390-
List<OutputFilterFactory> factories,
391-
List<AcceptEncoding> acceptEncodings) {
392-
OutputFilterFactory bestFactory = null;
393-
double bestQuality = 0;
394-
int bestServerPriority = Integer.MAX_VALUE;
395-
396-
for (int i = 0; i < factories.size(); i++) {
397-
OutputFilterFactory factory = factories.get(i);
398-
String factoryEncoding = factory.getEncodingName().toLowerCase(Locale.ENGLISH);
399-
400-
for (AcceptEncoding ae : acceptEncodings) {
401-
String aeEncoding = ae.getEncoding().toLowerCase(Locale.ENGLISH);
402-
double quality = ae.getQuality();
403-
404-
if (quality <= 0) {
405-
continue;
406-
}
407-
408-
if (factoryEncoding.equals(aeEncoding) || "*".equals(aeEncoding)) {
409-
if (quality > bestQuality || (quality == bestQuality && i < bestServerPriority)) {
410-
bestFactory = factory;
411-
bestQuality = quality;
412-
bestServerPriority = i;
413-
}
414-
}
415-
}
416-
}
417-
418-
return bestFactory;
419-
}
420-
421353
/**
422354
* Checks if any entry in the string array starts with the specified value
423355
*
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.coyote;
18+
19+
import java.util.List;
20+
import java.util.Locale;
21+
import java.util.function.Function;
22+
import java.util.function.ToDoubleFunction;
23+
24+
import org.apache.coyote.http11.filters.OutputFilterFactory;
25+
import org.apache.tomcat.util.http.parser.AcceptEncoding;
26+
import org.apache.tomcat.util.http.parser.TE;
27+
28+
/**
29+
* Utility class for negotiating transfer/content encodings using TE and
30+
* Accept-Encoding headers.
31+
*/
32+
public class EncodingNegotiator {
33+
34+
/**
35+
* Selects the best {@link OutputFilterFactory} for the HTTP {@code TE} header.
36+
* <p>Rules:
37+
* - Prefer the entry with the highest quality (q) value; entries with {@code q <= 0} are ignored.
38+
* - Wildcard ({@code *}) matches any server factory.
39+
* - On quality ties, prefer the factory with the lowest server priority (its index in {@code factories}).
40+
* - Encoding name matching is case-insensitive.
41+
*
42+
* @param factories The available factories in server-priority order (index 0 is highest)
43+
* @param entries The parsed {@code TE} header entries
44+
* @return The selected factory or {@code null} if no match
45+
*/
46+
public static OutputFilterFactory negotiateTE(List<OutputFilterFactory> factories, List<TE> entries) {
47+
return negotiateEncodings(factories, entries, TE::getEncoding, TE::getQuality);
48+
}
49+
50+
/**
51+
* Selects the best {@link OutputFilterFactory} for the HTTP {@code Accept-Encoding} header.
52+
* <p>Rules:
53+
* - Prefer the entry with the highest quality (q) value; entries with {@code q <= 0} are ignored.
54+
* - Wildcard ({@code *}) matches any server factory.
55+
* - On quality ties, prefer the factory with the lowest server priority (its index in {@code factories}).
56+
* - Encoding name matching is case-insensitive.
57+
*
58+
* @param factories The available factories in server-priority order (index 0 is highest)
59+
* @param acceptEncodings The parsed {@code Accept-Encoding} entries
60+
* @return The selected factory or {@code null} if no match
61+
*/
62+
public static OutputFilterFactory negotiateAcceptEncoding(
63+
List<OutputFilterFactory> factories, List<AcceptEncoding> acceptEncodings) {
64+
return negotiateEncodings(factories, acceptEncodings, AcceptEncoding::getEncoding, AcceptEncoding::getQuality);
65+
}
66+
67+
private static <T> OutputFilterFactory negotiateEncodings(
68+
List<OutputFilterFactory> factories,
69+
List<T> entries,
70+
Function<T,String> encodingFn,
71+
ToDoubleFunction<T> qualityFn) {
72+
OutputFilterFactory bestFactory = null;
73+
double bestQuality = 0;
74+
int bestServerPriority = Integer.MAX_VALUE;
75+
76+
for (int i = 0; i < factories.size(); i++) {
77+
OutputFilterFactory factory = factories.get(i);
78+
String factoryEncoding = factory.getEncodingName().toLowerCase(Locale.ENGLISH);
79+
80+
for (T entry : entries) {
81+
String entryEncoding = encodingFn.apply(entry).toLowerCase(Locale.ENGLISH);
82+
double quality = qualityFn.applyAsDouble(entry);
83+
84+
if (quality <= 0) {
85+
continue;
86+
}
87+
88+
if (factoryEncoding.equals(entryEncoding) || "*".equals(entryEncoding)) {
89+
if (quality > bestQuality || (quality == bestQuality && i < bestServerPriority)) {
90+
bestFactory = factory;
91+
bestQuality = quality;
92+
bestServerPriority = i;
93+
}
94+
}
95+
}
96+
}
97+
98+
return bestFactory;
99+
}
100+
}

0 commit comments

Comments
 (0)