Retrofit升级至2.9.0后PAC文件代理场景下出现Port out of range: -1异常的排查问询
问题分析与排查建议
首先,从你给出的异常栈和场景描述来看,这个问题的核心是OkHttp升级后,处理系统PAC代理时未兼容无端口的代理地址格式,导致端口被解析为-1,触发IllegalArgumentException。下面是具体的排查线索和可落地的解决思路:
排查线索
- OkHttp版本迭代的逻辑变更:Retrofit 2.9.0依赖的OkHttp版本(通常为3.14.x)相比2.6.0依赖的旧版本(3.12.x),在代理选择的实现上做了调整。旧版本可能会自动为无端口的代理地址补全默认端口(HTTP用80、HTTPS用443),但新版本移除了这个兼容逻辑,直接将未指定端口的代理解析为无效的-1端口。
- 系统ProxySelector的PAC解析行为:Android自带的
DefaultProxySelector在处理PAC脚本返回的代理时,如果PAC返回的是PROXY example.com这种不带端口的格式(而非标准的PROXY example.com:80),就会出现端口解析失败的情况。升级后的OkHttp不再对这种非标准格式做兜底处理,直接抛出异常。 - RouteSelector初始化的代理处理逻辑:从异常栈可以定位到问题出在
RouteSelector初始化时调用DefaultProxySelector.select()的环节,返回的代理地址端口无效。你可以对比新旧版本OkHttp中RouteSelector.resetNextProxy()方法的实现,确认是否在代理地址校验逻辑上有变更。
解决建议
- 自定义ProxySelector修复端口问题:实现一个包裹系统默认
ProxySelector的自定义类,在select()方法中对返回的代理进行检查,为无端口的代理补全默认端口。示例代码如下:
public class FixedProxySelector extends ProxySelector { private final ProxySelector delegate; public FixedProxySelector(ProxySelector delegate) { this.delegate = delegate; } @Override public List<Proxy> select(URI uri) { List<Proxy> proxies = delegate.select(uri); List<Proxy> fixedProxies = new ArrayList<>(); for (Proxy proxy : proxies) { if (proxy.type() == Proxy.Type.HTTP) { SocketAddress address = proxy.address(); if (address instanceof InetSocketAddress) { InetSocketAddress inetAddress = (InetSocketAddress) address; if (inetAddress.getPort() == -1) { // 根据请求协议补全默认端口 int defaultPort = uri.getScheme().equals("https") ? 443 : 80; InetSocketAddress fixedAddress = new InetSocketAddress(inetAddress.getHostName(), defaultPort); fixedProxies.add(new Proxy(Proxy.Type.HTTP, fixedAddress)); } else { fixedProxies.add(proxy); } } else { fixedProxies.add(proxy); } } else { fixedProxies.add(proxy); } } return fixedProxies; } @Override public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { delegate.connectFailed(uri, sa, ioe); } }
然后在OkHttpClient构建时配置这个自定义Selector:
OkHttpClient client = new OkHttpClient.Builder() .proxySelector(new FixedProxySelector(ProxySelector.getDefault())) .build();
- 强制忽略系统代理,手动处理PAC:如果你的应用原本就计划使用自定义PAC逻辑而非系统代理,可以直接让OkHttp跳过系统代理检测,自行实现PAC解析并设置代理:
OkHttpClient client = new OkHttpClient.Builder() .proxy(Proxy.NO_PROXY) // 这里添加你的PAC解析逻辑,获取有效代理后设置 .build();
- 校验PAC脚本的返回格式:检查你的PAC脚本,确保返回的代理地址是标准格式(如
PROXY your-proxy-host:8080;),避免省略端口导致的解析问题。
异常栈信息:
Fatal Exception: java.lang.IllegalArgumentException: port out of range:-1 at java.net.InetSocketAddress.checkPort(InetSocketAddress.java:144) at java.net.InetSocketAddress.createUnresolved(InetSocketAddress.java:269) at sun.net.spi.DefaultProxySelector$1.run(DefaultProxySelector.java:315) at sun.net.spi.DefaultProxySelector$1.run(DefaultProxySelector.java:219) at java.security.AccessController.doPrivileged(AccessController.java:43) at sun.net.spi.DefaultProxySelector.select(DefaultProxySelector.java:218) at okhttp3.internal.connection.RouteSelector$resetNextProxy$1.invoke(RouteSelector.java:106) at okhttp3.internal.connection.RouteSelector.resetNextProxy(RouteSelector.java:113) at okhttp3.internal.connection.RouteSelector.<init>(RouteSelector.java:53) at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.java:209) at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.java:108) at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.java:76) at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.java:245) at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:32) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:100) at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:96) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:100) at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:83) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:100) at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:76) at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:100)
内容的提问来源于stack exchange,提问作者vandus
相关产品推荐
相关产品推荐

