rschmitt commented on code in PR #693:
URL: 
https://github.com/apache/httpcomponents-client/pull/693#discussion_r2268498321


##########
httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientHappyEyeballs.java:
##########
@@ -0,0 +1,158 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.examples;
+
+import java.util.concurrent.Future;
+
+import org.apache.hc.client5.http.SystemDefaultDnsResolver;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
+import org.apache.hc.client5.http.async.methods.SimpleRequestProducer;
+import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
+import 
org.apache.hc.client5.http.impl.nio.HappyEyeballsV2AsyncClientConnectionOperator;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
+import 
org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.config.Registry;
+import org.apache.hc.core5.http.config.RegistryBuilder;
+import org.apache.hc.core5.http.message.StatusLine;
+import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.io.CloseMode;
+
+/**
+ * Example demonstrating how to enable <em>Happy&nbsp;Eyeballs v2</em> 
(RFC&nbsp;8305)
+ * in the async client with RFC&nbsp;6724 address selection and on-demand TLS 
upgrade.
+ *
+ * <p><strong>What this example shows</strong></p>
+ * <ul>
+ *   <li>Creating a {@link HappyEyeballsV2AsyncClientConnectionOperator} that:
+ *     <ul>
+ *       <li>resolves A/AAAA via {@link SystemDefaultDnsResolver},</li>
+ *       <li>orders candidates per RFC&nbsp;6724,</li>
+ *       <li>stagger-dials dual-stack targets (≈50&nbsp;ms opposite-family 
kick, then 250&nbsp;ms pacing), and</li>
+ *       <li>cancels outstanding attempts once one connects.</li>
+ *     </ul>
+ *   </li>
+ *   <li>Registering an HTTPS {@link TlsStrategy} and wiring it into the 
operator so that
+ *       HTTPS routes are upgraded to TLS during connect.</li>
+ *   <li>Executing one HTTP and one HTTPS request to a dual-stack host.</li>
+ * </ul>
+ *
+ * <p><strong>Running</strong></p>
+ * <ol>
+ *   <li>Ensure logging is configured (e.g., log4j-slf4j) if you want to see
+ *       <code>HEv2: dialing/connected</code> debug lines.</li>
+ *   <li>Run the {@code main}. You should see the HTTP request (often 301 to 
HTTPS)
+ *       followed by an HTTPS request with a TLS handshake driven by the 
configured {@link TlsStrategy}.</li>
+ * </ol>
+ *
+ * <p><strong>Knobs you can tweak</strong></p>
+ * <ul>
+ *   <li>Use the constructor {@code new 
HappyEyeballsV2AsyncClientConnectionOperator(dns, attemptDelayMillis, 
scheduler, tlsRegistry)}
+ *       if you want a custom pacing interval or to supply your own 
scheduler.</li>
+ *   <li>Swap {@link SystemDefaultDnsResolver} for a custom {@code 
DnsResolver} if needed.</li>
+ *   <li>Customize the {@link ClientTlsStrategyBuilder} to control trust 
material, SNI, ALPN, etc.</li>
+ * </ul>
+ *
+ * <p><strong>References</strong></p>
+ * <ul>
+ *   <li>RFC&nbsp;8305: Happy Eyeballs Version 2</li>
+ *   <li>RFC&nbsp;6724: Default Address Selection for IPv6</li>
+ * </ul>
+ *
+ * @since 5.6
+ */
+public final class AsyncClientHappyEyeballs {

Review Comment:
   Remember, I added a runner for these:
   
   ```
   $ ./run-example.sh AsyncClientHappyEyeballs
   Executing request GET http://ipv6-test.com/
   GET http://ipv6-test.com/->HTTP/1.1 400 Bad Request
   Executing request GET https://ipv6-test.com/
   SimpleBody{content length=442, content type=text/html; charset=iso-8859-1}
   GET https://ipv6-test.com/->HTTP/1.1 400 Bad Request
   SimpleBody{content length=442, content type=text/html; charset=iso-8859-1}
   Shutting down
   ```
   
   The runner can pass in command line arguments, so it'd be nice if this class 
could take a URI as an argument, e.g.:
   
   ```
   $ ./run-example.sh AsyncClientHappyEyeballs http://neverssl.com/
   ```



##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/HappyEyeballsV2AsyncClientConnectionOperator.java:
##########
@@ -0,0 +1,821 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.client5.http.impl.nio;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.DatagramSocket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.hc.client5.http.DnsResolver;
+import org.apache.hc.client5.http.nio.AsyncClientConnectionOperator;
+import org.apache.hc.client5.http.nio.ManagedAsyncClientConnection;
+import org.apache.hc.core5.annotation.Experimental;
+import org.apache.hc.core5.annotation.Internal;
+import org.apache.hc.core5.concurrent.BasicFuture;
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.config.Lookup;
+import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.io.ModalCloseable;
+import org.apache.hc.core5.net.NamedEndpoint;
+import org.apache.hc.core5.reactor.ConnectionInitiator;
+import org.apache.hc.core5.reactor.IOSession;
+import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer;
+import org.apache.hc.core5.util.Args;
+import org.apache.hc.core5.util.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * {@code HappyEyeballsV2AsyncClientConnectionOperator} is an
+ * {@link AsyncClientConnectionOperator} implementation that applies the
+ * <em>Happy&nbsp;Eyeballs Version&nbsp;2</em> algorithm (RFC&nbsp;8305) to
+ * outbound connections.
+ *
+ * <p>The operator
+ * <ol>
+ *   <li>resolves <em>A</em> and <em>AAAA</em> records via the provided
+ *       {@link DnsResolver},</li>
+ *   <li>orders the resulting addresses according to the RFC&nbsp;6724
+ *       default-address-selection rules,</li>
+ *   <li>initiates a first connection attempt immediately, launches the
+ *       first address of the <strong>other family</strong> after
+ *       ≈ 50 ms, and then paces subsequent attempts every 250 ms
+ *       (configurable),</li>
+ *   <li>cancels all remaining attempts once one socket connects, and</li>
+ *   <li>reports the <em>last</em> exception if every attempt fails.</li>
+ * </ol>
+ *
+ * <h3>Usage</h3>
+ * <pre>{@code
+ * DnsResolver dns = SystemDefaultDnsResolver.INSTANCE;
+ * AsyncClientConnectionOperator he =
+ *         new HappyEyeballsV2AsyncClientConnectionOperator(dns, tlsRegistry);
+ *
+ * PoolingAsyncClientConnectionManager cm =
+ *         PoolingAsyncClientConnectionManagerBuilder.create()
+ *             .setConnectionOperator(he)
+ *             .build();
+ * }</pre>
+ *
+ * <h3>Thread-safety</h3>
+ * All instances are immutable and therefore thread-safe; the internal
+ * {@link ScheduledExecutorService} is created with a single
+ * daemon thread.
+ *
+ * <h3>Limitations / TODO</h3>
+ * Rules 3, 4 and 7 of RFC&nbsp;6724 (deprecated addresses, home vs
+ * care-of, native transport) are <strong>not yet implemented</strong>.
+ * Their placeholders are marked with TODO comments.
+ *
+ * @see <a href="https://www.rfc-editor.org/rfc/rfc8305";>RFC&nbsp;8305</a>
+ * @see <a href="https://www.rfc-editor.org/rfc/rfc6724";>RFC&nbsp;6724</a>
+ * @since 5.6
+ */
+@Experimental
+public final class HappyEyeballsV2AsyncClientConnectionOperator
+        implements AsyncClientConnectionOperator, ModalCloseable {

Review Comment:
   I think that `AsyncClientConnectionOperator` is too high-level for this. 
There's unrelated stuff in here like `upgrade`, for one thing. For another, 
there's an interface called `ConnectionInitiator` that seems like it would be a 
really good fit for an algorithm that implements staggered, concurrent, 
asynchronous connection establishment.
   
   In fact, one thing we can consider is teaching the existing 
`MultihomeConnectionInitiator`/`MultihomeIOSessionRequester` to perform 
concurrent connection attempts, rather than sequential ones. That would provide 
the most important and most difficult part of RFC 8305, and it would work with 
any endpoint with multiple IP addresses, irrespective of IPv4/IPv6 
configuration.



##########
httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientHappyEyeballs.java:
##########
@@ -0,0 +1,158 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.examples;
+
+import java.util.concurrent.Future;
+
+import org.apache.hc.client5.http.SystemDefaultDnsResolver;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
+import org.apache.hc.client5.http.async.methods.SimpleRequestProducer;
+import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
+import 
org.apache.hc.client5.http.impl.nio.HappyEyeballsV2AsyncClientConnectionOperator;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
+import 
org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.config.Registry;
+import org.apache.hc.core5.http.config.RegistryBuilder;
+import org.apache.hc.core5.http.message.StatusLine;
+import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.io.CloseMode;
+
+/**
+ * Example demonstrating how to enable <em>Happy&nbsp;Eyeballs v2</em> 
(RFC&nbsp;8305)
+ * in the async client with RFC&nbsp;6724 address selection and on-demand TLS 
upgrade.
+ *
+ * <p><strong>What this example shows</strong></p>
+ * <ul>
+ *   <li>Creating a {@link HappyEyeballsV2AsyncClientConnectionOperator} that:
+ *     <ul>
+ *       <li>resolves A/AAAA via {@link SystemDefaultDnsResolver},</li>
+ *       <li>orders candidates per RFC&nbsp;6724,</li>
+ *       <li>stagger-dials dual-stack targets (≈50&nbsp;ms opposite-family 
kick, then 250&nbsp;ms pacing), and</li>
+ *       <li>cancels outstanding attempts once one connects.</li>
+ *     </ul>
+ *   </li>
+ *   <li>Registering an HTTPS {@link TlsStrategy} and wiring it into the 
operator so that
+ *       HTTPS routes are upgraded to TLS during connect.</li>
+ *   <li>Executing one HTTP and one HTTPS request to a dual-stack host.</li>
+ * </ul>
+ *
+ * <p><strong>Running</strong></p>
+ * <ol>
+ *   <li>Ensure logging is configured (e.g., log4j-slf4j) if you want to see
+ *       <code>HEv2: dialing/connected</code> debug lines.</li>
+ *   <li>Run the {@code main}. You should see the HTTP request (often 301 to 
HTTPS)
+ *       followed by an HTTPS request with a TLS handshake driven by the 
configured {@link TlsStrategy}.</li>
+ * </ol>
+ *
+ * <p><strong>Knobs you can tweak</strong></p>
+ * <ul>
+ *   <li>Use the constructor {@code new 
HappyEyeballsV2AsyncClientConnectionOperator(dns, attemptDelayMillis, 
scheduler, tlsRegistry)}
+ *       if you want a custom pacing interval or to supply your own 
scheduler.</li>
+ *   <li>Swap {@link SystemDefaultDnsResolver} for a custom {@code 
DnsResolver} if needed.</li>
+ *   <li>Customize the {@link ClientTlsStrategyBuilder} to control trust 
material, SNI, ALPN, etc.</li>
+ * </ul>
+ *
+ * <p><strong>References</strong></p>
+ * <ul>
+ *   <li>RFC&nbsp;8305: Happy Eyeballs Version 2</li>
+ *   <li>RFC&nbsp;6724: Default Address Selection for IPv6</li>
+ * </ul>
+ *
+ * @since 5.6
+ */
+public final class AsyncClientHappyEyeballs {
+
+    public static void main(final String[] args) throws Exception {
+        final SystemDefaultDnsResolver dnsResolver = new 
SystemDefaultDnsResolver();
+
+        final TlsStrategy tls = ClientTlsStrategyBuilder.create()
+                .useSystemProperties()
+                .build();
+
+        final Registry<TlsStrategy> tlsRegistry = 
RegistryBuilder.<TlsStrategy>create()
+                .register(URIScheme.HTTPS.id, tls)
+                .build();
+
+        final HappyEyeballsV2AsyncClientConnectionOperator operator =
+                new HappyEyeballsV2AsyncClientConnectionOperator(dnsResolver, 
tlsRegistry);
+
+        final PoolingAsyncClientConnectionManager cm = 
PoolingAsyncClientConnectionManagerBuilder.create()
+                .setConnectionOperator(operator)
+                .build();
+
+        final CloseableHttpAsyncClient client = HttpAsyncClients.custom()
+                .setConnectionManager(cm)
+                .build();
+
+        client.start();
+
+        final String[] schemes = {URIScheme.HTTP.id, URIScheme.HTTPS.id};
+        for (final String scheme : schemes) {
+            final SimpleHttpRequest request = SimpleRequestBuilder.get()
+                    .setHttpHost(new HttpHost(scheme, "ipv6-test.com"))
+                    .setPath("/")
+                    .build();
+
+            System.out.println("Executing request " + request);
+            final Future<SimpleHttpResponse> future = client.execute(
+                    SimpleRequestProducer.create(request),
+                    SimpleResponseConsumer.create(),
+                    new FutureCallback<SimpleHttpResponse>() {
+
+                        @Override
+                        public void completed(final SimpleHttpResponse 
response) {

Review Comment:
   Do we have access to the resolved IP address from here? It'd be nice to 
print that



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscr...@hc.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@hc.apache.org
For additional commands, e-mail: dev-h...@hc.apache.org

Reply via email to