okhttp version dump

This commit is contained in:
seven332 committed 2016-02-08 15:26:29 +08:00
1 parent 2897a2fe12
commit dff16b3834
35 files changed
+613 -107

No files matched your search

-1
View File
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3;
import java.io.Closeable;
@@ -25,6 +25,8 @@ import java.util.Map;
import java.util.Set;
import javax.net.ssl.SSLPeerUnverifiedException;
import okhttp3.internal.Util;
import okhttp3.internal.tls.CertificateChainCleaner;
import okhttp3.internal.tls.TrustRootIndex;
import okio.ByteString;
import static java.util.Collections.unmodifiableSet;
@@ -128,9 +130,11 @@ public final class CertificatePinner {
public static final CertificatePinner DEFAULT = new Builder().build();
private final Map<String, Set<ByteString>> hostnameToPins;
private final TrustRootIndex trustRootIndex;
private CertificatePinner(Builder builder) {
hostnameToPins = Util.immutableMap(builder.hostnameToPins);
this.hostnameToPins = Util.immutableMap(builder.hostnameToPins);
this.trustRootIndex = builder.trustRootIndex;
}
/**
@@ -143,6 +147,9 @@ public final class CertificatePinner {
*/
public void check(String hostname, List<Certificate> peerCertificates)
throws SSLPeerUnverifiedException {
if (trustRootIndex != null) {
peerCertificates = new CertificateChainCleaner(trustRootIndex).clean(peerCertificates);
}
Set<ByteString> pins = findMatchingPins(hostname);
@@ -208,6 +215,10 @@ public final class CertificatePinner {
return wildcardPins;
}
Builder newBuilder() {
return new Builder(this);
}
/**
* Returns the SHA-1 of {@code certificate}'s public key. This uses the mechanism Moxie
* Marlinspike describes in <a href="https://github.com/moxie0/AndroidPinning">Android
@@ -227,6 +238,20 @@ public final class CertificatePinner {
/** Builds a configured certificate pinner. */
public static final class Builder {
private final Map<String, Set<ByteString>> hostnameToPins = new LinkedHashMap<>();
private TrustRootIndex trustRootIndex;
public Builder() {
}
Builder(CertificatePinner certificatePinner) {
this.hostnameToPins.putAll(certificatePinner.hostnameToPins);
this.trustRootIndex = certificatePinner.trustRootIndex;
}
public Builder trustRootIndex(TrustRootIndex trustRootIndex) {
this.trustRootIndex = trustRootIndex;
return this;
}
/**
* Pins certificates for {@code hostname}.
@@ -117,9 +117,7 @@ public final class ConnectionPool {
RealConnection get(Address address, StreamAllocation streamAllocation) {
assert (Thread.holdsLock(this));
for (RealConnection connection : connections) {
// TODO(jwilson): this is awkward. We're already holding a lock on 'this', and
// connection.allocationLimit() may also lock the FramedConnection.
if (connection.allocations.size() < connection.allocationLimit()
if (connection.allocations.size() < connection.allocationLimit
&& address.equals(connection.route().address)
&& !connection.noNewStreams) {
streamAllocation.acquire(connection);
+28
View File
@@ -559,4 +559,32 @@ public final class Cookie {
return result.toString();
}
@Override public boolean equals(Object other) {
if (!(other instanceof Cookie)) return false;
Cookie that = (Cookie) other;
return that.name.equals(name)
&& that.value.equals(value)
&& that.domain.equals(domain)
&& that.path.equals(path)
&& that.expiresAt == expiresAt
&& that.secure == secure
&& that.httpOnly == httpOnly
&& that.persistent == persistent
&& that.hostOnly == hostOnly;
}
@Override public int hashCode() {
int hash = 17;
hash = 31 * hash + name.hashCode();
hash = 31 * hash + value.hashCode();
hash = 31 * hash + domain.hashCode();
hash = 31 * hash + path.hashCode();
hash = 31 * hash + (int) (expiresAt ^ (expiresAt >>> 32));
hash = 31 * hash + (secure ? 0 : 1);
hash = 31 * hash + (httpOnly ? 0 : 1);
hash = 31 * hash + (persistent ? 0 : 1);
hash = 31 * hash + (hostOnly ? 0 : 1);
return hash;
}
}
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3;
import java.security.Principal;
@@ -28,13 +28,16 @@ import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.internal.Internal;
import okhttp3.internal.InternalCache;
import okhttp3.internal.Platform;
import okhttp3.internal.RouteDatabase;
import okhttp3.internal.Util;
import okhttp3.internal.http.StreamAllocation;
import okhttp3.internal.io.RealConnection;
import okhttp3.internal.tls.OkHostnameVerifier;
import okhttp3.internal.tls.TrustRootIndex;
/**
* Factory for {@linkplain Call calls}, which can be used to send HTTP requests and read their
@@ -54,7 +57,7 @@ import okhttp3.internal.tls.OkHostnameVerifier;
* Response response = clientWith30sTimeout.newCall(request).execute();
* }</pre>
*/
public final class OkHttpClient implements Cloneable, Call.Factory {
public class OkHttpClient implements Cloneable, Call.Factory {
private static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1);
@@ -130,6 +133,7 @@ public final class OkHttpClient implements Cloneable, Call.Factory {
final InternalCache internalCache;
final SocketFactory socketFactory;
final SSLSocketFactory sslSocketFactory;
final TrustRootIndex trustRootIndex;
final HostnameVerifier hostnameVerifier;
final CertificatePinner certificatePinner;
final Authenticator proxyAuthenticator;
@@ -159,7 +163,13 @@ public final class OkHttpClient implements Cloneable, Call.Factory {
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
if (builder.sslSocketFactory != null) {
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
} else {
try {
@@ -170,8 +180,21 @@ public final class OkHttpClient implements Cloneable, Call.Factory {
throw new AssertionError(); // The system has no TLS. Just give up.
}
}
if (sslSocketFactory != null && builder.trustRootIndex == null) {
X509TrustManager trustManager = Platform.get().trustManager(sslSocketFactory);
if (trustManager == null) {
throw new IllegalStateException("Unable to extract the trust manager on " + Platform.get()
+ ", sslSocketFactory is " + sslSocketFactory.getClass());
}
this.trustRootIndex = Platform.get().trustRootIndex(trustManager);
this.certificatePinner = builder.certificatePinner.newBuilder()
.trustRootIndex(trustRootIndex)
.build();
} else {
this.trustRootIndex = builder.trustRootIndex;
this.certificatePinner = builder.certificatePinner;
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner;
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
@@ -317,6 +340,7 @@ public final class OkHttpClient implements Cloneable, Call.Factory {
InternalCache internalCache;
SocketFactory socketFactory;
SSLSocketFactory sslSocketFactory;
TrustRootIndex trustRootIndex;
HostnameVerifier hostnameVerifier;
CertificatePinner certificatePinner;
Authenticator proxyAuthenticator;
@@ -364,6 +388,7 @@ public final class OkHttpClient implements Cloneable, Call.Factory {
this.cache = okHttpClient.cache;
this.socketFactory = okHttpClient.socketFactory;
this.sslSocketFactory = okHttpClient.sslSocketFactory;
this.trustRootIndex = okHttpClient.trustRootIndex;
this.hostnameVerifier = okHttpClient.hostnameVerifier;
this.certificatePinner = okHttpClient.certificatePinner;
this.proxyAuthenticator = okHttpClient.proxyAuthenticator;
@@ -501,6 +526,7 @@ public final class OkHttpClient implements Cloneable, Call.Factory {
public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) {
if (sslSocketFactory == null) throw new NullPointerException("sslSocketFactory == null");
this.sslSocketFactory = sslSocketFactory;
this.trustRootIndex = null;
return this;
}
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal;
import java.io.IOException;
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal;
import java.io.Closeable;
@@ -1,3 +1,18 @@
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal;
import java.io.IOException;
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal;
/**
@@ -18,6 +18,7 @@ package okhttp3.internal;
import android.util.Log;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -29,7 +30,12 @@ import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.Protocol;
import okhttp3.internal.tls.AndroidTrustRootIndex;
import okhttp3.internal.tls.RealTrustRootIndex;
import okhttp3.internal.tls.TrustRootIndex;
import okio.Buffer;
import static okhttp3.internal.Internal.logger;
@@ -55,6 +61,11 @@ import static okhttp3.internal.Internal.logger;
* unstable.
*
* Supported on OpenJDK 7 and 8 (via the JettyALPN-boot library).
*
* <h3>Trust Manager Extraction</h3>
*
* <p>Supported on Android 2.3+ and OpenJDK 7+. There are no public APIs to recover the trust
* manager that was used to create an {@link SSLSocketFactory}.
*/
public class Platform {
private static final Platform PLATFORM = findPlatform();
@@ -78,6 +89,14 @@ public class Platform {
public void untagSocket(Socket socket) throws SocketException {
}
public X509TrustManager trustManager(SSLSocketFactory sslSocketFactory) {
return null;
}
public TrustRootIndex trustRootIndex(X509TrustManager trustManager) {
return new RealTrustRootIndex(trustManager.getAcceptedIssuers());
}
/**
* Configure TLS extensions on {@code sslSocket} for {@code route}.
*
@@ -112,11 +131,13 @@ public class Platform {
private static Platform findPlatform() {
// Attempt to find Android 2.3+ APIs.
try {
Class<?> sslParametersClass;
try {
Class.forName("com.android.org.conscrypt.OpenSSLSocketImpl");
sslParametersClass = Class.forName("com.android.org.conscrypt.SSLParametersImpl");
} catch (ClassNotFoundException e) {
// Older platform before being unbundled.
Class.forName("org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl");
sslParametersClass = Class.forName(
"org.apache.harmony.xnet.provider.jsse.SSLParametersImpl");
}
OptionalMethod<Socket> setUseSessionTickets
@@ -144,25 +165,34 @@ public class Platform {
} catch (ClassNotFoundException | NoSuchMethodException ignored) {
}
return new Android(setUseSessionTickets, setHostname, trafficStatsTagSocket,
trafficStatsUntagSocket, getAlpnSelectedProtocol, setAlpnProtocols);
return new Android(sslParametersClass, setUseSessionTickets, setHostname,
trafficStatsTagSocket, trafficStatsUntagSocket, getAlpnSelectedProtocol,
setAlpnProtocols);
} catch (ClassNotFoundException ignored) {
// This isn't an Android runtime.
}
// Find Jetty's ALPN extension for OpenJDK.
// Find an Oracle JDK.
try {
String negoClassName = "org.eclipse.jetty.alpn.ALPN";
Class<?> 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);
Method removeMethod = negoClass.getMethod("remove", SSLSocket.class);
return new JdkWithJettyBootPlatform(
putMethod, getMethod, removeMethod, clientProviderClass, serverProviderClass);
} catch (ClassNotFoundException | NoSuchMethodException ignored) {
Class<?> sslContextClass = Class.forName("sun.security.ssl.SSLContextImpl");
// Find Jetty's ALPN extension for OpenJDK.
try {
String negoClassName = "org.eclipse.jetty.alpn.ALPN";
Class<?> 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);
Method removeMethod = negoClass.getMethod("remove", SSLSocket.class);
return new JdkWithJettyBootPlatform(sslContextClass,
putMethod, getMethod, removeMethod, clientProviderClass, serverProviderClass);
} catch (ClassNotFoundException | NoSuchMethodException ignored) {
}
return new JdkPlatform(sslContextClass);
} catch (ClassNotFoundException ignored) {
}
return new Platform();
@@ -172,6 +202,7 @@ public class Platform {
private static class Android extends Platform {
private static final int MAX_LOG_LENGTH = 4000;
private final Class<?> sslParametersClass;
private final OptionalMethod<Socket> setUseSessionTickets;
private final OptionalMethod<Socket> setHostname;
@@ -183,9 +214,11 @@ public class Platform {
private final OptionalMethod<Socket> getAlpnSelectedProtocol;
private final OptionalMethod<Socket> setAlpnProtocols;
public Android(OptionalMethod<Socket> setUseSessionTickets, OptionalMethod<Socket> setHostname,
Method trafficStatsTagSocket, Method trafficStatsUntagSocket,
OptionalMethod<Socket> getAlpnSelectedProtocol, OptionalMethod<Socket> setAlpnProtocols) {
public Android(Class<?> sslParametersClass, OptionalMethod<Socket> setUseSessionTickets,
OptionalMethod<Socket> setHostname, Method trafficStatsTagSocket,
Method trafficStatsUntagSocket, OptionalMethod<Socket> getAlpnSelectedProtocol,
OptionalMethod<Socket> setAlpnProtocols) {
this.sslParametersClass = sslParametersClass;
this.setUseSessionTickets = setUseSessionTickets;
this.setHostname = setHostname;
this.trafficStatsTagSocket = trafficStatsTagSocket;
@@ -210,6 +243,34 @@ public class Platform {
}
}
@Override public X509TrustManager trustManager(SSLSocketFactory sslSocketFactory) {
Object context = readFieldOrNull(sslSocketFactory, sslParametersClass, "sslParameters");
if (context == null) {
// If that didn't work, try the Google Play Services SSL provider before giving up. This
// must be loaded by the SSLSocketFactory's class loader.
try {
Class<?> gmsSslParametersClass = Class.forName(
"com.google.android.gms.org.conscrypt.SSLParametersImpl", false,
sslSocketFactory.getClass().getClassLoader());
context = readFieldOrNull(sslSocketFactory, gmsSslParametersClass, "sslParameters");
} catch (ClassNotFoundException e) {
return null;
}
}
X509TrustManager x509TrustManager = readFieldOrNull(
context, X509TrustManager.class, "x509TrustManager");
if (x509TrustManager != null) return x509TrustManager;
return readFieldOrNull(context, X509TrustManager.class, "trustManager");
}
@Override public TrustRootIndex trustRootIndex(X509TrustManager trustManager) {
TrustRootIndex result = AndroidTrustRootIndex.get(trustManager);
if (result != null) return result;
return super.trustRootIndex(trustManager);
}
@Override public void configureTlsExtensions(
SSLSocket sslSocket, String hostname, List<Protocol> protocols) {
// Enable SNI and session tickets.
@@ -271,18 +332,34 @@ public class Platform {
}
}
/** JDK 1.7 or better. */
private static class JdkPlatform extends Platform {
private final Class<?> sslContextClass;
public JdkPlatform(Class<?> sslContextClass) {
this.sslContextClass = sslContextClass;
}
@Override public X509TrustManager trustManager(SSLSocketFactory sslSocketFactory) {
Object context = readFieldOrNull(sslSocketFactory, sslContextClass, "context");
if (context == null) return null;
return readFieldOrNull(context, X509TrustManager.class, "trustManager");
}
}
/**
* OpenJDK 7+ with {@code org.mortbay.jetty.alpn/alpn-boot} in the boot class path.
*/
private static class JdkWithJettyBootPlatform extends Platform {
private static class JdkWithJettyBootPlatform extends JdkPlatform {
private final Method putMethod;
private final Method getMethod;
private final Method removeMethod;
private final Class<?> clientProviderClass;
private final Class<?> serverProviderClass;
public JdkWithJettyBootPlatform(Method putMethod, Method getMethod, Method removeMethod,
Class<?> clientProviderClass, Class<?> serverProviderClass) {
public JdkWithJettyBootPlatform(Class<?> sslContextClass, Method putMethod, Method getMethod,
Method removeMethod, Class<?> clientProviderClass, Class<?> serverProviderClass) {
super(sslContextClass);
this.putMethod = putMethod;
this.getMethod = getMethod;
this.removeMethod = removeMethod;
@@ -394,4 +471,27 @@ public class Platform {
}
return result.readByteArray();
}
static <T> T readFieldOrNull(Object instance, Class<T> fieldType, String fieldName) {
for (Class<?> c = instance.getClass(); c != Object.class; c = c.getSuperclass()) {
try {
Field field = c.getDeclaredField(fieldName);
field.setAccessible(true);
Object value = field.get(instance);
if (value == null || !fieldType.isInstance(value)) return null;
return fieldType.cast(value);
} catch (NoSuchFieldException ignored) {
} catch (IllegalAccessException e) {
throw new AssertionError();
}
}
// Didn't find the field we wanted. As a last gasp attempt, try to find the value on a delegate.
if (!fieldName.equals("delegate")) {
Object delegate = readFieldOrNull(instance, Object.class, "delegate");
if (delegate != null) return readFieldOrNull(delegate, fieldType, fieldName);
}
return null;
}
}
@@ -17,7 +17,7 @@ package okhttp3.internal;
public final class Version {
public static String userAgent() {
return "okhttp/3.0.1";
return "okhttp/3.1.1";
}
private Version() {
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.framed;
import java.io.Closeable;
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.framed;
import java.io.Closeable;
@@ -82,7 +82,7 @@ public final class FramedConnection implements Closeable {
*/
private final Listener listener;
private final Map<Integer, FramedStream> streams = new HashMap<>();
private final String hostName;
private final String hostname;
private int lastGoodStreamId;
private int nextStreamId;
private boolean shutdown;
@@ -148,14 +148,14 @@ public final class FramedConnection implements Closeable {
okHttpSettings.set(Settings.INITIAL_WINDOW_SIZE, 0, OKHTTP_CLIENT_WINDOW_SIZE);
}
hostName = builder.hostName;
hostname = builder.hostname;
if (protocol == Protocol.HTTP_2) {
variant = new Http2();
// Like newSingleThreadExecutor, except lazy creates the thread.
pushExecutor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
Util.threadFactory(String.format("OkHttp %s Push Observer", hostName), true));
Util.threadFactory(String.format("OkHttp %s Push Observer", hostname), true));
// 1 less than SPDY http://tools.ietf.org/html/draft-ietf-httpbis-http2-17#section-6.9.2
peerSettings.set(Settings.INITIAL_WINDOW_SIZE, 0, 65535);
peerSettings.set(Settings.MAX_FRAME_SIZE, 0, Http2.INITIAL_MAX_FRAME_SIZE);
@@ -342,7 +342,7 @@ public final class FramedConnection implements Closeable {
}
void writeSynResetLater(final int streamId, final ErrorCode errorCode) {
executor.submit(new NamedRunnable("OkHttp %s stream %d", hostName, streamId) {
executor.submit(new NamedRunnable("OkHttp %s stream %d", hostname, streamId) {
@Override public void execute() {
try {
writeSynReset(streamId, errorCode);
@@ -357,7 +357,7 @@ public final class FramedConnection implements Closeable {
}
void writeWindowUpdateLater(final int streamId, final long unacknowledgedBytesRead) {
executor.execute(new NamedRunnable("OkHttp Window Update %s stream %d", hostName, streamId) {
executor.execute(new NamedRunnable("OkHttp Window Update %s stream %d", hostname, streamId) {
@Override public void execute() {
try {
frameWriter.windowUpdate(streamId, unacknowledgedBytesRead);
@@ -390,7 +390,7 @@ public final class FramedConnection implements Closeable {
private void writePingLater(
final boolean reply, final int payload1, final int payload2, final Ping ping) {
executor.execute(new NamedRunnable("OkHttp %s ping %08x%08x",
hostName, payload1, payload2) {
hostname, payload1, payload2) {
@Override public void execute() {
try {
writePing(reply, payload1, payload2, ping);
@@ -528,7 +528,7 @@ public final class FramedConnection implements Closeable {
public static class Builder {
private Socket socket;
private String hostName;
private String hostname;
private BufferedSource source;
private BufferedSink sink;
private Listener listener = Listener.REFUSE_INCOMING_STREAMS;
@@ -550,9 +550,9 @@ public final class FramedConnection implements Closeable {
}
public Builder socket(
Socket socket, String hostName, BufferedSource source, BufferedSink sink) {
Socket socket, String hostname, BufferedSource source, BufferedSink sink) {
this.socket = socket;
this.hostName = hostName;
this.hostname = hostname;
this.source = source;
this.sink = sink;
return this;
@@ -586,7 +586,7 @@ public final class FramedConnection implements Closeable {
final FrameReader frameReader;
private Reader(FrameReader frameReader) {
super("OkHttp %s", hostName);
super("OkHttp %s", hostname);
this.frameReader = frameReader;
}
@@ -663,12 +663,12 @@ public final class FramedConnection implements Closeable {
inFinished, headerBlock);
lastGoodStreamId = streamId;
streams.put(streamId, newStream);
executor.execute(new NamedRunnable("OkHttp %s stream %d", hostName, streamId) {
executor.execute(new NamedRunnable("OkHttp %s stream %d", hostname, streamId) {
@Override public void execute() {
try {
listener.onStream(newStream);
} catch (IOException e) {
logger.log(Level.INFO, "FramedConnection.Listener failure for " + hostName, e);
logger.log(Level.INFO, "FramedConnection.Listener failure for " + hostname, e);
try {
newStream.close(ErrorCode.PROTOCOL_ERROR);
} catch (IOException ignored) {
@@ -724,7 +724,7 @@ public final class FramedConnection implements Closeable {
streamsToNotify = streams.values().toArray(new FramedStream[streams.size()]);
}
}
executor.execute(new NamedRunnable("OkHttp %s settings", hostName) {
executor.execute(new NamedRunnable("OkHttp %s settings", hostname) {
@Override public void execute() {
listener.onSettings(FramedConnection.this);
}
@@ -740,7 +740,7 @@ public final class FramedConnection implements Closeable {
}
private void ackSettingsLater(final Settings peerSettings) {
executor.execute(new NamedRunnable("OkHttp %s ACK Settings", hostName) {
executor.execute(new NamedRunnable("OkHttp %s ACK Settings", hostname) {
@Override public void execute() {
try {
frameWriter.ackSettings(peerSettings);
@@ -834,7 +834,7 @@ public final class FramedConnection implements Closeable {
}
currentPushRequests.add(streamId);
}
pushExecutor.execute(new NamedRunnable("OkHttp %s Push Request[%s]", hostName, streamId) {
pushExecutor.execute(new NamedRunnable("OkHttp %s Push Request[%s]", hostname, streamId) {
@Override public void execute() {
boolean cancel = pushObserver.onRequest(streamId, requestHeaders);
try {
@@ -852,7 +852,7 @@ public final class FramedConnection implements Closeable {
private void pushHeadersLater(final int streamId, final List<Header> requestHeaders,
final boolean inFinished) {
pushExecutor.execute(new NamedRunnable("OkHttp %s Push Headers[%s]", hostName, streamId) {
pushExecutor.execute(new NamedRunnable("OkHttp %s Push Headers[%s]", hostname, streamId) {
@Override public void execute() {
boolean cancel = pushObserver.onHeaders(streamId, requestHeaders, inFinished);
try {
@@ -878,7 +878,7 @@ public final class FramedConnection implements Closeable {
source.require(byteCount); // Eagerly read the frame before firing client thread.
source.read(buffer, byteCount);
if (buffer.size() != byteCount) throw new IOException(buffer.size() + " != " + byteCount);
pushExecutor.execute(new NamedRunnable("OkHttp %s Push Data[%s]", hostName, streamId) {
pushExecutor.execute(new NamedRunnable("OkHttp %s Push Data[%s]", hostname, streamId) {
@Override public void execute() {
try {
boolean cancel = pushObserver.onData(streamId, buffer, byteCount, inFinished);
@@ -895,7 +895,7 @@ public final class FramedConnection implements Closeable {
}
private void pushResetLater(final int streamId, final ErrorCode errorCode) {
pushExecutor.execute(new NamedRunnable("OkHttp %s Push Reset[%s]", hostName, streamId) {
pushExecutor.execute(new NamedRunnable("OkHttp %s Push Reset[%s]", hostname, streamId) {
@Override public void execute() {
pushObserver.onReset(streamId, errorCode);
synchronized (FramedConnection.this) {
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.framed;
import java.io.EOFException;
@@ -1,3 +1,18 @@
/*
* Copyright (C) 2014 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.framed;
import okio.ByteString;
@@ -294,7 +294,7 @@ public final class Http2 implements Variant {
case 6: // SETTINGS_MAX_HEADER_LIST_SIZE
break; // Advisory only, so ignored.
default:
throw ioException("PROTOCOL_ERROR invalid settings id: %s", id);
break; // Must ignore setting with unknown id.
}
settings.set(id, 0, value);
}
@@ -1,3 +1,18 @@
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
import java.util.Date;
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
public final class HeaderParser {
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
import java.io.EOFException;
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
import java.io.IOException;
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
import java.text.DateFormat;
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
import java.io.IOException;
@@ -51,6 +50,7 @@ import okio.Sink;
import okio.Source;
import okio.Timeout;
import static java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT;
import static java.net.HttpURLConnection.HTTP_MOVED_PERM;
import static java.net.HttpURLConnection.HTTP_MOVED_TEMP;
import static java.net.HttpURLConnection.HTTP_MULT_CHOICE;
@@ -868,8 +868,8 @@ public final class HttpEngine {
/**
* Figures out the HTTP request to make in response to receiving this engine's response. This will
* either add authentication headers or follow redirects. If a follow-up is either unnecessary or
* not applicable, this returns null.
* either add authentication headers, follow redirects or handle a client request timeout. If a
* follow-up is either unnecessary or not applicable, this returns null.
*/
public Request followUpRequest() throws IOException {
if (userResponse == null) throw new IllegalStateException();
@@ -940,6 +940,17 @@ public final class HttpEngine {
return requestBuilder.url(url).build();
case HTTP_CLIENT_TIMEOUT:
// 408's are rare in practice, but some servers like HAProxy use this response code. The
// spec says that we may repeat the request without modifications. Modern browsers also
// repeat the request (even non-idempotent ones.)
boolean retryableBody = requestBodyOut == null || requestBodyOut instanceof RetryableSink;
if (callerWritesRequestBody && !retryableBody) {
return null;
}
return userRequest;
default:
return null;
}
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
import java.io.IOException;
@@ -1,3 +1,18 @@
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
import java.net.HttpURLConnection;
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
import java.io.IOException;
@@ -1,3 +1,18 @@
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.http;
import java.io.IOException;
@@ -44,7 +44,9 @@ import okhttp3.internal.ConnectionSpecSelector;
import okhttp3.internal.Platform;
import okhttp3.internal.Util;
import okhttp3.internal.Version;
import okhttp3.internal.framed.ErrorCode;
import okhttp3.internal.framed.FramedConnection;
import okhttp3.internal.framed.FramedStream;
import okhttp3.internal.http.Http1xStream;
import okhttp3.internal.http.OkHeaders;
import okhttp3.internal.http.RouteException;
@@ -60,7 +62,7 @@ import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static okhttp3.internal.Util.closeQuietly;
public final class RealConnection implements Connection {
public final class RealConnection extends FramedConnection.Listener implements Connection {
private final Route route;
/** The low-level TCP socket. */
@@ -77,6 +79,7 @@ public final class RealConnection implements Connection {
public int successCount;
public BufferedSource source;
public BufferedSink sink;
public int allocationLimit;
public final List<Reference<StreamAllocation>> allocations = new ArrayList<>();
public boolean noNewStreams;
public long idleAtNanos = Long.MAX_VALUE;
@@ -154,11 +157,15 @@ public final class RealConnection implements Connection {
FramedConnection framedConnection = new FramedConnection.Builder(true)
.socket(socket, route.address().url().host(), source, sink)
.protocol(protocol)
.listener(this)
.build();
framedConnection.sendConnectionPreface();
// Only assign the framed connection once the preface has been sent successfully.
this.allocationLimit = framedConnection.maxConcurrentStreams();
this.framedConnection = framedConnection;
} else {
this.allocationLimit = 1;
}
}
@@ -308,13 +315,6 @@ public final class RealConnection implements Connection {
return socket;
}
public int allocationLimit() {
FramedConnection framedConnection = this.framedConnection;
return framedConnection != null
? framedConnection.maxConcurrentStreams()
: 1;
}
/** Returns true if this connection is ready to host new streams. */
public boolean isHealthy(boolean doExtensiveChecks) {
if (socket.isClosed() || socket.isInputShutdown() || socket.isOutputShutdown()) {
@@ -347,6 +347,16 @@ public final class RealConnection implements Connection {
return true;
}
/** Refuse incoming streams. */
@Override public void onStream(FramedStream stream) throws IOException {
stream.close(ErrorCode.REFUSED_STREAM);
}
/** When settings are received, adjust the allocation limit. */
@Override public void onSettings(FramedConnection connection) {
allocationLimit = connection.maxConcurrentStreams();
}
@Override public Handshake handshake() {
return handshake;
}
@@ -360,7 +370,11 @@ public final class RealConnection implements Connection {
}
@Override public Protocol protocol() {
return protocol != null ? protocol : Protocol.HTTP_1_1;
if (framedConnection == null) {
return protocol != null ? protocol : Protocol.HTTP_1_1;
} else {
return framedConnection.getProtocol();
}
}
@Override public String toString() {
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2016 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.tls;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.cert.TrustAnchor;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
* A index of trusted root certificates that exploits knowledge of Android implementation details.
* This class is potentially much faster to initialize than {@link RealTrustRootIndex} because
* it doesn't need to load and index trusted CA certificates.
*/
public final class AndroidTrustRootIndex implements TrustRootIndex {
private final X509TrustManager trustManager;
private final Method findByIssuerAndSignatureMethod;
public AndroidTrustRootIndex(
X509TrustManager trustManager, Method findByIssuerAndSignatureMethod) {
this.findByIssuerAndSignatureMethod = findByIssuerAndSignatureMethod;
this.trustManager = trustManager;
}
@Override public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
try {
TrustAnchor trustAnchor = (TrustAnchor) findByIssuerAndSignatureMethod.invoke(
trustManager, cert);
return trustAnchor.getTrustedCert();
} catch (IllegalAccessException e) {
throw new AssertionError();
} catch (InvocationTargetException e) {
return null;
}
}
public static TrustRootIndex get(X509TrustManager trustManager) {
// From org.conscrypt.TrustManagerImpl, we want the method with this signature:
// private TrustAnchor findTrustAnchorByIssuerAndSignature(X509Certificate lastCert);
try {
Method method = trustManager.getClass().getDeclaredMethod(
"findTrustAnchorByIssuerAndSignature", X509Certificate.class);
method.setAccessible(true);
return new AndroidTrustRootIndex(trustManager, method);
} catch (NoSuchMethodException e) {
return null;
}
}
}
@@ -0,0 +1,101 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.tls;
import java.security.GeneralSecurityException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import javax.net.ssl.SSLPeerUnverifiedException;
/**
* Computes the effective certificate chain from the raw array returned by Java's built in TLS APIs.
* Cleaning a chain returns a list of certificates where the first element is {@code chain[0]}, each
* certificate is signed by the certificate that follows, and the last certificate is a trusted CA
* certificate.
*
* <p>Use of the chain cleaner is necessary to omit unexpected certificates that aren't relevant to
* the TLS handshake and to extract the trusted CA certificate for the benefit of certificate
* pinning.
*
* <p>This class includes code from <a href="https://conscrypt.org/">Conscrypt's</a> {@code
* TrustManagerImpl} and {@code TrustedCertificateIndex}.
*/
public final class CertificateChainCleaner {
private final TrustRootIndex trustRootIndex;
public CertificateChainCleaner(TrustRootIndex trustRootIndex) {
this.trustRootIndex = trustRootIndex;
}
/**
* Returns a cleaned chain for {@code chain}.
*
* <p>This method throws if the complete chain to a trusted CA certificate cannot be constructed.
* This is unexpected unless the trust root index in this class has a different trust manager than
* what was used to establish {@code chain}.
*/
public List<Certificate> clean(List<Certificate> chain) throws SSLPeerUnverifiedException {
Deque<Certificate> queue = new ArrayDeque<>(chain);
List<Certificate> result = new ArrayList<>();
result.add(queue.removeFirst());
followIssuerChain:
while (true) {
X509Certificate toVerify = (X509Certificate) result.get(result.size() - 1);
// If this cert has been signed by a trusted CA cert, we're done. Add the trusted CA
// certificate to the end of the chain, unless it's already present. (That would happen if the
// first certificate in the chain is itself a self-signed and trusted CA certificate.)
X509Certificate caCert = trustRootIndex.findByIssuerAndSignature(toVerify);
if (caCert != null) {
if (result.size() > 1 || !toVerify.equals(caCert)) {
result.add(caCert);
}
return result;
}
// Search for the certificate in the chain that signed this certificate. This is typically the
// next element in the chain, but it could be any element.
for (Iterator<Certificate> i = queue.iterator(); i.hasNext(); ) {
X509Certificate signingCert = (X509Certificate) i.next();
if (toVerify.getIssuerDN().equals(signingCert.getSubjectDN())
&& verifySignature(toVerify, signingCert)) {
i.remove();
result.add(signingCert);
continue followIssuerChain;
}
}
throw new SSLPeerUnverifiedException("Failed to find a cert that signed " + toVerify);
}
}
/** Returns true if {@code toVerify} was signed by {@code signingCert}'s public key. */
private boolean verifySignature(X509Certificate toVerify, X509Certificate signingCert) {
try {
toVerify.verify(signingCert.getPublicKey());
return true;
} catch (GeneralSecurityException verifyFailed) {
return false;
}
}
}
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.tls;
import javax.security.auth.x500.X500Principal;
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.tls;
import java.security.cert.Certificate;
@@ -57,7 +56,7 @@ public final class OkHostnameVerifier implements HostnameVerifier {
public boolean verify(String host, X509Certificate certificate) {
return verifyAsIpAddress(host)
? verifyIpAddress(host, certificate)
: verifyHostName(host, certificate);
: verifyHostname(host, certificate);
}
/** Returns true if {@code certificate} matches {@code ipAddress}. */
@@ -71,14 +70,14 @@ public final class OkHostnameVerifier implements HostnameVerifier {
return false;
}
/** Returns true if {@code certificate} matches {@code hostName}. */
private boolean verifyHostName(String hostName, X509Certificate certificate) {
hostName = hostName.toLowerCase(Locale.US);
/** Returns true if {@code certificate} matches {@code hostname}. */
private boolean verifyHostname(String hostname, X509Certificate certificate) {
hostname = hostname.toLowerCase(Locale.US);
boolean hasDns = false;
List<String> altNames = getSubjectAltNames(certificate, ALT_DNS_NAME);
for (int i = 0, size = altNames.size(); i < size; i++) {
hasDns = true;
if (verifyHostName(hostName, altNames.get(i))) {
if (verifyHostname(hostname, altNames.get(i))) {
return true;
}
}
@@ -88,7 +87,7 @@ public final class OkHostnameVerifier implements HostnameVerifier {
// RFC 2818 advises using the most specific name for matching.
String cn = new DistinguishedNameParser(principal).findMostSpecific("cn");
if (cn != null) {
return verifyHostName(hostName, cn);
return verifyHostname(hostname, cn);
}
}
@@ -134,17 +133,17 @@ public final class OkHostnameVerifier implements HostnameVerifier {
}
/**
* Returns {@code true} iff {@code hostName} matches the domain name {@code pattern}.
* Returns {@code true} iff {@code hostname} matches the domain name {@code pattern}.
*
* @param hostName lower-case host name.
* @param hostname lower-case host name.
* @param pattern domain name pattern from certificate. May be a wildcard pattern such as {@code
* *.android.com}.
*/
private boolean verifyHostName(String hostName, String pattern) {
private boolean verifyHostname(String hostname, String pattern) {
// Basic sanity checks
// Check length == 0 instead of .isEmpty() to support Java 5.
if ((hostName == null) || (hostName.length() == 0) || (hostName.startsWith("."))
|| (hostName.endsWith(".."))) {
if ((hostname == null) || (hostname.length() == 0) || (hostname.startsWith("."))
|| (hostname.endsWith(".."))) {
// Invalid domain name
return false;
}
@@ -154,29 +153,29 @@ public final class OkHostnameVerifier implements HostnameVerifier {
return false;
}
// Normalize hostName and pattern by turning them into absolute domain names if they are not
// Normalize hostname and pattern by turning them into absolute domain names if they are not
// yet absolute. This is needed because server certificates do not normally contain absolute
// names or patterns, but they should be treated as absolute. At the same time, any hostName
// names or patterns, but they should be treated as absolute. At the same time, any hostname
// presented to this method should also be treated as absolute for the purposes of matching
// to the server certificate.
// www.android.com matches www.android.com
// www.android.com matches www.android.com.
// www.android.com. matches www.android.com.
// www.android.com. matches www.android.com
if (!hostName.endsWith(".")) {
hostName += '.';
if (!hostname.endsWith(".")) {
hostname += '.';
}
if (!pattern.endsWith(".")) {
pattern += '.';
}
// hostName and pattern are now absolute domain names.
// hostname and pattern are now absolute domain names.
pattern = pattern.toLowerCase(Locale.US);
// hostName and pattern are now in lower case -- domain names are case-insensitive.
// hostname and pattern are now in lower case -- domain names are case-insensitive.
if (!pattern.contains("*")) {
// Not a wildcard pattern -- hostName and pattern must match exactly.
return hostName.equals(pattern);
// Not a wildcard pattern -- hostname and pattern must match exactly.
return hostname.equals(pattern);
}
// Wildcard pattern
@@ -196,11 +195,11 @@ public final class OkHostnameVerifier implements HostnameVerifier {
return false;
}
// Optimization: check whether hostName is too short to match the pattern. hostName must be at
// Optimization: check whether hostname is too short to match the pattern. hostName must be at
// least as long as the pattern because asterisk must match the whole left-most label and
// hostName starts with a non-empty label. Thus, asterisk has to match one or more characters.
if (hostName.length() < pattern.length()) {
// hostName too short to match the pattern.
// hostname starts with a non-empty label. Thus, asterisk has to match one or more characters.
if (hostname.length() < pattern.length()) {
// hostname too short to match the pattern.
return false;
}
@@ -209,22 +208,22 @@ public final class OkHostnameVerifier implements HostnameVerifier {
return false;
}
// hostName must end with the region of pattern following the asterisk.
// hostname must end with the region of pattern following the asterisk.
String suffix = pattern.substring(1);
if (!hostName.endsWith(suffix)) {
// hostName does not end with the suffix
if (!hostname.endsWith(suffix)) {
// hostname does not end with the suffix
return false;
}
// Check that asterisk did not match across domain name labels.
int suffixStartIndexInHostName = hostName.length() - suffix.length();
if ((suffixStartIndexInHostName > 0)
&& (hostName.lastIndexOf('.', suffixStartIndexInHostName - 1) != -1)) {
int suffixStartIndexInHostname = hostname.length() - suffix.length();
if ((suffixStartIndexInHostname > 0)
&& (hostname.lastIndexOf('.', suffixStartIndexInHostname - 1) != -1)) {
// Asterisk is matching across domain name labels -- not permitted.
return false;
}
// hostName matches pattern
// hostname matches pattern
return true;
}
}
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2016 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.tls;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.security.auth.x500.X500Principal;
public final class RealTrustRootIndex implements TrustRootIndex {
private final Map<X500Principal, List<X509Certificate>> subjectToCaCerts;
public RealTrustRootIndex(X509Certificate... caCerts) {
subjectToCaCerts = new LinkedHashMap<>();
for (X509Certificate caCert : caCerts) {
X500Principal subject = caCert.getSubjectX500Principal();
List<X509Certificate> subjectCaCerts = subjectToCaCerts.get(subject);
if (subjectCaCerts == null) {
subjectCaCerts = new ArrayList<>(1);
subjectToCaCerts.put(subject, subjectCaCerts);
}
subjectCaCerts.add(caCert);
}
}
@Override public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
X500Principal issuer = cert.getIssuerX500Principal();
List<X509Certificate> subjectCaCerts = subjectToCaCerts.get(issuer);
if (subjectCaCerts == null) return null;
for (X509Certificate caCert : subjectCaCerts) {
PublicKey publicKey = caCert.getPublicKey();
try {
cert.verify(publicKey);
return caCert;
} catch (Exception ignored) {
}
}
return null;
}
}
@@ -0,0 +1,23 @@
/*
* Copyright (C) 2016 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package okhttp3.internal.tls;
import java.security.cert.X509Certificate;
public interface TrustRootIndex {
/** Returns the trusted CA certificate that signed {@code cert}. */
X509Certificate findByIssuerAndSignature(X509Certificate cert);
}