Skip to content

Commit 0802ffe

Browse files
committed
small fix
1 parent afbe52c commit 0802ffe

2 files changed

Lines changed: 52 additions & 98 deletions

File tree

httpclient5/src/main/java/org/apache/hc/client5/http/Rfc6724AddressSelectingDnsResolver.java

Lines changed: 17 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* <http://www.apache.org/>.
2525
*
2626
*/
27+
2728
package org.apache.hc.client5.http;
2829

2930
import java.net.DatagramSocket;
@@ -37,7 +38,6 @@
3738
import java.util.Arrays;
3839
import java.util.Collections;
3940
import java.util.Comparator;
40-
import java.util.LinkedList;
4141
import java.util.List;
4242

4343
import org.apache.hc.client5.http.config.ProtocolFamilyPreference;
@@ -53,10 +53,13 @@
5353
*
5454
* <p>The canonical hostname lookup is delegated unchanged.</p>
5555
*
56-
* <p>{@link ProtocolFamilyPreference#INTERLEAVE} alternates IPv6/IPv4 entries
57-
* as far as possible while preserving per-family relative order.</p>
56+
* <p>
57+
* {@link ProtocolFamilyPreference#INTERLEAVE} is treated as "no family bias":
58+
* the resolver keeps the RFC 6724 sorted order intact. Family interleaving, if
59+
* desired, should be handled at dial time (e.g. Happy Eyeballs).
60+
* </p>
5861
*
59-
* @since 5.7
62+
* @since 5.6
6063
*/
6164
@Contract(threading = ThreadingBehavior.IMMUTABLE)
6265
public final class Rfc6724AddressSelectingDnsResolver implements DnsResolver {
@@ -69,7 +72,7 @@ public final class Rfc6724AddressSelectingDnsResolver implements DnsResolver {
6972
private final ProtocolFamilyPreference familyPreference;
7073

7174
/**
72-
* Creates a new resolver that applies RFC 6724 ordering with no forced family filtering.
75+
* Creates a new resolver that applies RFC 6724 ordering with no family bias (INTERLEAVE).
7376
*
7477
* @param delegate underlying resolver to use.
7578
*/
@@ -98,7 +101,6 @@ public InetAddress[] resolve(final String host) throws UnknownHostException {
98101
}
99102

100103
final InetAddress[] resolved = delegate.resolve(host);
101-
102104
if (resolved == null) {
103105
if (LOG.isDebugEnabled()) {
104106
LOG.debug("{} delegate returned null for '{}'", simpleName(), host);
@@ -139,6 +141,7 @@ public InetAddress[] resolve(final String host) throws UnknownHostException {
139141
break;
140142
}
141143
}
144+
142145
if (LOG.isDebugEnabled()) {
143146
LOG.debug("{} after family filter {} -> {} candidate(s): {}", simpleName(), familyPreference, candidates.size(), fmt(candidates));
144147
}
@@ -153,7 +156,7 @@ public InetAddress[] resolve(final String host) throws UnknownHostException {
153156
// 2) RFC 6724 sort (uses UDP connect to infer source addresses; no packets sent)
154157
final List<InetAddress> rfcSorted = sortByRfc6724(candidates);
155158

156-
// 3) Apply family preference ordering where applicable
159+
// 3) Apply preference bias
157160
final List<InetAddress> ordered = applyFamilyPreference(rfcSorted, familyPreference);
158161

159162
if (LOG.isDebugEnabled()) {
@@ -219,9 +222,11 @@ private static List<InetAddress> sortByRfc6724(final List<InetAddress> addrs) {
219222
private static List<InetAddress> applyFamilyPreference(
220223
final List<InetAddress> rfcSorted,
221224
final ProtocolFamilyPreference pref) {
225+
222226
if (rfcSorted.size() <= 1) {
223227
return rfcSorted;
224228
}
229+
225230
switch (pref) {
226231
case PREFER_IPV6:
227232
case PREFER_IPV4: {
@@ -240,8 +245,7 @@ private static List<InetAddress> applyFamilyPreference(
240245
merged.addAll(first);
241246
merged.addAll(second);
242247
if (LOG.isDebugEnabled()) {
243-
LOG.debug("Family preference {} applied. First bucket={}, second bucket={}",
244-
pref, fmt(first), fmt(second));
248+
LOG.debug("Family preference {} applied. First bucket={}, second bucket={}", pref, fmt(first), fmt(second));
245249
LOG.debug("Family preference output: {}", fmt(merged));
246250
}
247251
return merged;
@@ -256,47 +260,11 @@ private static List<InetAddress> applyFamilyPreference(
256260
}
257261
case INTERLEAVE:
258262
default: {
259-
final LinkedList<InetAddress> v6 = new LinkedList<>();
260-
final LinkedList<InetAddress> v4 = new LinkedList<>();
261-
for (final InetAddress a : rfcSorted) {
262-
if (a instanceof Inet6Address) {
263-
v6.add(a);
264-
} else {
265-
v4.add(a);
266-
}
267-
}
268-
if (v6.isEmpty() || v4.isEmpty()) {
269-
if (LOG.isDebugEnabled()) {
270-
LOG.debug("INTERLEAVE requested but only one family present. Order unchanged: {}", fmt(rfcSorted));
271-
}
272-
return rfcSorted;
273-
}
274-
275-
final boolean startV6 = rfcSorted.get(0) instanceof Inet6Address;
276-
final List<InetAddress> merged = new ArrayList<>(rfcSorted.size());
277-
278-
while (!v6.isEmpty() || !v4.isEmpty()) {
279-
if (startV6) {
280-
if (!v6.isEmpty()) {
281-
merged.add(v6.removeFirst());
282-
}
283-
if (!v4.isEmpty()) {
284-
merged.add(v4.removeFirst());
285-
}
286-
} else {
287-
if (!v4.isEmpty()) {
288-
merged.add(v4.removeFirst());
289-
}
290-
if (!v6.isEmpty()) {
291-
merged.add(v6.removeFirst());
292-
}
293-
}
294-
}
295-
263+
// No family bias. Keep RFC 6724 order intact.
296264
if (LOG.isDebugEnabled()) {
297-
LOG.debug("INTERLEAVE starting family={} -> {}", startV6 ? "IPv6" : "IPv4", fmt(merged));
265+
LOG.debug("INTERLEAVE treated as no-bias. Order unchanged: {}", fmt(rfcSorted));
298266
}
299-
return merged;
267+
return rfcSorted;
300268
}
301269
}
302270
}
@@ -408,6 +376,7 @@ private static Scope classifyScope(final InetAddress ip) {
408376
}
409377
if (ip.isMulticastAddress()) {
410378
if (ip instanceof Inet6Address) {
379+
// RFC 4291: low 4 bits of second byte are scope.
411380
return Scope.fromValue(ip.getAddress()[1] & 0x0f);
412381
}
413382
return Scope.GLOBAL;
@@ -531,8 +500,6 @@ private static PolicyEntry classify(final InetAddress ip) {
531500
return preferDB;
532501
}
533502

534-
// TODO Rule 3 & 4: skipped
535-
536503
// Rule 5: Prefer matching label.
537504
if (attrSourceDA.label == attrDA.label && attrSourceDB.label != attrDB.label) {
538505
return preferDA;
@@ -549,8 +516,6 @@ private static PolicyEntry classify(final InetAddress ip) {
549516
return preferDB;
550517
}
551518

552-
// TODO Rule 7: skipped
553-
554519
// Rule 8: Prefer smaller scope.
555520
if (attrDA.scope.value < attrDB.scope.value) {
556521
return preferDA;

httpclient5/src/test/java/org/apache/hc/client5/http/Rfc6724AddressSelectingDnsResolverTest.java

Lines changed: 35 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
*/
2727
package org.apache.hc.client5.http;
2828

29+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
2930
import static org.junit.jupiter.api.Assertions.assertEquals;
3031
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
31-
import static org.junit.jupiter.api.Assertions.assertNotEquals;
3232
import static org.mockito.Mockito.when;
3333

3434
import java.net.Inet4Address;
@@ -103,71 +103,59 @@ void ipv4Only_emptyWhenNoIPv4Candidates() throws Exception {
103103
}
104104

105105
@Test
106-
void preferIpv6_groupsAllV6First_preservingRelativeOrder() throws Exception {
107-
final InetAddress v4a = InetAddress.getByName("192.0.2.1");
106+
void interleave_isDefault_and_hasNoFamilyBias() throws Exception {
108107
final InetAddress v6a = InetAddress.getByName("2001:db8::1");
109-
final InetAddress v4b = InetAddress.getByName("203.0.113.10");
110108
final InetAddress v6b = InetAddress.getByName("2001:db8::2");
109+
final InetAddress v4a = InetAddress.getByName("192.0.2.1");
110+
final InetAddress v4b = InetAddress.getByName("203.0.113.10");
111111

112-
when(delegate.resolve("dual.example")).thenReturn(new InetAddress[]{v4a, v6a, v4b, v6b});
113-
114-
final Rfc6724AddressSelectingDnsResolver r =
115-
new Rfc6724AddressSelectingDnsResolver(delegate, ProtocolFamilyPreference.PREFER_IPV6);
112+
when(delegate.resolve("dual.example")).thenReturn(new InetAddress[]{v6a, v6b, v4a, v4b});
116113

117-
final InetAddress[] out = r.resolve("dual.example");
114+
final Rfc6724AddressSelectingDnsResolver rDefault = new Rfc6724AddressSelectingDnsResolver(delegate);
115+
final Rfc6724AddressSelectingDnsResolver rInterleave =
116+
new Rfc6724AddressSelectingDnsResolver(delegate, ProtocolFamilyPreference.INTERLEAVE);
118117

119-
// all v6 first, in original relative order
120-
final List<InetAddress> v6Seen = new ArrayList<>();
121-
final List<InetAddress> v4Seen = new ArrayList<>();
122-
Assertions.assertNotNull(out);
123-
for (final InetAddress a : out) {
124-
if (a instanceof Inet6Address) {
125-
v6Seen.add(a);
126-
} else {
127-
v4Seen.add(a);
128-
}
129-
}
130-
assertEquals(Arrays.asList(v6a, v6b), v6Seen);
131-
assertEquals(Arrays.asList(v4a, v4b), v4Seen);
118+
final InetAddress[] outDefault = rDefault.resolve("dual.example");
119+
final InetAddress[] outInterleave = rInterleave.resolve("dual.example");
132120

133-
// ensure first element is IPv6 (grouping actually happened)
134-
assertInstanceOf(Inet6Address.class, out[0]);
121+
assertArrayEquals(outDefault, outInterleave);
122+
Assertions.assertNotNull(outInterleave);
123+
assertEquals(4, outInterleave.length);
135124
}
136125

137126
@Test
138-
void interleave_alternatesFamilies_and_preservesRelativeOrder() throws Exception {
139-
final InetAddress v6a = InetAddress.getByName("2001:db8::1");
140-
final InetAddress v6b = InetAddress.getByName("2001:db8::2");
127+
void preferIpv6_groupsAllV6First_preservingRelativeOrder() throws Exception {
141128
final InetAddress v4a = InetAddress.getByName("192.0.2.1");
129+
final InetAddress v6a = InetAddress.getByName("2001:db8::1");
142130
final InetAddress v4b = InetAddress.getByName("203.0.113.10");
131+
final InetAddress v6b = InetAddress.getByName("2001:db8::2");
143132

144-
when(delegate.resolve("dual.example")).thenReturn(new InetAddress[]{v6a, v6b, v4a, v4b});
133+
when(delegate.resolve("dual.example")).thenReturn(new InetAddress[]{v4a, v6a, v4b, v6b});
145134

146-
final Rfc6724AddressSelectingDnsResolver r =
135+
final Rfc6724AddressSelectingDnsResolver baseline =
147136
new Rfc6724AddressSelectingDnsResolver(delegate, ProtocolFamilyPreference.INTERLEAVE);
137+
final InetAddress[] baseOut = baseline.resolve("dual.example");
148138

149-
final InetAddress[] out = r.resolve("dual.example");
139+
final Rfc6724AddressSelectingDnsResolver preferV6 =
140+
new Rfc6724AddressSelectingDnsResolver(delegate, ProtocolFamilyPreference.PREFER_IPV6);
141+
final InetAddress[] out = preferV6.resolve("dual.example");
150142

151-
// Preserve per-family relative order
152-
final List<InetAddress> v6Seen = new ArrayList<>();
153-
final List<InetAddress> v4Seen = new ArrayList<>();
154-
Assertions.assertNotNull(out);
155-
for (final InetAddress a : out) {
143+
// Expected: stable partition of the RFC-sorted baseline.
144+
final List<InetAddress> baseV6 = new ArrayList<>();
145+
final List<InetAddress> baseV4 = new ArrayList<>();
146+
for (final InetAddress a : baseOut) {
156147
if (a instanceof Inet6Address) {
157-
v6Seen.add(a);
148+
baseV6.add(a);
158149
} else {
159-
v4Seen.add(a);
150+
baseV4.add(a);
160151
}
161152
}
162-
assertEquals(Arrays.asList(v6a, v6b), v6Seen);
163-
assertEquals(Arrays.asList(v4a, v4b), v4Seen);
164-
165-
// Alternation (as far as both families have remaining items)
166-
final int pairs = Math.min(v6Seen.size(), v4Seen.size());
167-
for (int i = 1; i < pairs * 2; i++) {
168-
assertNotEquals(out[i - 1] instanceof Inet6Address, out[i] instanceof Inet6Address,
169-
"adjacent entries should alternate family under INTERLEAVE");
170-
}
153+
final List<InetAddress> expected = new ArrayList<>(baseOut.length);
154+
expected.addAll(baseV6);
155+
expected.addAll(baseV4);
156+
157+
assertEquals(expected, Arrays.asList(out));
158+
assertInstanceOf(Inet6Address.class, out[0]);
171159
}
172160

173161
@Test
@@ -178,4 +166,5 @@ void canonicalHostname_delegates() throws Exception {
178166
assertEquals("canon.example.org", r.resolveCanonicalHostname("example.org"));
179167
Mockito.verify(delegate).resolveCanonicalHostname("example.org");
180168
}
169+
181170
}

0 commit comments

Comments
 (0)