Skip to content

Commit 6f5fcce

Browse files
authored
core: Handle ProxySelector IllegalArgumentException (#13053)
Fixes #13052. Android’s DefaultProxySelector.select() can throw IllegalArgumentException when a configured proxy port is invalid, causing an unchecked exception to escape the DNS resolver task. This change wraps that exception in an IOException with the message "Failed when selecting a proxy", preserving the original cause. The resolver’s existing error handling reports UNAVAILABLE.
1 parent cd2e1d9 commit 6f5fcce

3 files changed

Lines changed: 52 additions & 1 deletion

File tree

‎core/src/main/java/io/grpc/internal/ProxyDetectorImpl.java‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,12 @@ private ProxiedSocketAddress detectProxy(InetSocketAddress targetAddr) throws IO
206206
return null;
207207
}
208208

209-
List<Proxy> proxies = proxySelector.select(uri);
209+
List<Proxy> proxies;
210+
try {
211+
proxies = proxySelector.select(uri);
212+
} catch (IllegalArgumentException e) {
213+
throw new IOException("Failed when selecting a proxy", e);
214+
}
210215
// ProxySelector.select(URI) is contractually required to return a non-null, non-empty list.
211216
// Surface the offending implementation's class name so a broken ProxySelector can be fixed.
212217
if (proxies == null || proxies.isEmpty()) {

‎core/src/test/java/io/grpc/internal/DnsNameResolverTest.java‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@
6464
import java.io.IOException;
6565
import java.net.InetAddress;
6666
import java.net.InetSocketAddress;
67+
import java.net.ProxySelector;
6768
import java.net.SocketAddress;
69+
import java.net.URI;
6870
import java.net.UnknownHostException;
6971
import java.util.ArrayList;
7072
import java.util.Arrays;
@@ -546,6 +548,36 @@ public List<InetAddress> resolveAddress(String host) throws Exception {
546548
.isInstanceOf(IOException.class);
547549
}
548550

551+
@Test
552+
public void resolve_proxySelectorThrowsIllegalArgumentException() throws Exception {
553+
IllegalArgumentException cause =
554+
new IllegalArgumentException("port out of range: 899858473");
555+
ProxySelector proxySelector = mock(ProxySelector.class);
556+
when(proxySelector.select(any(URI.class))).thenThrow(cause);
557+
ProxyDetectorImpl.AuthenticationProvider authenticator =
558+
mock(ProxyDetectorImpl.AuthenticationProvider.class);
559+
ProxyDetector proxyDetector = new ProxyDetectorImpl(() -> proxySelector, authenticator);
560+
RetryingNameResolver resolver = newResolver(
561+
"addr.fake:1234", 443, proxyDetector, Stopwatch.createUnstarted());
562+
DnsNameResolver dnsResolver = (DnsNameResolver) resolver.getRetriedNameResolver();
563+
AddressResolver mockAddressResolver = mock(AddressResolver.class);
564+
dnsResolver.setAddressResolver(mockAddressResolver);
565+
566+
resolver.start(mockListener);
567+
assertThat(fakeExecutor.runDueTasks()).isEqualTo(1);
568+
569+
verify(mockListener).onResult2(resultCaptor.capture());
570+
verifyNoMoreInteractions(mockListener);
571+
Status status = resultCaptor.getValue().getAddressesOrError().getStatus();
572+
assertThat(status.getCode()).isEqualTo(Status.Code.UNAVAILABLE);
573+
assertThat(status.getDescription()).isEqualTo("Unable to resolve host addr.fake");
574+
assertThat(status.getCause()).isInstanceOf(IOException.class);
575+
assertThat(status.getCause().getMessage()).isEqualTo("Failed when selecting a proxy");
576+
assertSame(cause, status.getCause().getCause());
577+
verify(proxySelector).select(any(URI.class));
578+
verifyNoMoreInteractions(mockAddressResolver, authenticator);
579+
}
580+
549581
// Load balancer rejects the empty addresses.
550582
@Test
551583
public void resolve_emptyResult_notAccepted() throws Exception {

‎core/src/test/java/io/grpc/internal/ProxyDetectorImplTest.java‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static org.junit.Assert.assertNotEquals;
2222
import static org.junit.Assert.assertNotNull;
2323
import static org.junit.Assert.assertNull;
24+
import static org.junit.Assert.assertSame;
2425
import static org.junit.Assert.assertThrows;
2526
import static org.junit.Assert.assertTrue;
2627
import static org.mockito.ArgumentMatchers.any;
@@ -214,4 +215,17 @@ public void throwsWhenProxySelectorReturnsNullList() throws Exception {
214215
assertTrue(e.getMessage(), e.getMessage().contains("null"));
215216
assertTrue(e.getMessage(), e.getMessage().contains(proxySelector.getClass().getName()));
216217
}
218+
219+
@Test
220+
public void throwsWhenProxySelectorThrowsIllegalArgumentException() throws Exception {
221+
IllegalArgumentException cause =
222+
new IllegalArgumentException("port out of range: 899858473");
223+
when(proxySelector.select(any(URI.class))).thenThrow(cause);
224+
225+
IOException exception =
226+
assertThrows(IOException.class, () -> proxyDetector.proxyFor(destination));
227+
228+
assertSame(cause, exception.getCause());
229+
assertEquals("Failed when selecting a proxy", exception.getMessage());
230+
}
217231
}

0 commit comments

Comments
 (0)