5050import org .slf4j .LoggerFactory ;
5151
5252/**
53- * {@code Rfc6724AddressSelectingDnsResolver } wraps a delegate {@link DnsResolver}
53+ * {@code AddressSelectingDnsResolver } wraps a delegate {@link DnsResolver}
5454 * and applies RFC 6724 destination address selection rules (RFC 6724 §6)
5555 * to the returned addresses. It can also enforce or bias a protocol family preference.
5656 *
@@ -185,16 +185,10 @@ private static List<InetAddress> filterCandidates(
185185 final InetAddress [] resolved ,
186186 final ProtocolFamilyPreference pref ) {
187187
188- final List <InetAddress > out = new ArrayList <>(resolved .length );
189- for (final InetAddress a : resolved ) {
190- if (!isUsableDestination (a )) {
191- continue ;
192- }
193- if (shouldInclude (pref , a )) {
194- out .add (a );
195- }
196- }
197- return out ;
188+ return Arrays .stream (resolved )
189+ .filter (AddressSelectingDnsResolver ::isUsableDestination )
190+ .filter (a -> shouldInclude (pref , a ))
191+ .collect (Collectors .toList ());
198192 }
199193
200194 private static boolean shouldInclude (final ProtocolFamilyPreference pref , final InetAddress a ) {
@@ -220,17 +214,9 @@ private List<InetAddress> sortByRfc6724(final List<InetAddress> addrs) {
220214 LOG .trace ("RFC6724 input candidates: {}" , fmt (addrs ));
221215 }
222216
223- final List <InetSocketAddress > socketAddresses = new ArrayList <>(addrs .size ());
224- for (final InetAddress a : addrs ) {
225- socketAddresses .add (new InetSocketAddress (a , PROBE_PORT ));
226- }
227-
228- final List <InetAddress > srcs = inferSourceAddresses (socketAddresses );
229-
230217 final List <Info > infos = new ArrayList <>(addrs .size ());
231- for (int i = 0 ; i < addrs .size (); i ++) {
232- final InetAddress dst = addrs .get (i );
233- final InetAddress src = srcs .get (i );
218+ for (final InetAddress dst : addrs ) {
219+ final InetAddress src = inferSourceAddress (new InetSocketAddress (dst , PROBE_PORT ));
234220 infos .add (new Info (dst , src , ipAttrOf (dst ), ipAttrOf (src )));
235221 }
236222
@@ -245,10 +231,9 @@ private List<InetAddress> sortByRfc6724(final List<InetAddress> addrs) {
245231
246232 infos .sort (RFC6724_COMPARATOR );
247233
248- final List <InetAddress > out = new ArrayList <>(infos .size ());
249- for (final Info info : infos ) {
250- out .add (info .dst );
251- }
234+ final List <InetAddress > out = infos .stream ()
235+ .map (info -> info .dst )
236+ .collect (Collectors .toList ());
252237
253238 if (LOG .isTraceEnabled ()) {
254239 LOG .trace ("RFC6724 output order: {}" , fmt (out ));
@@ -257,30 +242,15 @@ private List<InetAddress> sortByRfc6724(final List<InetAddress> addrs) {
257242 return out ;
258243 }
259244
260- private List <InetAddress > inferSourceAddresses (final List <InetSocketAddress > destinations ) {
261- final List <InetAddress > srcs = new ArrayList <>(destinations .size ());
262-
263- for (final InetSocketAddress dest : destinations ) {
264- InetAddress src = null ;
265- try {
266- src = sourceAddressResolver .resolveSource (dest );
267- } catch (final SocketException ignore ) {
268- if (LOG .isTraceEnabled ()) {
269- LOG .trace ("RFC6724 could not infer source address for {}: {}" , dest , ignore .toString ());
270- }
271- }
272- srcs .add (src );
273- }
274-
275- if (LOG .isTraceEnabled ()) {
276- final List <String > printable = new ArrayList <>(srcs .size ());
277- for (final InetAddress a : srcs ) {
278- printable .add (addr (a ));
245+ private InetAddress inferSourceAddress (final InetSocketAddress destination ) {
246+ try {
247+ return sourceAddressResolver .resolveSource (destination );
248+ } catch (final SocketException ex ) {
249+ if (LOG .isTraceEnabled ()) {
250+ LOG .trace ("RFC6724 could not infer source address for {}: {}" , destination , ex .toString ());
279251 }
280- LOG . trace ( "RFC6724 inferred source addresses: {}" , printable ) ;
252+ return null ;
281253 }
282-
283- return srcs ;
284254 }
285255
286256 private static List <InetAddress > applyFamilyPreference (
@@ -388,7 +358,8 @@ private static final class Attr {
388358 }
389359 }
390360
391- private enum Scope {
361+ // Package-private for unit testing.
362+ enum Scope {
392363 INTERFACE_LOCAL (0x1 ),
393364 LINK_LOCAL (0x2 ),
394365 ADMIN_LOCAL (0x4 ),
@@ -419,9 +390,12 @@ static Scope fromValue(final int v) {
419390 case 0x8 : {
420391 return ORG_LOCAL ;
421392 }
422- default : {
393+ case 0xe : {
423394 return GLOBAL ;
424395 }
396+ default : {
397+ throw new IllegalArgumentException ("Unknown scope value: 0x" + Integer .toHexString (v ));
398+ }
425399 }
426400 }
427401 }
@@ -434,7 +408,8 @@ private static Attr ipAttrOf(final InetAddress ip) {
434408 return new Attr (classifyScope (ip ), e .precedence , e .label );
435409 }
436410
437- private static Scope classifyScope (final InetAddress ip ) {
411+ // Package-private for unit testing.
412+ static Scope classifyScope (final InetAddress ip ) {
438413 if (ip .isLoopbackAddress ()) {
439414 return Scope .INTERFACE_LOCAL ;
440415 }
@@ -443,8 +418,14 @@ private static Scope classifyScope(final InetAddress ip) {
443418 }
444419 if (ip .isMulticastAddress ()) {
445420 if (ip instanceof Inet6Address ) {
446- // RFC 6724 §3.1 and RFC 4291: low 4 bits of second byte are scope for IPv6 multicast.
447- return Scope .fromValue (ip .getAddress ()[1 ] & 0x0f );
421+ // RFC 6724 §3.1 and RFC 4291: low 4 bits of second byte encode scope for IPv6 multicast.
422+ // Not all nibble values map to a known Scope constant; treat unknown values as GLOBAL.
423+ final int nibble = ip .getAddress ()[1 ] & 0x0f ;
424+ try {
425+ return Scope .fromValue (nibble );
426+ } catch (final IllegalArgumentException e ) {
427+ return Scope .GLOBAL ;
428+ }
448429 }
449430 return Scope .GLOBAL ;
450431 }
@@ -466,7 +447,8 @@ private static final class PolicyEntry {
466447 }
467448 }
468449
469- private static final class Network {
450+ // Package-private for unit testing.
451+ static final class Network {
470452 final byte [] ip ;
471453 final int bits ;
472454
@@ -549,6 +531,11 @@ private static PolicyEntry classify(final InetAddress ip) {
549531 final int preferB = 1 ;
550532
551533 // RFC 6724 §6: destination address selection rules.
534+ //
535+ // Rules 3, 4 and 7 are not implementable with standard JDK APIs:
536+ // Rule 3 (Avoid deprecated source addresses) — InetAddress exposes no deprecation state.
537+ // Rule 4 (Prefer home addresses) — Mobile IPv6 concept; not applicable to an HTTP client.
538+ // Rule 7 (Prefer native transport) — JDK provides no encapsulation/tunneling info.
552539
553540 // Rule 1: Avoid unusable destinations.
554541 final boolean validA = aSrc != null && !aSrc .isAnyLocalAddress ();
@@ -571,6 +558,10 @@ private static PolicyEntry classify(final InetAddress ip) {
571558 return preferB ;
572559 }
573560
561+ // Rule 3: Avoid deprecated addresses — skipped (see above).
562+
563+ // Rule 4: Prefer home addresses — skipped (see above).
564+
574565 // Rule 5: Prefer matching label.
575566 if (aSrcAttr .label == aDstAttr .label && bSrcAttr .label != bDstAttr .label ) {
576567 return preferA ;
@@ -587,6 +578,8 @@ private static PolicyEntry classify(final InetAddress ip) {
587578 return preferB ;
588579 }
589580
581+ // Rule 7: Prefer native transport — skipped (see above).
582+
590583 // Rule 8: Prefer smaller scope.
591584 if (aDstAttr .scope .value < bDstAttr .scope .value ) {
592585 return preferA ;
@@ -595,7 +588,7 @@ private static PolicyEntry classify(final InetAddress ip) {
595588 return preferB ;
596589 }
597590
598- // Rule 9: Longest matching prefix (IPv6 only).
591+ // Rule 9: Longest matching prefix (IPv6 only, per RFC 6724 §6 ).
599592 if (aDst instanceof Inet6Address && bDst instanceof Inet6Address ) {
600593 final int commonA = commonPrefixLen (aSrc , aDst );
601594 final int commonB = commonPrefixLen (bSrc , bDst );
@@ -645,19 +638,15 @@ static String addr(final InetAddress a) {
645638 }
646639
647640 static List <String > fmt (final InetAddress [] arr ) {
648- final List <String > out = new ArrayList <>(arr .length );
649- for (final InetAddress a : arr ) {
650- out .add (addr (a ));
651- }
652- return out ;
641+ return Arrays .stream (arr )
642+ .map (AddressSelectingDnsResolver ::addr )
643+ .collect (Collectors .toList ());
653644 }
654645
655646 static List <String > fmt (final List <InetAddress > arr ) {
656- final List <String > out = new ArrayList <>(arr .size ());
657- for (final InetAddress a : arr ) {
658- out .add (addr (a ));
659- }
660- return out ;
647+ return arr .stream ()
648+ .map (AddressSelectingDnsResolver ::addr )
649+ .collect (Collectors .toList ());
661650 }
662651
663652}
0 commit comments