mirror of
https://github.com/square/okhttp.git
synced 2026-01-27 04:22:07 +03:00
Non-static authenticators.
This avoids the problems of HttpURLConnection's authenticator where multiple concurrent uses of HTTP require a single shared authenticator.
This commit is contained in:
@@ -38,16 +38,20 @@ public final class Address {
|
||||
final int uriPort;
|
||||
final SSLSocketFactory sslSocketFactory;
|
||||
final HostnameVerifier hostnameVerifier;
|
||||
final OkAuthenticator authenticator;
|
||||
|
||||
public Address(String uriHost, int uriPort, SSLSocketFactory sslSocketFactory,
|
||||
HostnameVerifier hostnameVerifier, Proxy proxy) throws UnknownHostException {
|
||||
HostnameVerifier hostnameVerifier, OkAuthenticator authenticator, Proxy proxy)
|
||||
throws UnknownHostException {
|
||||
if (uriHost == null) throw new NullPointerException("uriHost == null");
|
||||
if (uriPort <= 0) throw new IllegalArgumentException("uriPort <= 0: " + uriPort);
|
||||
if (authenticator == null) throw new IllegalArgumentException("authenticator == null");
|
||||
this.proxy = proxy;
|
||||
this.uriHost = uriHost;
|
||||
this.uriPort = uriPort;
|
||||
this.sslSocketFactory = sslSocketFactory;
|
||||
this.hostnameVerifier = hostnameVerifier;
|
||||
this.authenticator = authenticator;
|
||||
}
|
||||
|
||||
/** Returns the hostname of the origin server. */
|
||||
@@ -79,6 +83,14 @@ public final class Address {
|
||||
return hostnameVerifier;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the client's authenticator. This method never returns null.
|
||||
*/
|
||||
public OkAuthenticator getAuthenticator() {
|
||||
return authenticator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this address's explicitly-specified HTTP proxy, or null to
|
||||
* delegate to the HTTP client's proxy selector.
|
||||
@@ -94,7 +106,8 @@ public final class Address {
|
||||
&& this.uriHost.equals(that.uriHost)
|
||||
&& this.uriPort == that.uriPort
|
||||
&& equal(this.sslSocketFactory, that.sslSocketFactory)
|
||||
&& equal(this.hostnameVerifier, that.hostnameVerifier);
|
||||
&& equal(this.hostnameVerifier, that.hostnameVerifier)
|
||||
&& equal(this.authenticator, that.authenticator);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -105,6 +118,7 @@ public final class Address {
|
||||
result = 31 * result + uriPort;
|
||||
result = 31 * result + (sslSocketFactory != null ? sslSocketFactory.hashCode() : 0);
|
||||
result = 31 * result + (hostnameVerifier != null ? hostnameVerifier.hashCode() : 0);
|
||||
result = 31 * result + (authenticator != null ? authenticator.hashCode() : 0);
|
||||
result = 31 * result + (proxy != null ? proxy.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -275,8 +275,9 @@ public final class Connection implements Closeable {
|
||||
case HTTP_PROXY_AUTH:
|
||||
requestHeaders = new RawHeaders(requestHeaders);
|
||||
URL url = new URL("https", tunnelRequest.host, tunnelRequest.port, "/");
|
||||
boolean credentialsFound = HttpAuthenticator.processAuthHeader(HTTP_PROXY_AUTH,
|
||||
responseHeaders, requestHeaders, route.proxy, url);
|
||||
boolean credentialsFound = HttpAuthenticator.processAuthHeader(
|
||||
route.address.authenticator, HTTP_PROXY_AUTH, responseHeaders, requestHeaders,
|
||||
route.proxy, url);
|
||||
if (credentialsFound) {
|
||||
continue;
|
||||
} else {
|
||||
|
||||
123
okhttp/src/main/java/com/squareup/okhttp/OkAuthenticator.java
Normal file
123
okhttp/src/main/java/com/squareup/okhttp/OkAuthenticator.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 com.squareup.okhttp;
|
||||
|
||||
import com.squareup.okhttp.internal.Base64;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Responds to authentication challenges from the remote web or proxy server by
|
||||
* returning credentials.
|
||||
*/
|
||||
public interface OkAuthenticator {
|
||||
/**
|
||||
* Returns a credential that satisfies the authentication challenge made by
|
||||
* {@code url}. Returns null if the challenge cannot be satisfied. This method
|
||||
* is called in response to an HTTP 401 unauthorized status code sent by the
|
||||
* origin server.
|
||||
*
|
||||
* @param challenges parsed "WWW-Authenticate" challenge headers from the HTTP
|
||||
* response.
|
||||
*/
|
||||
Credential authenticate(Proxy proxy, URL url, List<Challenge> challenges) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns a credential that satisfies the authentication challenge made by
|
||||
* {@code proxy}. Returns null if the challenge cannot be satisfied. This
|
||||
* method is called in response to an HTTP 401 unauthorized status code sent
|
||||
* by the proxy server.
|
||||
*
|
||||
* @param challenges parsed "Proxy-Authenticate" challenge headers from the
|
||||
* HTTP response.
|
||||
*/
|
||||
Credential authenticateProxy(Proxy proxy, URL url, List<Challenge> challenges) throws IOException;
|
||||
|
||||
/** An RFC 2617 challenge. */
|
||||
public final class Challenge {
|
||||
private final String scheme;
|
||||
private final String realm;
|
||||
|
||||
public Challenge(String scheme, String realm) {
|
||||
this.scheme = scheme;
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
/** Returns the authentication scheme, like {@code Basic}. */
|
||||
public String getScheme() {
|
||||
return scheme;
|
||||
}
|
||||
|
||||
/** Returns the protection space. */
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
return o instanceof Challenge
|
||||
&& ((Challenge) o).scheme.equals(scheme)
|
||||
&& ((Challenge) o).realm.equals(realm);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return scheme.hashCode() + 31 * realm.hashCode();
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return scheme + " realm=\"" + realm + "\"";
|
||||
}
|
||||
}
|
||||
|
||||
/** An RFC 2617 credential. */
|
||||
public final class Credential {
|
||||
private final String headerValue;
|
||||
|
||||
private Credential(String headerValue) {
|
||||
this.headerValue = headerValue;
|
||||
}
|
||||
|
||||
/** Returns an auth credential for the Basic scheme. */
|
||||
public static Credential basic(String userName, String password) {
|
||||
try {
|
||||
String usernameAndPassword = userName + ":" + password;
|
||||
byte[] bytes = usernameAndPassword.getBytes("ISO-8859-1");
|
||||
String encoded = Base64.encode(bytes);
|
||||
return new Credential("Basic " + encoded);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
public String getHeaderValue() {
|
||||
return headerValue;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
return o instanceof Credential && ((Credential) o).headerValue.equals(headerValue);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return headerValue.hashCode();
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return headerValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package com.squareup.okhttp;
|
||||
|
||||
import com.squareup.okhttp.internal.http.HttpAuthenticator;
|
||||
import com.squareup.okhttp.internal.http.HttpURLConnectionImpl;
|
||||
import com.squareup.okhttp.internal.http.HttpsURLConnectionImpl;
|
||||
import com.squareup.okhttp.internal.http.OkResponseCache;
|
||||
import com.squareup.okhttp.internal.http.OkResponseCacheAdapter;
|
||||
import java.net.Authenticator;
|
||||
import java.net.CookieHandler;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.Proxy;
|
||||
@@ -41,6 +43,7 @@ public final class OkHttpClient {
|
||||
private ResponseCache responseCache;
|
||||
private SSLSocketFactory sslSocketFactory;
|
||||
private HostnameVerifier hostnameVerifier;
|
||||
private OkAuthenticator authenticator;
|
||||
private ConnectionPool connectionPool;
|
||||
private boolean followProtocolRedirects = true;
|
||||
|
||||
@@ -149,6 +152,22 @@ public final class OkHttpClient {
|
||||
return hostnameVerifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authenticator used to respond to challenges from the remote web
|
||||
* server or proxy server.
|
||||
*
|
||||
* <p>If unset, the {@link Authenticator#setDefault system-wide default}
|
||||
* authenticator will be used.
|
||||
*/
|
||||
public OkHttpClient setAuthenticator(OkAuthenticator authenticator) {
|
||||
this.authenticator = authenticator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OkAuthenticator getAuthenticator() {
|
||||
return authenticator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connection pool used to recycle HTTP and HTTPS connections.
|
||||
*
|
||||
@@ -209,6 +228,9 @@ public final class OkHttpClient {
|
||||
result.hostnameVerifier = hostnameVerifier != null
|
||||
? hostnameVerifier
|
||||
: HttpsURLConnection.getDefaultHostnameVerifier();
|
||||
result.authenticator = authenticator != null
|
||||
? authenticator
|
||||
: HttpAuthenticator.SYSTEM_DEFAULT;
|
||||
result.connectionPool = connectionPool != null ? connectionPool : ConnectionPool.getDefault();
|
||||
result.followProtocolRedirects = followProtocolRedirects;
|
||||
return result;
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
*/
|
||||
package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.internal.Base64;
|
||||
import com.squareup.okhttp.OkAuthenticator;
|
||||
import com.squareup.okhttp.OkAuthenticator.Challenge;
|
||||
import java.io.IOException;
|
||||
import java.net.Authenticator;
|
||||
import java.net.InetAddress;
|
||||
@@ -27,11 +28,49 @@ import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.squareup.okhttp.OkAuthenticator.Credential;
|
||||
import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
|
||||
import static java.net.HttpURLConnection.HTTP_UNAUTHORIZED;
|
||||
|
||||
/** Handles HTTP authentication headers from origin and proxy servers. */
|
||||
public final class HttpAuthenticator {
|
||||
/** Uses the global authenticator to get the password. */
|
||||
public static final OkAuthenticator SYSTEM_DEFAULT = new OkAuthenticator() {
|
||||
@Override public Credential authenticate(
|
||||
Proxy proxy, URL url, List<Challenge> challenges) throws IOException {
|
||||
for (Challenge challenge : challenges) {
|
||||
PasswordAuthentication auth = Authenticator.requestPasswordAuthentication(url.getHost(),
|
||||
getConnectToInetAddress(proxy, url), url.getPort(), url.getProtocol(),
|
||||
challenge.getRealm(), challenge.getScheme(), url, Authenticator.RequestorType.SERVER);
|
||||
if (auth != null) {
|
||||
return Credential.basic(auth.getUserName(), new String(auth.getPassword()));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public Credential authenticateProxy(
|
||||
Proxy proxy, URL url, List<Challenge> challenges) throws IOException {
|
||||
for (Challenge challenge : challenges) {
|
||||
InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address();
|
||||
PasswordAuthentication auth = Authenticator.requestPasswordAuthentication(
|
||||
proxyAddress.getHostName(), getConnectToInetAddress(proxy, url), proxyAddress.getPort(),
|
||||
url.getProtocol(), challenge.getRealm(), challenge.getScheme(), url,
|
||||
Authenticator.RequestorType.PROXY);
|
||||
if (auth != null) {
|
||||
return Credential.basic(auth.getUserName(), new String(auth.getPassword()));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private InetAddress getConnectToInetAddress(Proxy proxy, URL url) throws IOException {
|
||||
return (proxy != null && proxy.type() != Proxy.Type.DIRECT)
|
||||
? ((InetSocketAddress) proxy.address()).getAddress()
|
||||
: InetAddress.getByName(url.getHost());
|
||||
}
|
||||
};
|
||||
|
||||
private HttpAuthenticator() {
|
||||
}
|
||||
|
||||
@@ -41,68 +80,33 @@ public final class HttpAuthenticator {
|
||||
* @return true if credentials have been added to successorRequestHeaders
|
||||
* and another request should be attempted.
|
||||
*/
|
||||
public static boolean processAuthHeader(int responseCode, RawHeaders responseHeaders,
|
||||
RawHeaders successorRequestHeaders, Proxy proxy, URL url) throws IOException {
|
||||
if (responseCode != HTTP_PROXY_AUTH && responseCode != HTTP_UNAUTHORIZED) {
|
||||
throw new IllegalArgumentException();
|
||||
public static boolean processAuthHeader(OkAuthenticator authenticator, int responseCode,
|
||||
RawHeaders responseHeaders, RawHeaders successorRequestHeaders, Proxy proxy, URL url)
|
||||
throws IOException {
|
||||
String responseField;
|
||||
String requestField;
|
||||
if (responseCode == HTTP_UNAUTHORIZED) {
|
||||
responseField = "WWW-Authenticate";
|
||||
requestField = "Authorization";
|
||||
} else if (responseCode == HTTP_PROXY_AUTH) {
|
||||
responseField = "Proxy-Authenticate";
|
||||
requestField = "Proxy-Authorization";
|
||||
} else {
|
||||
throw new IllegalArgumentException(); // TODO: ProtocolException?
|
||||
}
|
||||
|
||||
// Keep asking for username/password until authorized.
|
||||
String challengeHeader =
|
||||
responseCode == HTTP_PROXY_AUTH ? "Proxy-Authenticate" : "WWW-Authenticate";
|
||||
String credentials = getCredentials(responseHeaders, challengeHeader, proxy, url);
|
||||
if (credentials == null) {
|
||||
return false; // Could not find credentials so end the request cycle.
|
||||
}
|
||||
|
||||
// Add authorization credentials, bypassing the already-connected check.
|
||||
String fieldName = responseCode == HTTP_PROXY_AUTH ? "Proxy-Authorization" : "Authorization";
|
||||
successorRequestHeaders.set(fieldName, credentials);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authorization credentials that may satisfy the challenge.
|
||||
* Returns null if a challenge header was not provided or if credentials
|
||||
* were not available.
|
||||
*/
|
||||
private static String getCredentials(RawHeaders responseHeaders, String challengeHeader,
|
||||
Proxy proxy, URL url) throws IOException {
|
||||
List<Challenge> challenges = parseChallenges(responseHeaders, challengeHeader);
|
||||
List<Challenge> challenges = parseChallenges(responseHeaders, responseField);
|
||||
if (challenges.isEmpty()) {
|
||||
return null;
|
||||
return false; // Could not find a challenge so end the request cycle.
|
||||
}
|
||||
|
||||
for (Challenge challenge : challenges) {
|
||||
// Use the global authenticator to get the password.
|
||||
PasswordAuthentication auth;
|
||||
if (responseHeaders.getResponseCode() == HTTP_PROXY_AUTH) {
|
||||
InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address();
|
||||
auth = Authenticator.requestPasswordAuthentication(proxyAddress.getHostName(),
|
||||
getConnectToInetAddress(proxy, url), proxyAddress.getPort(), url.getProtocol(),
|
||||
challenge.realm, challenge.scheme, url, Authenticator.RequestorType.PROXY);
|
||||
} else {
|
||||
auth = Authenticator.requestPasswordAuthentication(url.getHost(),
|
||||
getConnectToInetAddress(proxy, url), url.getPort(), url.getProtocol(), challenge.realm,
|
||||
challenge.scheme, url, Authenticator.RequestorType.SERVER);
|
||||
}
|
||||
if (auth == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use base64 to encode the username and password.
|
||||
String usernameAndPassword = auth.getUserName() + ":" + new String(auth.getPassword());
|
||||
byte[] bytes = usernameAndPassword.getBytes("ISO-8859-1");
|
||||
String encoded = Base64.encode(bytes);
|
||||
return challenge.scheme + " " + encoded;
|
||||
Credential credential = responseHeaders.getResponseCode() == HTTP_PROXY_AUTH
|
||||
? authenticator.authenticateProxy(proxy, url, challenges)
|
||||
: authenticator.authenticate(proxy, url, challenges);
|
||||
if (credential == null) {
|
||||
return false; // Could not satisfy the challenge so end the request cycle.
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static InetAddress getConnectToInetAddress(Proxy proxy, URL url) throws IOException {
|
||||
return (proxy != null && proxy.type() != Proxy.Type.DIRECT)
|
||||
? ((InetSocketAddress) proxy.address()).getAddress() : InetAddress.getByName(url.getHost());
|
||||
// Add authorization credentials, bypassing the already-connected check.
|
||||
successorRequestHeaders.set(requestField, credential.getHeaderValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,25 +155,4 @@ public final class HttpAuthenticator {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** An RFC 2617 challenge. */
|
||||
private static final class Challenge {
|
||||
final String scheme;
|
||||
final String realm;
|
||||
|
||||
Challenge(String scheme, String realm) {
|
||||
this.scheme = scheme;
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
return o instanceof Challenge
|
||||
&& ((Challenge) o).scheme.equals(scheme)
|
||||
&& ((Challenge) o).realm.equals(realm);
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return scheme.hashCode() + 31 * realm.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ public class HttpEngine {
|
||||
hostnameVerifier = policy.hostnameVerifier;
|
||||
}
|
||||
Address address = new Address(uriHost, getEffectivePort(uri), sslSocketFactory,
|
||||
hostnameVerifier, policy.requestedProxy);
|
||||
hostnameVerifier, policy.authenticator, policy.requestedProxy);
|
||||
routeSelector = new RouteSelector(address, uri, policy.proxySelector, policy.connectionPool,
|
||||
Dns.DEFAULT, policy.getFailedRoutes());
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.Connection;
|
||||
import com.squareup.okhttp.ConnectionPool;
|
||||
import com.squareup.okhttp.OkAuthenticator;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Route;
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
@@ -83,6 +84,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
|
||||
/* SSL configuration; necessary for HTTP requests that get redirected to HTTPS. */
|
||||
SSLSocketFactory sslSocketFactory;
|
||||
HostnameVerifier hostnameVerifier;
|
||||
OkAuthenticator authenticator;
|
||||
final Set<Route> failedRoutes;
|
||||
|
||||
|
||||
@@ -104,6 +106,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
|
||||
this.connectionPool = client.getConnectionPool();
|
||||
this.sslSocketFactory = client.getSslSocketFactory();
|
||||
this.hostnameVerifier = client.getHostnameVerifier();
|
||||
this.authenticator = client.getAuthenticator();
|
||||
this.responseCache = responseCache;
|
||||
}
|
||||
|
||||
@@ -421,8 +424,9 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
|
||||
}
|
||||
// fall-through
|
||||
case HTTP_UNAUTHORIZED:
|
||||
boolean credentialsFound = HttpAuthenticator.processAuthHeader(getResponseCode(),
|
||||
httpEngine.getResponseHeaders().getHeaders(), rawRequestHeaders, selectedProxy, url);
|
||||
boolean credentialsFound = HttpAuthenticator.processAuthHeader(authenticator,
|
||||
getResponseCode(), httpEngine.getResponseHeaders().getHeaders(), rawRequestHeaders,
|
||||
selectedProxy, url);
|
||||
return credentialsFound ? Retry.SAME_CONNECTION : Retry.NONE;
|
||||
|
||||
case HTTP_MULT_CHOICE:
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.google.mockwebserver.MockWebServer;
|
||||
import com.squareup.okhttp.internal.RecordingHostnameVerifier;
|
||||
import com.squareup.okhttp.internal.SslContextBuilder;
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
import com.squareup.okhttp.internal.http.HttpAuthenticator;
|
||||
import com.squareup.okhttp.internal.mockspdyserver.MockSpdyServer;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
@@ -70,14 +71,15 @@ public final class ConnectionPoolTest {
|
||||
|
||||
@Before public void setUp() throws Exception {
|
||||
httpServer.play();
|
||||
httpAddress = new Address(httpServer.getHostName(), httpServer.getPort(), null, null, null);
|
||||
httpAddress = new Address(httpServer.getHostName(), httpServer.getPort(), null, null,
|
||||
HttpAuthenticator.SYSTEM_DEFAULT, null);
|
||||
httpSocketAddress = new InetSocketAddress(InetAddress.getByName(httpServer.getHostName()),
|
||||
httpServer.getPort());
|
||||
|
||||
spdyServer.play();
|
||||
spdyAddress =
|
||||
new Address(spdyServer.getHostName(), spdyServer.getPort(), sslContext.getSocketFactory(),
|
||||
new RecordingHostnameVerifier(), null);
|
||||
new RecordingHostnameVerifier(), HttpAuthenticator.SYSTEM_DEFAULT, null);
|
||||
spdySocketAddress = new InetSocketAddress(InetAddress.getByName(spdyServer.getHostName()),
|
||||
spdyServer.getPort());
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 com.squareup.okhttp.internal;
|
||||
|
||||
import com.squareup.okhttp.OkAuthenticator;
|
||||
import java.io.IOException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class RecordingOkAuthenticator implements OkAuthenticator {
|
||||
public final List<String> calls = new ArrayList<String>();
|
||||
public final Credential credential;
|
||||
|
||||
public RecordingOkAuthenticator(Credential credential) {
|
||||
this.credential = credential;
|
||||
}
|
||||
|
||||
@Override public Credential authenticate(Proxy proxy, URL url, List<Challenge> challenges)
|
||||
throws IOException {
|
||||
calls.add("authenticate"
|
||||
+ " proxy=" + proxy.type()
|
||||
+ " url=" + url
|
||||
+ " challenges=" + challenges);
|
||||
return credential;
|
||||
}
|
||||
|
||||
@Override public Credential authenticateProxy(Proxy proxy, URL url, List<Challenge> challenges)
|
||||
throws IOException {
|
||||
calls.add("authenticateProxy"
|
||||
+ " proxy=" + proxy.type()
|
||||
+ " url=" + url
|
||||
+ " challenges=" + challenges);
|
||||
return credential;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.squareup.okhttp.internal.http;
|
||||
import com.squareup.okhttp.Address;
|
||||
import com.squareup.okhttp.Connection;
|
||||
import com.squareup.okhttp.ConnectionPool;
|
||||
import com.squareup.okhttp.OkAuthenticator;
|
||||
import com.squareup.okhttp.Route;
|
||||
import com.squareup.okhttp.internal.Dns;
|
||||
import com.squareup.okhttp.internal.SslContextBuilder;
|
||||
@@ -78,11 +79,12 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
}
|
||||
|
||||
private final OkAuthenticator authenticator = HttpAuthenticator.SYSTEM_DEFAULT;
|
||||
private final FakeDns dns = new FakeDns();
|
||||
private final FakeProxySelector proxySelector = new FakeProxySelector();
|
||||
|
||||
@Test public void singleRoute() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, null, null, null);
|
||||
Address address = new Address(uriHost, uriPort, null, null, authenticator, null);
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
Collections.EMPTY_SET);
|
||||
|
||||
@@ -100,7 +102,7 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void singleRouteReturnsFailedRoute() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, null, null, null);
|
||||
Address address = new Address(uriHost, uriPort, null, null, authenticator, null);
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
Collections.EMPTY_SET);
|
||||
|
||||
@@ -121,7 +123,7 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void explicitProxyTriesThatProxiesAddressesOnly() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, null, null, proxyA);
|
||||
Address address = new Address(uriHost, uriPort, null, null, authenticator, proxyA);
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
Collections.EMPTY_SET);
|
||||
|
||||
@@ -138,7 +140,7 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void explicitDirectProxy() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, null, null, NO_PROXY);
|
||||
Address address = new Address(uriHost, uriPort, null, null, authenticator, NO_PROXY);
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
Collections.EMPTY_SET);
|
||||
|
||||
@@ -153,7 +155,7 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void proxySelectorReturnsNull() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, null, null, null);
|
||||
Address address = new Address(uriHost, uriPort, null, null, authenticator, null);
|
||||
|
||||
proxySelector.proxies = null;
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
@@ -169,7 +171,7 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void proxySelectorReturnsNoProxies() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, null, null, null);
|
||||
Address address = new Address(uriHost, uriPort, null, null, authenticator, null);
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
Collections.EMPTY_SET);
|
||||
|
||||
@@ -184,7 +186,7 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void proxySelectorReturnsMultipleProxies() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, null, null, null);
|
||||
Address address = new Address(uriHost, uriPort, null, null, authenticator, null);
|
||||
|
||||
proxySelector.proxies.add(proxyA);
|
||||
proxySelector.proxies.add(proxyB);
|
||||
@@ -218,7 +220,7 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void proxySelectorDirectConnectionsAreSkipped() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, null, null, null);
|
||||
Address address = new Address(uriHost, uriPort, null, null, authenticator, null);
|
||||
|
||||
proxySelector.proxies.add(NO_PROXY);
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
@@ -235,7 +237,7 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void proxyDnsFailureContinuesToNextProxy() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, null, null, null);
|
||||
Address address = new Address(uriHost, uriPort, null, null, authenticator, null);
|
||||
|
||||
proxySelector.proxies.add(proxyA);
|
||||
proxySelector.proxies.add(proxyB);
|
||||
@@ -274,8 +276,8 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void nonSslErrorAddsAllTlsModesToFailedRoute() throws Exception {
|
||||
Address address =
|
||||
new Address(uriHost, uriPort, socketFactory, hostnameVerifier, Proxy.NO_PROXY);
|
||||
Address address = new Address(uriHost, uriPort, socketFactory, hostnameVerifier, authenticator,
|
||||
Proxy.NO_PROXY);
|
||||
Set<Route> failedRoutes = new LinkedHashSet<Route>();
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
failedRoutes);
|
||||
@@ -287,8 +289,8 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void sslErrorAddsOnlyFailedTlsModeToFailedRoute() throws Exception {
|
||||
Address address =
|
||||
new Address(uriHost, uriPort, socketFactory, hostnameVerifier, Proxy.NO_PROXY);
|
||||
Address address = new Address(uriHost, uriPort, socketFactory, hostnameVerifier, authenticator,
|
||||
Proxy.NO_PROXY);
|
||||
Set<Route> failedRoutes = new LinkedHashSet<Route>();
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
failedRoutes);
|
||||
@@ -300,7 +302,8 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void multipleProxiesMultipleInetAddressesMultipleTlsModes() throws Exception {
|
||||
Address address = new Address(uriHost, uriPort, socketFactory, hostnameVerifier, null);
|
||||
Address address = new Address(uriHost, uriPort, socketFactory, hostnameVerifier, authenticator,
|
||||
null);
|
||||
proxySelector.proxies.add(proxyA);
|
||||
proxySelector.proxies.add(proxyB);
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
@@ -338,8 +341,8 @@ public final class RouteSelectorTest {
|
||||
}
|
||||
|
||||
@Test public void failedRoutesAreLast() throws Exception {
|
||||
Address address =
|
||||
new Address(uriHost, uriPort, socketFactory, hostnameVerifier, Proxy.NO_PROXY);
|
||||
Address address = new Address(uriHost, uriPort, socketFactory, hostnameVerifier, authenticator,
|
||||
Proxy.NO_PROXY);
|
||||
|
||||
Set<Route> failedRoutes = new LinkedHashSet<Route>(1);
|
||||
RouteSelector routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns,
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.squareup.okhttp.HttpResponseCache;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.internal.RecordingAuthenticator;
|
||||
import com.squareup.okhttp.internal.RecordingHostnameVerifier;
|
||||
import com.squareup.okhttp.internal.RecordingOkAuthenticator;
|
||||
import com.squareup.okhttp.internal.SslContextBuilder;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@@ -78,6 +79,7 @@ import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
|
||||
import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_START;
|
||||
import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
|
||||
import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
|
||||
import static com.squareup.okhttp.OkAuthenticator.Credential;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@@ -2358,6 +2360,30 @@ public final class URLConnectionTest {
|
||||
fail("TODO");
|
||||
}
|
||||
|
||||
@Test public void customAuthenticator() throws Exception {
|
||||
MockResponse pleaseAuthenticate = new MockResponse().setResponseCode(401)
|
||||
.addHeader("WWW-Authenticate: Basic realm=\"protected area\"")
|
||||
.setBody("Please authenticate.");
|
||||
server.enqueue(pleaseAuthenticate);
|
||||
server.enqueue(new MockResponse().setBody("A"));
|
||||
server.play();
|
||||
|
||||
Credential credential = Credential.basic("jesse", "peanutbutter");
|
||||
RecordingOkAuthenticator authenticator = new RecordingOkAuthenticator(credential);
|
||||
client.setAuthenticator(authenticator);
|
||||
assertContent("A", client.open(server.getUrl("/private")));
|
||||
|
||||
assertContainsNoneMatching(server.takeRequest().getHeaders(), "Authorization: .*");
|
||||
assertContains(server.takeRequest().getHeaders(),
|
||||
"Authorization: " + credential.getHeaderValue());
|
||||
|
||||
assertEquals(1, authenticator.calls.size());
|
||||
String call = authenticator.calls.get(0);
|
||||
assertTrue(call, call.contains("proxy=DIRECT"));
|
||||
assertTrue(call, call.contains("url=" + server.getUrl("/private")));
|
||||
assertTrue(call, call.contains("challenges=[Basic realm=\"protected area\"]"));
|
||||
}
|
||||
|
||||
/** Returns a gzipped copy of {@code bytes}. */
|
||||
public byte[] gzip(byte[] bytes) throws IOException {
|
||||
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
|
||||
|
||||
Reference in New Issue
Block a user