Skip to content

Commit 5cef6ed

Browse files
HTTPCLIENT-2372 - Normalize HttpHost port comparison to treat implicit default ports as equal (#643)
1 parent c65eb9b commit 5cef6ed

File tree

5 files changed

+89
-4
lines changed

5 files changed

+89
-4
lines changed

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

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.Iterator;
3333
import java.util.Locale;
3434

35+
import org.apache.hc.client5.http.SchemePortResolver;
3536
import org.apache.hc.client5.http.protocol.RedirectStrategy;
3637
import org.apache.hc.client5.http.utils.URIUtils;
3738
import org.apache.hc.core5.annotation.Contract;
@@ -56,18 +57,44 @@
5657
@Contract(threading = ThreadingBehavior.STATELESS)
5758
public class DefaultRedirectStrategy implements RedirectStrategy {
5859

60+
private final SchemePortResolver schemePortResolver;
61+
5962
/**
6063
* Default instance of {@link DefaultRedirectStrategy}.
6164
*/
6265
public static final DefaultRedirectStrategy INSTANCE = new DefaultRedirectStrategy();
6366

67+
/**
68+
* Creates a new {@code DefaultRedirectStrategy} using the given {@link SchemePortResolver}.
69+
* If {@code schemePortResolver} is {@code null}, this will default to
70+
* {@link DefaultSchemePortResolver#INSTANCE}.
71+
*
72+
* @param schemePortResolver the resolver to use for determining default ports
73+
* @since 5.6
74+
*/
75+
public DefaultRedirectStrategy(final SchemePortResolver schemePortResolver) {
76+
this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE;
77+
}
78+
79+
/**
80+
* Creates a new {@code DefaultRedirectStrategy} with the default
81+
* {@link DefaultSchemePortResolver#INSTANCE}.
82+
*
83+
* @since 5.6
84+
*/
85+
public DefaultRedirectStrategy() {
86+
this(null);
87+
}
88+
6489
@Override
6590
public boolean isRedirectAllowed(
6691
final HttpHost currentTarget,
6792
final HttpHost newTarget,
6893
final HttpRequest redirect,
6994
final HttpContext context) {
70-
if (!currentTarget.equals(newTarget)) {
95+
96+
// If authority (host + effective port) differs, strip sensitive headers
97+
if (!isSameAuthority(currentTarget, newTarget)) {
7198
for (final Iterator<Header> it = redirect.headerIterator(); it.hasNext(); ) {
7299
final Header header = it.next();
73100
if (header.isSensitive()
@@ -80,6 +107,20 @@ public boolean isRedirectAllowed(
80107
return true;
81108
}
82109

110+
private boolean isSameAuthority(final HttpHost h1, final HttpHost h2) {
111+
if (h1 == null || h2 == null) {
112+
return false;
113+
}
114+
final String host1 = h1.getHostName();
115+
final String host2 = h2.getHostName();
116+
if (!host1.equalsIgnoreCase(host2)) {
117+
return false;
118+
}
119+
final int port1 = schemePortResolver.resolve(h1);
120+
final int port2 = schemePortResolver.resolve(h2);
121+
return port1 == port2;
122+
}
123+
83124
@Override
84125
public boolean isRedirected(
85126
final HttpRequest request,

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
@@ -836,7 +836,7 @@ public CloseableHttpAsyncClient build() {
836836
if (!redirectHandlingDisabled) {
837837
RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
838838
if (redirectStrategyCopy == null) {
839-
redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
839+
redirectStrategyCopy = schemePortResolver != null ? new DefaultRedirectStrategy(schemePortResolver) : DefaultRedirectStrategy.INSTANCE;
840840
}
841841
execChainDefinition.addFirst(
842842
new AsyncRedirectExec(routePlannerCopy, redirectStrategyCopy),

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1023,7 +1023,7 @@ public CloseableHttpAsyncClient build() {
10231023
if (!redirectHandlingDisabled) {
10241024
RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
10251025
if (redirectStrategyCopy == null) {
1026-
redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
1026+
redirectStrategyCopy = schemePortResolver != null ? new DefaultRedirectStrategy(schemePortResolver) : DefaultRedirectStrategy.INSTANCE;
10271027
}
10281028
execChainDefinition.addFirst(
10291029
new AsyncRedirectExec(routePlannerCopy, redirectStrategyCopy),

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,7 @@ public CloseableHttpClient build() {
10111011
if (!redirectHandlingDisabled) {
10121012
RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
10131013
if (redirectStrategyCopy == null) {
1014-
redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
1014+
redirectStrategyCopy = schemePortResolver != null ? new DefaultRedirectStrategy(schemePortResolver) : DefaultRedirectStrategy.INSTANCE;
10151015
}
10161016
execChainDefinition.addFirst(
10171017
new RedirectExec(routePlannerCopy, redirectStrategyCopy),

httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestDefaultRedirectStrategy.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,48 @@ void testRedirectAllowed() throws Exception {
324324
null));
325325
}
326326

327+
328+
329+
330+
@Test
331+
void testRedirectAllowedDefaultPortNormalization() {
332+
final DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
333+
334+
// HTTPS with explicit 443 vs HTTPS with no port (defaults to 443)
335+
final HttpHost explicitHttps = new HttpHost("https", "example.com", 443);
336+
final HttpHost implicitHttps = new HttpHost("https", "example.com", -1);
337+
Assertions.assertTrue(redirectStrategy.isRedirectAllowed(
338+
explicitHttps,
339+
implicitHttps,
340+
BasicRequestBuilder.get("/")
341+
.addHeader(HttpHeaders.AUTHORIZATION, "token")
342+
.build(),
343+
null));
344+
Assertions.assertTrue(redirectStrategy.isRedirectAllowed(
345+
implicitHttps,
346+
explicitHttps,
347+
BasicRequestBuilder.get("/")
348+
.addHeader(HttpHeaders.COOKIE, "cookie=123")
349+
.build(),
350+
null));
351+
352+
final HttpHost explicitHttp = new HttpHost("http", "example.org", 80);
353+
final HttpHost implicitHttp = new HttpHost("http", "example.org", -1);
354+
Assertions.assertTrue(redirectStrategy.isRedirectAllowed(
355+
explicitHttp,
356+
implicitHttp,
357+
BasicRequestBuilder.get("/")
358+
.addHeader(HttpHeaders.AUTHORIZATION, "token123")
359+
.build(),
360+
null));
361+
Assertions.assertTrue(redirectStrategy.isRedirectAllowed(
362+
implicitHttp,
363+
explicitHttp,
364+
BasicRequestBuilder.get("/")
365+
.addHeader(HttpHeaders.COOKIE, "cookie=abc")
366+
.build(),
367+
null));
368+
}
369+
370+
327371
}

0 commit comments

Comments
 (0)