mirror of
https://github.com/square/okhttp.git
synced 2026-01-12 10:23:16 +03:00
Add ALPN support; Configure maven to use ALPN on OpenJDK 8.
This commit is contained in:
@@ -8,3 +8,4 @@ before_install:
|
||||
|
||||
jdk:
|
||||
- oraclejdk7
|
||||
- oraclejdk8
|
||||
|
||||
13
README.md
13
README.md
@@ -34,8 +34,8 @@ Testing
|
||||
|
||||
### On the Desktop
|
||||
|
||||
Run OkHttp tests on the desktop with Maven. Running SPDY tests on the desktop uses
|
||||
[Jetty-NPN][3] which requires OpenJDK 7.
|
||||
Run OkHttp tests on the desktop with Maven. Running HTTP/2 and SPDY tests on the desktop uses
|
||||
[Jetty-NPN][3] when running OpenJDK 7 or [Jetty-ALPN][4] when OpenJDK 8.
|
||||
|
||||
```
|
||||
mvn clean test
|
||||
@@ -46,7 +46,7 @@ mvn clean test
|
||||
OkHttp's test suite creates an in-process HTTPS server. Prior to Android 2.3, SSL server sockets
|
||||
were broken, and so HTTPS tests will time out when run on such devices.
|
||||
|
||||
Test on a USB-attached Android using [Vogar][4]. Unfortunately `dx` requires that you build with
|
||||
Test on a USB-attached Android using [Vogar][5]. Unfortunately `dx` requires that you build with
|
||||
Java 6, otherwise the test class will be silently omitted from the `.dex` file.
|
||||
|
||||
```
|
||||
@@ -69,7 +69,7 @@ MockWebServer coupling with OkHttp is essential for proper testing of SPDY and H
|
||||
|
||||
### Download
|
||||
|
||||
Download [the latest JAR][5] or grab via Maven:
|
||||
Download [the latest JAR][6] or grab via Maven:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
@@ -102,5 +102,6 @@ License
|
||||
[1]: http://square.github.io/okhttp
|
||||
[2]: http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.squareup.okhttp&a=okhttp&v=LATEST
|
||||
[3]: https://github.com/jetty-project/jetty-npn
|
||||
[4]: https://code.google.com/p/vogar/
|
||||
[5]: http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.squareup.okhttp&a=mockwebserver&v=LATEST
|
||||
[4]: https://github.com/jetty-project/jetty-alpn
|
||||
[5]: https://code.google.com/p/vogar/
|
||||
[6]: http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.squareup.okhttp&a=mockwebserver&v=LATEST
|
||||
|
||||
@@ -38,11 +38,6 @@
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.npn</groupId>
|
||||
<artifactId>npn-boot</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
@@ -71,28 +66,70 @@
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<executable>java</executable>
|
||||
<arguments>
|
||||
<argument>-Xms512m</argument>
|
||||
<argument>-Xmx512m</argument>
|
||||
<commandlineArgs>-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar</commandlineArgs>
|
||||
<argument>-classpath</argument>
|
||||
<classpath/>
|
||||
<argument>com.squareup.okhttp.benchmarks.Benchmark</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<executable>java</executable>
|
||||
<arguments>
|
||||
<argument>-Xms512m</argument>
|
||||
<argument>-Xmx512m</argument>
|
||||
<commandlineArgs>-Xbootclasspath/p:${bootclasspath}</commandlineArgs>
|
||||
<argument>-classpath</argument>
|
||||
<classpath/>
|
||||
<argument>com.squareup.okhttp.benchmarks.Benchmark</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>npn-when-jdk7</id>
|
||||
<activation>
|
||||
<jdk>1.7</jdk>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.npn</groupId>
|
||||
<artifactId>npn-boot</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>alpn-when-jdk8</id>
|
||||
<activation>
|
||||
<jdk>1.8</jdk>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.alpn</groupId>
|
||||
<artifactId>alpn-boot</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<!-- Fails on caliper's ASM on OpenJDK 8. -->
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.npn</groupId>
|
||||
<artifactId>npn-boot</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
||||
@@ -110,7 +110,7 @@ public final class MockWebServer {
|
||||
|
||||
private int port = -1;
|
||||
private boolean protocolNegotiationEnabled = true;
|
||||
private List<Protocol> npnProtocols
|
||||
private List<Protocol> protocols
|
||||
= Util.immutableList(Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
|
||||
|
||||
public int getPort() {
|
||||
@@ -209,7 +209,7 @@ public final class MockWebServer {
|
||||
if (protocols.contains(null)) {
|
||||
throw new IllegalArgumentException("protocols must not contain null");
|
||||
}
|
||||
this.npnProtocols = protocols;
|
||||
this.protocols = protocols;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,7 +351,7 @@ public final class MockWebServer {
|
||||
openClientSockets.put(socket, true);
|
||||
|
||||
if (protocolNegotiationEnabled) {
|
||||
Platform.get().setProtocols(sslSocket, npnProtocols);
|
||||
Platform.get().setProtocols(sslSocket, protocols);
|
||||
}
|
||||
|
||||
sslSocket.startHandshake();
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.npn</groupId>
|
||||
<artifactId>npn-boot</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
|
||||
@@ -92,12 +92,12 @@ public final class CallTest {
|
||||
}
|
||||
|
||||
@Test public void get_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
get();
|
||||
}
|
||||
|
||||
@Test public void get_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
get();
|
||||
}
|
||||
|
||||
@@ -123,12 +123,12 @@ public final class CallTest {
|
||||
}
|
||||
|
||||
@Test public void head_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
head();
|
||||
}
|
||||
|
||||
@Test public void head_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
head();
|
||||
}
|
||||
|
||||
@@ -153,12 +153,12 @@ public final class CallTest {
|
||||
}
|
||||
|
||||
@Test public void post_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
post();
|
||||
}
|
||||
|
||||
@Test public void post_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
post();
|
||||
}
|
||||
|
||||
@@ -183,12 +183,12 @@ public final class CallTest {
|
||||
}
|
||||
|
||||
@Test public void postZeroLength_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
postZeroLength();
|
||||
}
|
||||
|
||||
@Test public void postZerolength_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
postZeroLength();
|
||||
}
|
||||
|
||||
@@ -213,12 +213,12 @@ public final class CallTest {
|
||||
}
|
||||
|
||||
@Test public void delete_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
delete();
|
||||
}
|
||||
|
||||
@Test public void delete_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
delete();
|
||||
}
|
||||
|
||||
@@ -243,12 +243,12 @@ public final class CallTest {
|
||||
}
|
||||
|
||||
@Test public void put_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
put();
|
||||
}
|
||||
|
||||
@Test public void put_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
put();
|
||||
}
|
||||
|
||||
@@ -273,12 +273,12 @@ public final class CallTest {
|
||||
}
|
||||
|
||||
@Test public void patch_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
patch();
|
||||
}
|
||||
|
||||
@Test public void patch_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
patch();
|
||||
}
|
||||
|
||||
@@ -914,12 +914,12 @@ public final class CallTest {
|
||||
}
|
||||
|
||||
@Test public void canceledBeforeIOSignalsOnFailure_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
canceledBeforeIOSignalsOnFailure();
|
||||
}
|
||||
|
||||
@Test public void canceledBeforeIOSignalsOnFailure_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
canceledBeforeIOSignalsOnFailure();
|
||||
}
|
||||
|
||||
@@ -940,12 +940,12 @@ public final class CallTest {
|
||||
}
|
||||
|
||||
@Test public void canceledBeforeResponseReadSignalsOnFailure_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
canceledBeforeResponseReadSignalsOnFailure();
|
||||
}
|
||||
|
||||
@Test public void canceledBeforeResponseReadSignalsOnFailure_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
canceledBeforeResponseReadSignalsOnFailure();
|
||||
}
|
||||
|
||||
@@ -989,13 +989,13 @@ public final class CallTest {
|
||||
|
||||
@Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_HTTP_2()
|
||||
throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce();
|
||||
}
|
||||
|
||||
@Test public void canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce_SPDY_3()
|
||||
throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
canceledAfterResponseIsDeliveredBreaksStreamButSignalsOnce();
|
||||
}
|
||||
|
||||
@@ -1006,9 +1006,9 @@ public final class CallTest {
|
||||
|
||||
/**
|
||||
* Tests that use this will fail unless boot classpath is set. Ex. {@code
|
||||
* -Xbootclasspath/p:/tmp/npn-boot-1.1.7.v20140316.jar}
|
||||
* -Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317}
|
||||
*/
|
||||
private void enableNpn(Protocol protocol) {
|
||||
private void enableProtocol(Protocol protocol) {
|
||||
client.setSslSocketFactory(sslContext.getSocketFactory());
|
||||
client.setHostnameVerifier(new RecordingHostnameVerifier());
|
||||
client.setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1));
|
||||
|
||||
@@ -647,10 +647,10 @@ public final class URLConnectionTest {
|
||||
assertContent("def", client.open(server.getUrl("/")));
|
||||
|
||||
RecordedRequest request1 = server.takeRequest();
|
||||
assertEquals("TLSv1", request1.getSslProtocol()); // OkHttp's current best TLS version.
|
||||
assertTrue(request1.getSslProtocol().startsWith("TLSv1")); // v1.2 on OpenJDK 8.
|
||||
|
||||
RecordedRequest request2 = server.takeRequest();
|
||||
assertEquals("TLSv1", request2.getSslProtocol()); // OkHttp's current best TLS version.
|
||||
assertTrue(request2.getSslProtocol().startsWith("TLSv1")); // v1.2 on OpenJDK 8.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2745,19 +2745,19 @@ public final class URLConnectionTest {
|
||||
assertTrue(call, call.contains("challenges=[Bearer realm=\"oauthed\"]"));
|
||||
}
|
||||
|
||||
@Test public void npnSetsProtocolHeader_SPDY_3() throws Exception {
|
||||
npnSetsProtocolHeader(Protocol.SPDY_3);
|
||||
@Test public void setsNegotiatedProtocolHeader_SPDY_3() throws Exception {
|
||||
setsNegotiatedProtocolHeader(Protocol.SPDY_3);
|
||||
}
|
||||
|
||||
@Test public void npnSetsProtocolHeader_HTTP_2() throws Exception {
|
||||
npnSetsProtocolHeader(Protocol.HTTP_2);
|
||||
@Test public void setsNegotiatedProtocolHeader_HTTP_2() throws Exception {
|
||||
setsNegotiatedProtocolHeader(Protocol.HTTP_2);
|
||||
}
|
||||
|
||||
private void npnSetsProtocolHeader(Protocol protocol) throws IOException {
|
||||
enableNpn(protocol);
|
||||
private void setsNegotiatedProtocolHeader(Protocol protocol) throws IOException {
|
||||
enableProtocol(protocol);
|
||||
server.enqueue(new MockResponse().setBody("A"));
|
||||
server.play();
|
||||
client.setProtocols(Arrays.asList(Protocol.HTTP_1_1, protocol));
|
||||
client.setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1));
|
||||
connection = client.open(server.getUrl("/"));
|
||||
List<String> protocolValues = connection.getHeaderFields().get(SELECTED_PROTOCOL);
|
||||
assertEquals(Arrays.asList(protocol.toString()), protocolValues);
|
||||
@@ -2786,12 +2786,12 @@ public final class URLConnectionTest {
|
||||
}
|
||||
|
||||
@Test public void zeroLengthPost_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
zeroLengthPost();
|
||||
}
|
||||
|
||||
@Test public void zeroLengthPost_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
zeroLengthPost();
|
||||
}
|
||||
|
||||
@@ -2801,12 +2801,12 @@ public final class URLConnectionTest {
|
||||
}
|
||||
|
||||
@Test public void zeroLengthPut_SPDY_3() throws Exception {
|
||||
enableNpn(Protocol.SPDY_3);
|
||||
enableProtocol(Protocol.SPDY_3);
|
||||
zeroLengthPut();
|
||||
}
|
||||
|
||||
@Test public void zeroLengthPut_HTTP_2() throws Exception {
|
||||
enableNpn(Protocol.HTTP_2);
|
||||
enableProtocol(Protocol.HTTP_2);
|
||||
zeroLengthPut();
|
||||
}
|
||||
|
||||
@@ -3113,9 +3113,9 @@ public final class URLConnectionTest {
|
||||
|
||||
/**
|
||||
* Tests that use this will fail unless boot classpath is set. Ex. {@code
|
||||
* -Xbootclasspath/p:/tmp/npn-boot-1.1.7.v20140316.jar}
|
||||
* -Xbootclasspath/p:/tmp/alpn-boot-8.0.0.v20140317}
|
||||
*/
|
||||
private void enableNpn(Protocol protocol) {
|
||||
private void enableProtocol(Protocol protocol) {
|
||||
client.setSslSocketFactory(sslContext.getSocketFactory());
|
||||
client.setHostnameVerifier(new RecordingHostnameVerifier());
|
||||
client.setProtocols(Arrays.asList(protocol, Protocol.HTTP_1_1));
|
||||
|
||||
@@ -51,8 +51,8 @@ import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
|
||||
* <ul>
|
||||
* <li>Server Name Indication (SNI) enables one IP address to negotiate secure
|
||||
* connections for multiple domain names.
|
||||
* <li>Next Protocol Negotiation (NPN) enables the HTTPS port (443) to be used
|
||||
* for both HTTP and SPDY protocols.
|
||||
* <li>Application Layer Protocol Negotiation (ALPN) enables the HTTPS port
|
||||
* (443) to be used for different HTTP and SPDY protocols.
|
||||
* </ul>
|
||||
* Unfortunately, older HTTPS servers refuse to connect when such options are
|
||||
* presented. Rather than avoiding these options entirely, this class allows a
|
||||
|
||||
@@ -321,19 +321,17 @@ public final class OkHttpClient implements URLStreamHandlerFactory, Cloneable {
|
||||
* <ul>
|
||||
* <li><a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">http/1.1</a>
|
||||
* <li><a href="http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3-1">spdy/3.1</a>
|
||||
* <li><a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-09">HTTP-draft-09/2.0</a>
|
||||
* <li><a href="http://tools.ietf.org/html/draft-ietf-httpbis-http2-10">h2-10</a>
|
||||
* </ul>
|
||||
*
|
||||
* <p><strong>This is an evolving set.</strong> Future releases may drop
|
||||
* support for transitional protocols (like spdy/3.1), in favor of their
|
||||
* successors (spdy/4 or hTTP/2). The http/1.1 transport will never be
|
||||
* dropped.
|
||||
* support for transitional protocols (like h2-10), in favor of their
|
||||
* successors (h2). The http/1.1 transport will never be dropped.
|
||||
*
|
||||
* <p>If multiple protocols are specified, <a
|
||||
* href="https://technotes.googlecode.com/git/nextprotoneg.html">NPN</a> will
|
||||
* be used to negotiate a transport. Future releases may use another mechanism
|
||||
* (such as <a href="http://tools.ietf.org/html/draft-friedl-tls-applayerprotoneg-02">ALPN</a>)
|
||||
* to negotiate a transport.
|
||||
* href="https://technotes.googlecode.com/git/nextprotoneg.html">NPN</a> or
|
||||
* <a href="http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg">ALPN</a>
|
||||
* will be used to negotiate a transport.
|
||||
*
|
||||
* @param protocols the protocols to use, in order of preference. The list
|
||||
* must contain {@link Protocol#HTTP_1_1}. It must not contain null.
|
||||
|
||||
@@ -20,7 +20,8 @@ import java.io.IOException;
|
||||
/**
|
||||
* Protocols that OkHttp implements for <a
|
||||
* href="http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04">NPN</a> and
|
||||
* <a href="http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg">ALPN</a>.
|
||||
* <a href="http://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg">ALPN</a>
|
||||
* selection.
|
||||
*
|
||||
* <h3>Protocol vs Scheme</h3>
|
||||
* Despite its name, {@link java.net.URL#getProtocol()} returns the
|
||||
|
||||
@@ -45,12 +45,13 @@ import okio.Buffer;
|
||||
*
|
||||
* <p>NPN (Next Protocol Negotiation) was developed for SPDY. It is widely
|
||||
* available and we support it on both Android (4.1+) and OpenJDK 7 (via the
|
||||
* Jetty NPN-boot library). NPN is not yet available on Java 8.
|
||||
* Jetty Alpn-boot library). NPN is not yet available on OpenJDK 8.
|
||||
*
|
||||
* <p>ALPN (Application Layer Protocol Negotiation) is the successor to NPN. It
|
||||
* has some technical advantages over NPN. ALPN first arrived in Android 4.4,
|
||||
* but that release suffers a <a href="http://goo.gl/y5izPP">concurrency bug</a>
|
||||
* so we don't use it. ALPN will be supported in the future.
|
||||
* so we don't use it. ALPN is supported on OpenJDK 7 and 8 (via the Jetty
|
||||
* ALPN-boot library).
|
||||
*
|
||||
* <p>On platforms that support both extensions, OkHttp will use both,
|
||||
* preferring ALPN's result. Future versions of OkHttp will drop support for
|
||||
@@ -148,21 +149,24 @@ public class Platform {
|
||||
// This isn't Android 2.3 or better.
|
||||
}
|
||||
|
||||
// Attempt to find the Jetty's NPN extension for OpenJDK.
|
||||
try {
|
||||
String npnClassName = "org.eclipse.jetty.npn.NextProtoNego";
|
||||
Class<?> nextProtoNegoClass = Class.forName(npnClassName);
|
||||
Class<?> providerClass = Class.forName(npnClassName + "$Provider");
|
||||
Class<?> clientProviderClass = Class.forName(npnClassName + "$ClientProvider");
|
||||
Class<?> serverProviderClass = Class.forName(npnClassName + "$ServerProvider");
|
||||
Method putMethod = nextProtoNegoClass.getMethod("put", SSLSocket.class, providerClass);
|
||||
Method getMethod = nextProtoNegoClass.getMethod("get", SSLSocket.class);
|
||||
return new JdkWithJettyNpnPlatform(
|
||||
try { // to find the Jetty's ALPN or NPN extension for OpenJDK.
|
||||
String negoClassName = "org.eclipse.jetty.alpn.ALPN";
|
||||
Class<?> negoClass;
|
||||
try {
|
||||
negoClass = Class.forName(negoClassName);
|
||||
} catch (ClassNotFoundException ignored) { // ALPN isn't on the classpath.
|
||||
negoClassName = "org.eclipse.jetty.npn.NextProtoNego";
|
||||
negoClass = Class.forName(negoClassName);
|
||||
}
|
||||
Class<?> providerClass = Class.forName(negoClassName + "$Provider");
|
||||
Class<?> clientProviderClass = Class.forName(negoClassName + "$ClientProvider");
|
||||
Class<?> serverProviderClass = Class.forName(negoClassName + "$ServerProvider");
|
||||
Method putMethod = negoClass.getMethod("put", SSLSocket.class, providerClass);
|
||||
Method getMethod = negoClass.getMethod("get", SSLSocket.class);
|
||||
return new JdkWithJettyBootPlatform(
|
||||
putMethod, getMethod, clientProviderClass, serverProviderClass);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
// NPN isn't on the classpath.
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
// The NPN version isn't what we expect.
|
||||
} catch (ClassNotFoundException ignored) { // NPN isn't on the classpath.
|
||||
} catch (NoSuchMethodException ignored) { // The ALPN or NPN version isn't what we expect.
|
||||
}
|
||||
|
||||
return new Platform();
|
||||
@@ -247,15 +251,18 @@ public class Platform {
|
||||
}
|
||||
}
|
||||
|
||||
/** OpenJDK 7 plus {@code org.mortbay.jetty.npn/npn-boot} on the boot class path. */
|
||||
private static class JdkWithJettyNpnPlatform extends Platform {
|
||||
/**
|
||||
* OpenJDK 7+ with {@code org.mortbay.jetty.npn/npn-boot} or
|
||||
* {@code org.mortbay.jetty.alpn/alpn-boot} in the boot class path.
|
||||
*/
|
||||
private static class JdkWithJettyBootPlatform extends Platform {
|
||||
private final Method getMethod;
|
||||
private final Method putMethod;
|
||||
private final Class<?> clientProviderClass;
|
||||
private final Class<?> serverProviderClass;
|
||||
|
||||
public JdkWithJettyNpnPlatform(Method putMethod, Method getMethod, Class<?> clientProviderClass,
|
||||
Class<?> serverProviderClass) {
|
||||
public JdkWithJettyBootPlatform(Method putMethod, Method getMethod,
|
||||
Class<?> clientProviderClass, Class<?> serverProviderClass) {
|
||||
this.putMethod = putMethod;
|
||||
this.getMethod = getMethod;
|
||||
this.clientProviderClass = clientProviderClass;
|
||||
@@ -267,11 +274,11 @@ public class Platform {
|
||||
List<String> names = new ArrayList<String>(protocols.size());
|
||||
for (int i = 0, size = protocols.size(); i < size; i++) {
|
||||
Protocol protocol = protocols.get(i);
|
||||
if (protocol == Protocol.HTTP_1_0) continue; // No HTTP/1.0 for NPN.
|
||||
if (protocol == Protocol.HTTP_1_0) continue; // No HTTP/1.0 for NPN or ALPN.
|
||||
names.add(protocol.toString());
|
||||
}
|
||||
Object provider = Proxy.newProxyInstance(Platform.class.getClassLoader(),
|
||||
new Class[] { clientProviderClass, serverProviderClass }, new JettyNpnProvider(names));
|
||||
new Class[] { clientProviderClass, serverProviderClass }, new JettyNegoProvider(names));
|
||||
putMethod.invoke(null, socket, provider);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new AssertionError(e);
|
||||
@@ -282,12 +289,12 @@ public class Platform {
|
||||
|
||||
@Override public String getSelectedProtocol(SSLSocket socket) {
|
||||
try {
|
||||
JettyNpnProvider provider =
|
||||
(JettyNpnProvider) Proxy.getInvocationHandler(getMethod.invoke(null, socket));
|
||||
JettyNegoProvider provider =
|
||||
(JettyNegoProvider) Proxy.getInvocationHandler(getMethod.invoke(null, socket));
|
||||
if (!provider.unsupported && provider.selected == null) {
|
||||
Logger logger = Logger.getLogger("com.squareup.okhttp.OkHttpClient");
|
||||
logger.log(Level.INFO,
|
||||
"NPN callback dropped so SPDY is disabled. Is npn-boot on the boot class path?");
|
||||
logger.log(Level.INFO, "NPN/ALPN callback dropped: SPDY and HTTP/2 are disabled. "
|
||||
+ "Is npn-boot or alpn-boot on the boot class path?");
|
||||
return null;
|
||||
}
|
||||
return provider.unsupported ? null : provider.selected;
|
||||
@@ -300,18 +307,18 @@ public class Platform {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the methods of NextProtoNego's ClientProvider and ServerProvider
|
||||
* Handle the methods of NPN or ALPN's ClientProvider and ServerProvider
|
||||
* without a compile-time dependency on those interfaces.
|
||||
*/
|
||||
private static class JettyNpnProvider implements InvocationHandler {
|
||||
private static class JettyNegoProvider implements InvocationHandler {
|
||||
/** This peer's supported protocols. */
|
||||
private final List<String> protocols;
|
||||
/** Set when remote peer notifies NPN is unsupported. */
|
||||
/** Set when remote peer notifies NPN or ALPN is unsupported. */
|
||||
private boolean unsupported;
|
||||
/** The protocol the client selected. */
|
||||
/** The protocol the client (NPN) or server (ALPN) selected. */
|
||||
private String selected;
|
||||
|
||||
public JettyNpnProvider(List<String> protocols) {
|
||||
public JettyNegoProvider(List<String> protocols) {
|
||||
this.protocols = protocols;
|
||||
}
|
||||
|
||||
@@ -322,27 +329,25 @@ public class Platform {
|
||||
args = Util.EMPTY_STRING_ARRAY;
|
||||
}
|
||||
if (methodName.equals("supports") && boolean.class == returnType) {
|
||||
return true; // Client supports NPN.
|
||||
return true; // NPN or ALPN is supported.
|
||||
} else if (methodName.equals("unsupported") && void.class == returnType) {
|
||||
this.unsupported = true; // Remote peer doesn't support NPN.
|
||||
this.unsupported = true; // Peer doesn't support NPN or ALPN.
|
||||
return null;
|
||||
} else if (methodName.equals("protocols") && args.length == 0) {
|
||||
return protocols; // Server advertises these protocols.
|
||||
} else if (methodName.equals("selectProtocol") // Called when client.
|
||||
&& String.class == returnType
|
||||
&& args.length == 1
|
||||
&& (args[0] == null || args[0] instanceof List)) {
|
||||
List<String> serverProtocols = (List) args[0];
|
||||
// Pick the first protocol the server advertises and client knows.
|
||||
for (int i = 0, size = serverProtocols.size(); i < size; i++) {
|
||||
if (protocols.contains(serverProtocols.get(i))) {
|
||||
return selected = serverProtocols.get(i);
|
||||
return protocols; // Server (NPN) or Client (ALPN) advertises these protocols.
|
||||
} else if ((methodName.equals("selectProtocol") || methodName.equals("select"))
|
||||
&& String.class == returnType && args.length == 1 && args[0] instanceof List) {
|
||||
List<String> peerProtocols = (List) args[0];
|
||||
// Pick the first known protocol the peer advertises.
|
||||
for (int i = 0, size = peerProtocols.size(); i < size; i++) {
|
||||
if (protocols.contains(peerProtocols.get(i))) {
|
||||
return selected = peerProtocols.get(i);
|
||||
}
|
||||
}
|
||||
// On no intersection, try client's first protocol.
|
||||
return selected = protocols.get(0);
|
||||
} else if (methodName.equals("protocolSelected") && args.length == 1) {
|
||||
this.selected = (String) args[0]; // Client selected this protocol.
|
||||
return selected = protocols.get(0); // On no intersection, try peer's first protocol.
|
||||
} else if ((methodName.equals("protocolSelected") || methodName.equals("selected"))
|
||||
&& args.length == 1) {
|
||||
this.selected = (String) args[0]; // Client (NPN) or Server (ALPN) selected this protocol.
|
||||
return null;
|
||||
} else {
|
||||
return method.invoke(this, args);
|
||||
|
||||
83
pom.xml
83
pom.xml
@@ -36,6 +36,8 @@
|
||||
<okio.version>0.8.0</okio.version>
|
||||
<!-- Targetted to jdk7u60-b13; Oracle jdk7u55-b13. -->
|
||||
<npn.version>1.1.7.v20140316</npn.version>
|
||||
<!-- Targetted to OpenJDK 8 b132 -->
|
||||
<alpn.version>8.0.0.v20140317</alpn.version>
|
||||
<bouncycastle.version>1.48</bouncycastle.version>
|
||||
<gson.version>2.2.3</gson.version>
|
||||
<apache.http.version>4.2.2</apache.http.version>
|
||||
@@ -72,15 +74,20 @@
|
||||
<artifactId>okio</artifactId>
|
||||
<version>${okio.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.npn</groupId>
|
||||
<artifactId>npn-boot</artifactId>
|
||||
<version>${npn.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<groupId>org.mortbay.jetty.alpn</groupId>
|
||||
<artifactId>alpn-boot</artifactId>
|
||||
<version>${alpn.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
@@ -126,15 +133,12 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.16</version>
|
||||
<configuration>
|
||||
<argLine>-Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar</argLine>
|
||||
</configuration>
|
||||
<version>2.17</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.surefire</groupId>
|
||||
<artifactId>surefire-junit47</artifactId>
|
||||
<version>2.16</version>
|
||||
<version>2.17</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
@@ -185,7 +189,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-maven-plugin</artifactId>
|
||||
<version>1.10</version>
|
||||
<version>1.11</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>test</phase>
|
||||
@@ -204,5 +208,66 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>npn-when-jdk7</id>
|
||||
<activation>
|
||||
<jdk>1.7</jdk>
|
||||
</activation>
|
||||
<properties>
|
||||
<bootclasspath>${settings.localRepository}/org/mortbay/jetty/npn/npn-boot/${npn.version}/npn-boot-${npn.version}.jar</bootclasspath>
|
||||
</properties>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-Xbootclasspath/p:${bootclasspath}</argLine>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.npn</groupId>
|
||||
<artifactId>npn-boot</artifactId>
|
||||
<version>${npn.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>alpn-when-jdk8</id>
|
||||
<activation>
|
||||
<jdk>1.8</jdk>
|
||||
</activation>
|
||||
<properties>
|
||||
<bootclasspath>${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn.version}/alpn-boot-${alpn.version}.jar</bootclasspath>
|
||||
</properties>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<argLine>-Xbootclasspath/p:${bootclasspath}</argLine>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mortbay.jetty.alpn</groupId>
|
||||
<artifactId>alpn-boot</artifactId>
|
||||
<version>${alpn.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
common connection problems. If your service has multiple IP addresses OkHttp will
|
||||
attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6
|
||||
and for services hosted in redundant data centers. OkHttp initiates new connections
|
||||
with modern TLS features (SNI, NPN), and falls back to SSLv3 if the handshake
|
||||
with modern TLS features (SNI, ALPN), and falls back to SSLv3 if the handshake
|
||||
fails.</p>
|
||||
|
||||
<p>You can try OkHttp without rewriting your network code. The core module implements
|
||||
|
||||
Reference in New Issue
Block a user