使用 SPDY 版本 3 在 OKHttpClient 版本 3.2.0 中引发的错误

Posted

技术标签:

【中文标题】使用 SPDY 版本 3 在 OKHttpClient 版本 3.2.0 中引发的错误【英文标题】:Error thrown in OKHttpClient version 3.2.0 using SPDY version 3 【发布时间】:2016-07-30 00:05:13 【问题描述】:

我有一个应用程序使用:

compile 'com.squareup.okhttp3:okhttp:2.7.0'
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.2.0'
compile 'com.squareup.okio:okio:1.6.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3'

并对服务器进行 API REST 调用。在 OKHttpClient 上启用 SPDY 3 时,会抛出错误:

04-08 11:01:22.321 10628-10628/dts W/System.err: java.lang.IllegalArgumentException: protocols doesn't contain http/1.1: [spdy/3.1, h2]
04-08 11:01:22.321 10628-10628/dts W/System.err:  at okhttp3.OkHttpClient$Builder.protocols(OkHttpClient.java:672)

我在 OKHttpClient 上找不到正确启用 SPDY/3 的任何内容或错误代码。目标站点已正确配置 SPDY/3。

我已尝试使用 SPDY3、HTTP/2 和 HTTP/1 的所有组合的两种协议配置,应用程序在配置 HTTP/1 时工作,但如果仅启用 SPDY/3 则会引发错误:

okHttpClient = new OkHttpClient.Builder()
                    // .protocols(Collections.singletonList(Protocol.SPDY_3))
                    .protocols(Arrays.asList(Protocol.SPDY_3, Protocol.HTTP_2))
                    .retryOnConnectionFailure(true)
                    .connectTimeout(25, TimeUnit.SECONDS)
                    .connectionPool(new ConnectionPool(30, 120, TimeUnit.SECONDS))
                    .connectionSpecs(Collections.singletonList(spec))
                    .addInterceptor(new AddCookiesInterceptor())
                    .addInterceptor(new ReceivedCookiesInterceptor())
                    .cache(new okhttp3.Cache(cacheLocation, (500 * 1024 * 1024)))
                    .build();
        

我知道有人可能有想法,任何帮助将不胜感激。

【问题讨论】:

我确实在文档link 中找到了这个,但对配置 SPDY/3 没有帮助: 参数:协议 - 要使用的协议,按优先顺序排列。该列表必须包含 Protocol.HTTP_1_1。它不能包含 null 或 Protocol.HTTP_1_0。 【参考方案1】:

显然,最新版本的 OKHttp 放弃了对 SPDY/3 的支持。仅选择 proto SPDY 或 HTTP2 时会抛出错误代码。文档指出,始终需要 HTTP1 作为 proto 以及之后的其他选择。使用 android 4.1 切换到 HTTP2,您会发现更多连接问题(OKHttp/Android)不支持开箱即用的 TLS1.2,您必须创建自定义 SSLSocketFactory 以覆盖默认值(来自 here ):

public class TLSSocketFactory extends SSLSocketFactory 

private SSLSocketFactory delegate;

public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException 
    SSLContext context = SSLContext.getInstance("TLS");
    context.init(null, null, null);
    delegate = context.getSocketFactory();


@Override
public String[] getDefaultCipherSuites() 
    return new String[]"TLSv1.2", "TLS_RSA_WITH_AES_128_CBC_SHA";


@Override
public String[] getSupportedCipherSuites() 
    return new String[]"TLSv1.2", "TLS_RSA_WITH_AES_128_CBC_SHA";


@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException 
    return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose));


@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException 
    return enableTLSOnSocket(delegate.createSocket(host, port));


@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException 
    return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort));


@Override
public Socket createSocket(InetAddress host, int port) throws IOException 
    return enableTLSOnSocket(delegate.createSocket(host, port));


@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException 
    return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort));


private Socket enableTLSOnSocket(Socket socket) 
    if (socket != null && (socket instanceof SSLSocket)) 
        ((SSLSocket) socket).setEnabledProtocols(new String[]"TLSv1.2");
    
    return socket;

【讨论】:

以上是关于使用 SPDY 版本 3 在 OKHttpClient 版本 3.2.0 中引发的错误的主要内容,如果未能解决你的问题,请参考以下文章

如果服务器实现 spdy/3 而浏览器只支持 spdy/2 会发生啥?

如何在 Heroku 上使用 Rails 3.2.2 实现 SPDY?

Jetty:可以使用 SPDY/3.1 吗?

Nginx 1.10 上的 SPDY 代替 HTTP2

Jetty 9.0 SPDY 嵌入式配置

SPDY 与 HAProxy 和 Nginx