You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 10:49:08