ok2c commented on code in PR #430:
URL: 
https://github.com/apache/httpcomponents-client/pull/430#discussion_r1162559293


##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/routing/DistributedProxySelector.java:
##########
@@ -0,0 +1,183 @@
+/*
+ * ====================================================================
+ * 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.routing;
+
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A DistributedProxySelector is a custom {@link ProxySelector} implementation 
that
+ * delegates proxy selection to a list of underlying ProxySelectors in a
+ * distributed manner. It ensures that proxy selection is load-balanced
+ * across the available ProxySelectors, and provides thread safety by
+ * maintaining separate states for each thread.
+ *
+ * <p>The DistributedProxySelector class maintains a list of ProxySelectors,
+ * a {@link ThreadLocal} variable for the current {@link ProxySelector}, and 
an {@link AtomicInteger}
+ * to keep track of the shared index across all threads. When the select()
+ * method is called, it delegates the proxy selection to the current
+ * ProxySelector or the next available one in the list if the current one
+ * returns an empty proxy list. Any exceptions that occur during proxy
+ * selection are caught and ignored, and the next ProxySelector is tried.
+ *
+ * <p>The connectFailed() method notifies the active {@link ProxySelector} of a
+ * connection failure, allowing the underlying ProxySelector to handle
+ * connection failures according to its own logic.
+ *
+ * @since 5.3
+ */
+@Contract(threading = ThreadingBehavior.SAFE)
+public class DistributedProxySelector extends ProxySelector {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(DistributedProxySelector.class);
+
+    /**
+     * A list of {@link ProxySelector} instances to be used by the 
DistributedProxySelector
+     * for selecting proxies.
+     */
+    private final List<ProxySelector> selectors;
+
+    /**
+     * A {@link ThreadLocal} variable holding the current {@link 
ProxySelector} for each thread,
+     * ensuring thread safety when accessing the current {@link ProxySelector}.
+     */
+    private final ThreadLocal<ProxySelector> currentSelector;
+
+    /**
+     * An {@link AtomicInteger} representing the shared index across all 
threads for
+     * maintaining the current position in the list of ProxySelectors, ensuring
+     * proper distribution of {@link ProxySelector} usage.
+     */
+    private final AtomicInteger sharedIndex;
+
+
+    /**
+     * Constructs a DistributedProxySelector with the given list of {@link 
ProxySelector}.
+     * The constructor initializes the currentSelector as a {@link 
ThreadLocal}, and
+     * the sharedIndex as an {@link AtomicInteger}.
+     *
+     * @param selectors the list of ProxySelectors to use.
+     * @throws IllegalArgumentException if the list is null or empty.
+     */
+    public DistributedProxySelector(final List<ProxySelector> selectors) {
+        if (selectors == null || selectors.isEmpty()) {
+            throw new IllegalArgumentException("At least one ProxySelector is 
required");
+        }
+        this.selectors = new ArrayList<>(selectors);
+        this.currentSelector = new ThreadLocal<>();
+        this.sharedIndex = new AtomicInteger();
+    }
+
+    /**
+     * Selects a list of proxies for the given {@link URI} by delegating to 
the current
+     * {@link ProxySelector} or the next available {@link ProxySelector} in 
the list if the current
+     * one returns an empty proxy list. If an {@link Exception} occurs, it 
will be caught
+     * and ignored, and the next {@link ProxySelector} will be tried.
+     *
+     * @param uri the {@link URI} to select a proxy for.
+     * @return a list of proxies for the given {@link URI}.
+     */
+    @Override
+    public List<Proxy> select(final URI uri) {
+        List<Proxy> result = Collections.emptyList();
+        ProxySelector selector = currentSelector.get();
+
+        for (int i = 0; i < selectors.size(); i++) {
+            if (selector == null) {
+                selector = nextSelector();
+                currentSelector.set(selector);
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Current selector is null, selecting next proxy 
selector for URI {}: {}", uri, selector);
+                }
+            }
+            try {
+                result = selector.select(uri);

Review Comment:
   @arturobernalg Almost there. Please store the current selector only for the 
duration of the `result = selector.select(uri)` call and unset it immediately 
after (or in case of a failure in `#connectFailed`). The main idea is to use 
thread local only as short as possible and unset it as soon as possible to 
avoid thread local leaks.



-- 
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