You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2014/01/15 17:37:04 UTC
[4/5] CB-5799 Update version of OkHTTP to 1.3
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/Platform.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/Platform.java b/framework/src/com/squareup/okhttp/internal/Platform.java
old mode 100644
new mode 100755
index 6b4ac34..d5884b1
--- a/framework/src/com/squareup/okhttp/internal/Platform.java
+++ b/framework/src/com/squareup/okhttp/internal/Platform.java
@@ -16,7 +16,6 @@
*/
package com.squareup.okhttp.internal;
-import com.squareup.okhttp.OkHttpClient;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
@@ -25,7 +24,7 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
-import java.net.NetworkInterface;
+import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.URI;
@@ -57,6 +56,11 @@ public class Platform {
return PLATFORM;
}
+ /** Prefix used on custom headers. */
+ public String getPrefix() {
+ return "OkHttp";
+ }
+
public void logW(String warning) {
System.out.println(warning);
}
@@ -99,6 +103,11 @@ public class Platform {
public void setNpnProtocols(SSLSocket socket, byte[] npnProtocols) {
}
+ public void connectSocket(Socket socket, InetSocketAddress address,
+ int connectTimeout) throws IOException {
+ socket.connect(address, connectTimeout);
+ }
+
/**
* Returns a deflater output stream that supports SYNC_FLUSH for SPDY name
* value blocks. This throws an {@link UnsupportedOperationException} on
@@ -125,33 +134,21 @@ public class Platform {
}
}
- /**
- * Returns the maximum transmission unit of the network interface used by
- * {@code socket}, or a reasonable default if this platform doesn't expose the
- * MTU to the application layer.
- *
- * <p>The returned value should only be used as an optimization; such as to
- * size buffers efficiently.
- */
- public int getMtu(Socket socket) throws IOException {
- return 1400; // Smaller than 1500 to leave room for headers on interfaces like PPPoE.
- }
-
/** Attempt to match the host runtime to a capable Platform implementation. */
private static Platform findPlatform() {
- Method getMtu;
- try {
- getMtu = NetworkInterface.class.getMethod("getMTU");
- } catch (NoSuchMethodException e) {
- return new Platform(); // No Java 1.6 APIs. It's either Java 1.5, Android 2.2 or earlier.
- }
-
// Attempt to find Android 2.3+ APIs.
Class<?> openSslSocketClass;
Method setUseSessionTickets;
Method setHostname;
try {
- openSslSocketClass = Class.forName("org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl");
+ try {
+ openSslSocketClass = Class.forName("com.android.org.conscrypt.OpenSSLSocketImpl");
+ } catch (ClassNotFoundException ignored) {
+ // Older platform before being unbundled.
+ openSslSocketClass = Class.forName(
+ "org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl");
+ }
+
setUseSessionTickets = openSslSocketClass.getMethod("setUseSessionTickets", boolean.class);
setHostname = openSslSocketClass.getMethod("setHostname", String.class);
@@ -159,10 +156,10 @@ public class Platform {
try {
Method setNpnProtocols = openSslSocketClass.getMethod("setNpnProtocols", byte[].class);
Method getNpnSelectedProtocol = openSslSocketClass.getMethod("getNpnSelectedProtocol");
- return new Android41(getMtu, openSslSocketClass, setUseSessionTickets, setHostname,
+ return new Android41(openSslSocketClass, setUseSessionTickets, setHostname,
setNpnProtocols, getNpnSelectedProtocol);
} catch (NoSuchMethodException ignored) {
- return new Android23(getMtu, openSslSocketClass, setUseSessionTickets, setHostname);
+ return new Android23(openSslSocketClass, setUseSessionTickets, setHostname);
}
} catch (ClassNotFoundException ignored) {
// This isn't an Android runtime.
@@ -179,55 +176,43 @@ public class Platform {
Class<?> serverProviderClass = Class.forName(npnClassName + "$ServerProvider");
Method putMethod = nextProtoNegoClass.getMethod("put", SSLSocket.class, providerClass);
Method getMethod = nextProtoNegoClass.getMethod("get", SSLSocket.class);
- return new JdkWithJettyNpnPlatform(getMtu, putMethod, getMethod, clientProviderClass,
- serverProviderClass);
+ return new JdkWithJettyNpnPlatform(
+ putMethod, getMethod, clientProviderClass, serverProviderClass);
} catch (ClassNotFoundException ignored) {
// NPN isn't on the classpath.
} catch (NoSuchMethodException ignored) {
// The NPN version isn't what we expect.
}
- return getMtu != null ? new Java5(getMtu) : new Platform();
- }
-
- private static class Java5 extends Platform {
- private final Method getMtu;
-
- private Java5(Method getMtu) {
- this.getMtu = getMtu;
- }
-
- @Override public int getMtu(Socket socket) throws IOException {
- try {
- NetworkInterface networkInterface = NetworkInterface.getByInetAddress(
- socket.getLocalAddress());
- return (Integer) getMtu.invoke(networkInterface);
- } catch (IllegalAccessException e) {
- throw new AssertionError(e);
- } catch (InvocationTargetException e) {
- if (e.getCause() instanceof IOException) throw (IOException) e.getCause();
- throw new RuntimeException(e.getCause());
- }
- }
+ return new Platform();
}
- /**
- * Android version 2.3 and newer support TLS session tickets and server name
- * indication (SNI).
- */
- private static class Android23 extends Java5 {
+ /** Android version 2.3 and newer support TLS session tickets and server name indication (SNI). */
+ private static class Android23 extends Platform {
protected final Class<?> openSslSocketClass;
private final Method setUseSessionTickets;
private final Method setHostname;
- private Android23(Method getMtu, Class<?> openSslSocketClass, Method setUseSessionTickets,
- Method setHostname) {
- super(getMtu);
+ private Android23(
+ Class<?> openSslSocketClass, Method setUseSessionTickets, Method setHostname) {
this.openSslSocketClass = openSslSocketClass;
this.setUseSessionTickets = setUseSessionTickets;
this.setHostname = setHostname;
}
+ @Override public void connectSocket(Socket socket, InetSocketAddress address,
+ int connectTimeout) throws IOException {
+ try {
+ socket.connect(address, connectTimeout);
+ } catch (SecurityException se) {
+ // Before android 4.3, socket.connect could throw a SecurityException
+ // if opening a socket resulted in an EACCES error.
+ IOException ioException = new IOException("Exception in connect");
+ ioException.initCause(se);
+ throw ioException;
+ }
+ }
+
@Override public void enableTlsExtensions(SSLSocket socket, String uriHost) {
super.enableTlsExtensions(socket, uriHost);
if (openSslSocketClass.isInstance(socket)) {
@@ -249,9 +234,9 @@ public class Platform {
private final Method setNpnProtocols;
private final Method getNpnSelectedProtocol;
- private Android41(Method getMtu, Class<?> openSslSocketClass, Method setUseSessionTickets,
- Method setHostname, Method setNpnProtocols, Method getNpnSelectedProtocol) {
- super(getMtu, openSslSocketClass, setUseSessionTickets, setHostname);
+ private Android41(Class<?> openSslSocketClass, Method setUseSessionTickets, Method setHostname,
+ Method setNpnProtocols, Method getNpnSelectedProtocol) {
+ super(openSslSocketClass, setUseSessionTickets, setHostname);
this.setNpnProtocols = setNpnProtocols;
this.getNpnSelectedProtocol = getNpnSelectedProtocol;
}
@@ -283,19 +268,15 @@ public class Platform {
}
}
- /**
- * OpenJDK 7 plus {@code org.mortbay.jetty.npn/npn-boot} on the boot class
- * path.
- */
- private static class JdkWithJettyNpnPlatform extends Java5 {
+ /** OpenJDK 7 plus {@code org.mortbay.jetty.npn/npn-boot} on the boot class path. */
+ private static class JdkWithJettyNpnPlatform extends Platform {
private final Method getMethod;
private final Method putMethod;
private final Class<?> clientProviderClass;
private final Class<?> serverProviderClass;
- public JdkWithJettyNpnPlatform(Method getMtu, Method putMethod, Method getMethod,
- Class<?> clientProviderClass, Class<?> serverProviderClass) {
- super(getMtu);
+ public JdkWithJettyNpnPlatform(Method putMethod, Method getMethod, Class<?> clientProviderClass,
+ Class<?> serverProviderClass) {
this.putMethod = putMethod;
this.getMethod = getMethod;
this.clientProviderClass = clientProviderClass;
@@ -328,7 +309,7 @@ public class Platform {
JettyNpnProvider provider =
(JettyNpnProvider) Proxy.getInvocationHandler(getMethod.invoke(null, socket));
if (!provider.unsupported && provider.selected == null) {
- Logger logger = Logger.getLogger(OkHttpClient.class.getName());
+ 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?");
return null;
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/StrictLineReader.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/StrictLineReader.java b/framework/src/com/squareup/okhttp/internal/StrictLineReader.java
old mode 100644
new mode 100755
index 3ddc693..74af6fd
--- a/framework/src/com/squareup/okhttp/internal/StrictLineReader.java
+++ b/framework/src/com/squareup/okhttp/internal/StrictLineReader.java
@@ -146,8 +146,7 @@ public class StrictLineReader implements Closeable {
// Let's anticipate up to 80 characters on top of those already read.
ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
- @Override
- public String toString() {
+ @Override public String toString() {
int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
try {
return new String(buf, 0, length, charset.name());
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/Util.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/Util.java b/framework/src/com/squareup/okhttp/internal/Util.java
old mode 100644
new mode 100755
index 290e5ea..9c5b008
--- a/framework/src/com/squareup/okhttp/internal/Util.java
+++ b/framework/src/com/squareup/okhttp/internal/Util.java
@@ -24,11 +24,19 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
import java.net.Socket;
+import java.net.ServerSocket;
import java.net.URI;
import java.net.URL;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicReference;
/** Junk drawer of utility methods. */
@@ -46,6 +54,9 @@ public final class Util {
public static final Charset UTF_8 = Charset.forName("UTF-8");
private static AtomicReference<byte[]> skipBuffer = new AtomicReference<byte[]>();
+ private static final char[] DIGITS =
+ { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
private Util() {
}
@@ -127,6 +138,21 @@ public final class Util {
}
/**
+ * Closes {@code serverSocket}, ignoring any checked exceptions. Does nothing if
+ * {@code serverSocket} is null.
+ */
+ public static void closeQuietly(ServerSocket serverSocket) {
+ if (serverSocket != null) {
+ try {
+ serverSocket.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+
+ /**
* Closes {@code a} and {@code b}. If either close fails, this completes
* the other close and rethrows the first encountered exception.
*/
@@ -258,6 +284,8 @@ public final class Util {
* buffer.
*/
public static long skipByReading(InputStream in, long byteCount) throws IOException {
+ if (byteCount == 0) return 0L;
+
// acquire the shared skip buffer.
byte[] buffer = skipBuffer.getAndSet(null);
if (buffer == null) {
@@ -324,4 +352,43 @@ public final class Util {
}
return result.toString();
}
+
+ /** Returns a 32 character string containing a hash of {@code s}. */
+ public static String hash(String s) {
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("MD5");
+ byte[] md5bytes = messageDigest.digest(s.getBytes("UTF-8"));
+ return bytesToHexString(md5bytes);
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ } catch (UnsupportedEncodingException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static String bytesToHexString(byte[] bytes) {
+ char[] digits = DIGITS;
+ char[] buf = new char[bytes.length * 2];
+ int c = 0;
+ for (byte b : bytes) {
+ buf[c++] = digits[(b >> 4) & 0xf];
+ buf[c++] = digits[b & 0xf];
+ }
+ return new String(buf);
+ }
+
+ /** Returns an immutable copy of {@code list}. */
+ public static <T> List<T> immutableList(List<T> list) {
+ return Collections.unmodifiableList(new ArrayList<T>(list));
+ }
+
+ public static ThreadFactory daemonThreadFactory(final String name) {
+ return new ThreadFactory() {
+ @Override public Thread newThread(Runnable runnable) {
+ Thread result = new Thread(runnable, name);
+ result.setDaemon(true);
+ return result;
+ }
+ };
+ }
}
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java b/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java
old mode 100644
new mode 100755
index 187f3b6..a5d39b3
--- a/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java
+++ b/framework/src/com/squareup/okhttp/internal/http/AbstractHttpInputStream.java
@@ -79,11 +79,11 @@ abstract class AbstractHttpInputStream extends InputStream {
* Closes the cache entry and makes the socket available for reuse. This
* should be invoked when the end of the body has been reached.
*/
- protected final void endOfInput(boolean streamCancelled) throws IOException {
+ protected final void endOfInput() throws IOException {
if (cacheRequest != null) {
cacheBody.close();
}
- httpEngine.release(streamCancelled);
+ httpEngine.release(false);
}
/**
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java b/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java
deleted file mode 100644
index 90675b0..0000000
--- a/framework/src/com/squareup/okhttp/internal/http/AbstractHttpOutputStream.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * 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.http;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * An output stream for the body of an HTTP request.
- *
- * <p>Since a single socket's output stream may be used to write multiple HTTP
- * requests to the same server, subclasses should not close the socket stream.
- */
-abstract class AbstractHttpOutputStream extends OutputStream {
- protected boolean closed;
-
- @Override public final void write(int data) throws IOException {
- write(new byte[] { (byte) data });
- }
-
- protected final void checkNotClosed() throws IOException {
- if (closed) {
- throw new IOException("stream closed");
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java b/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java
old mode 100644
new mode 100755
index 12e6409..d5f0f4f
--- a/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HeaderParser.java
@@ -27,11 +27,11 @@ final class HeaderParser {
int pos = 0;
while (pos < value.length()) {
int tokenStart = pos;
- pos = skipUntil(value, pos, "=,");
+ pos = skipUntil(value, pos, "=,;");
String directive = value.substring(tokenStart, pos).trim();
- if (pos == value.length() || value.charAt(pos) == ',') {
- pos++; // consume ',' (if necessary)
+ if (pos == value.length() || value.charAt(pos) == ',' || value.charAt(pos) == ';') {
+ pos++; // consume ',' or ';' (if necessary)
handler.handle(directive, null);
continue;
}
@@ -52,7 +52,7 @@ final class HeaderParser {
// unquoted string
} else {
int parameterStart = pos;
- pos = skipUntil(value, pos, ",");
+ pos = skipUntil(value, pos, ",;");
parameter = value.substring(parameterStart, pos).trim();
}
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java b/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java
old mode 100644
new mode 100755
index 4ccd12a..1ad3689
--- a/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpAuthenticator.java
@@ -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,57 @@ 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) {
+ if (!"Basic".equalsIgnoreCase(challenge.getScheme())) {
+ continue;
+ }
+
+ 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) {
+ if (!"Basic".equalsIgnoreCase(challenge.getScheme())) {
+ continue;
+ }
+
+ 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 +88,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;
}
/**
@@ -134,7 +146,7 @@ public final class HttpAuthenticator {
// It needs to be fixed to handle any scheme and any parameters
// http://code.google.com/p/android/issues/detail?id=11140
- if (!value.regionMatches(pos, "realm=\"", 0, "realm=\"".length())) {
+ if (!value.regionMatches(true, pos, "realm=\"", 0, "realm=\"".length())) {
break; // Unexpected challenge parameter; give up!
}
@@ -151,25 +163,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();
- }
- }
}
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpDate.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpDate.java b/framework/src/com/squareup/okhttp/internal/http/HttpDate.java
old mode 100644
new mode 100755
index acb5fda..b4d2c7c
--- a/framework/src/com/squareup/okhttp/internal/http/HttpDate.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpDate.java
@@ -36,14 +36,13 @@ final class HttpDate {
new ThreadLocal<DateFormat>() {
@Override protected DateFormat initialValue() {
DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
- rfc1123.setTimeZone(TimeZone.getTimeZone("UTC"));
+ rfc1123.setTimeZone(TimeZone.getTimeZone("GMT"));
return rfc1123;
}
};
/** If we fail to parse a date in a non-standard format, try each of these formats in sequence. */
- private static final String[] BROWSER_COMPATIBLE_DATE_FORMATS = new String[] {
- /* This list comes from {@code org.apache.http.impl.cookie.BrowserCompatSpec}. */
+ private static final String[] BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS = new String[] {
"EEEE, dd-MMM-yy HH:mm:ss zzz", // RFC 1036
"EEE MMM d HH:mm:ss yyyy", // ANSI C asctime()
"EEE, dd-MMM-yyyy HH:mm:ss z", "EEE, dd-MMM-yyyy HH-mm-ss z", "EEE, dd MMM yy HH:mm:ss z",
@@ -54,19 +53,26 @@ final class HttpDate {
/* RI bug 6641315 claims a cookie of this format was once served by www.yahoo.com */
"EEE MMM d yyyy HH:mm:ss z", };
- /**
- * Returns the date for {@code value}. Returns null if the value couldn't be
- * parsed.
- */
+ private static final DateFormat[] BROWSER_COMPATIBLE_DATE_FORMATS =
+ new DateFormat[BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS.length];
+
+ /** Returns the date for {@code value}. Returns null if the value couldn't be parsed. */
public static Date parse(String value) {
try {
return STANDARD_DATE_FORMAT.get().parse(value);
- } catch (ParseException ignore) {
+ } catch (ParseException ignored) {
}
- for (String formatString : BROWSER_COMPATIBLE_DATE_FORMATS) {
- try {
- return new SimpleDateFormat(formatString, Locale.US).parse(value);
- } catch (ParseException ignore) {
+ synchronized (BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS) {
+ for (int i = 0, count = BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS.length; i < count; i++) {
+ DateFormat format = BROWSER_COMPATIBLE_DATE_FORMATS[i];
+ if (format == null) {
+ format = new SimpleDateFormat(BROWSER_COMPATIBLE_DATE_FORMAT_STRINGS[i], Locale.US);
+ BROWSER_COMPATIBLE_DATE_FORMATS[i] = format;
+ }
+ try {
+ return format.parse(value);
+ } catch (ParseException ignored) {
+ }
}
}
return null;
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java b/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java
old mode 100644
new mode 100755
index 7a06dca..4a2dad4
--- a/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java
@@ -19,6 +19,8 @@ package com.squareup.okhttp.internal.http;
import com.squareup.okhttp.Address;
import com.squareup.okhttp.Connection;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.OkResponseCache;
import com.squareup.okhttp.ResponseSource;
import com.squareup.okhttp.TunnelRequest;
import com.squareup.okhttp.internal.Dns;
@@ -31,6 +33,7 @@ import java.io.OutputStream;
import java.net.CacheRequest;
import java.net.CacheResponse;
import java.net.CookieHandler;
+import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
@@ -85,7 +88,8 @@ public class HttpEngine {
};
public static final int HTTP_CONTINUE = 100;
- protected final HttpURLConnectionImpl policy;
+ protected final Policy policy;
+ protected final OkHttpClient client;
protected final String method;
@@ -106,6 +110,9 @@ public class HttpEngine {
/** The time when the request headers were written, or -1 if they haven't been written yet. */
long sentRequestMillis = -1;
+ /** Whether the connection has been established. */
+ boolean connected;
+
/**
* True if this client added an "Accept-Encoding: gzip" header field and is
* therefore responsible for also decompressing the transfer stream.
@@ -137,14 +144,15 @@ public class HttpEngine {
/**
* @param requestHeaders the client's supplied request headers. This class
- * creates a private copy that it can mutate.
+ * creates a private copy that it can mutate.
* @param connection the connection used for an intermediate response
- * immediately prior to this request/response pair, such as a same-host
- * redirect. This engine assumes ownership of the connection and must
- * release it when it is unneeded.
+ * immediately prior to this request/response pair, such as a same-host
+ * redirect. This engine assumes ownership of the connection and must
+ * release it when it is unneeded.
*/
- public HttpEngine(HttpURLConnectionImpl policy, String method, RawHeaders requestHeaders,
+ public HttpEngine(OkHttpClient client, Policy policy, String method, RawHeaders requestHeaders,
Connection connection, RetryableOutputStream requestBodyOut) throws IOException {
+ this.client = client;
this.policy = policy;
this.method = method;
this.connection = connection;
@@ -175,8 +183,9 @@ public class HttpEngine {
prepareRawRequestHeaders();
initResponseSource();
- if (policy.responseCache != null) {
- policy.responseCache.trackResponse(responseSource);
+ OkResponseCache responseCache = client.getOkResponseCache();
+ if (responseCache != null) {
+ responseCache.trackResponse(responseSource);
}
// The raw response source may require the network, but the request
@@ -196,8 +205,7 @@ public class HttpEngine {
if (responseSource.requiresConnection()) {
sendSocketRequest();
} else if (connection != null) {
- policy.connectionPool.recycle(connection);
- policy.getFailedRoutes().remove(connection.getRoute());
+ client.getConnectionPool().recycle(connection);
connection = null;
}
}
@@ -208,15 +216,14 @@ public class HttpEngine {
*/
private void initResponseSource() throws IOException {
responseSource = ResponseSource.NETWORK;
- if (!policy.getUseCaches() || policy.responseCache == null) {
- return;
- }
+ if (!policy.getUseCaches()) return;
- CacheResponse candidate =
- policy.responseCache.get(uri, method, requestHeaders.getHeaders().toMultimap(false));
- if (candidate == null) {
- return;
- }
+ OkResponseCache responseCache = client.getOkResponseCache();
+ if (responseCache == null) return;
+
+ CacheResponse candidate = responseCache.get(
+ uri, method, requestHeaders.getHeaders().toMultimap(false));
+ if (candidate == null) return;
Map<String, List<String>> responseHeadersMap = candidate.getHeaders();
cachedResponseBody = candidate.getBody();
@@ -274,22 +281,24 @@ public class HttpEngine {
SSLSocketFactory sslSocketFactory = null;
HostnameVerifier hostnameVerifier = null;
if (uri.getScheme().equalsIgnoreCase("https")) {
- sslSocketFactory = policy.sslSocketFactory;
- hostnameVerifier = policy.hostnameVerifier;
+ sslSocketFactory = client.getSslSocketFactory();
+ hostnameVerifier = client.getHostnameVerifier();
}
Address address = new Address(uriHost, getEffectivePort(uri), sslSocketFactory,
- hostnameVerifier, policy.requestedProxy);
- routeSelector = new RouteSelector(address, uri, policy.proxySelector, policy.connectionPool,
- Dns.DEFAULT, policy.getFailedRoutes());
+ hostnameVerifier, client.getAuthenticator(), client.getProxy(), client.getTransports());
+ routeSelector = new RouteSelector(address, uri, client.getProxySelector(),
+ client.getConnectionPool(), Dns.DEFAULT, client.getRoutesDatabase());
}
- connection = routeSelector.next();
+ connection = routeSelector.next(method);
if (!connection.isConnected()) {
- connection.connect(policy.getConnectTimeout(), policy.getReadTimeout(), getTunnelConfig());
- policy.connectionPool.maybeShare(connection);
- policy.getFailedRoutes().remove(connection.getRoute());
+ connection.connect(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig());
+ client.getConnectionPool().maybeShare(connection);
+ client.getRoutesDatabase().connected(connection.getRoute());
+ } else if (!connection.isSpdy()) {
+ connection.updateReadTimeout(client.getReadTimeout());
}
connected(connection);
- if (connection.getRoute().getProxy() != policy.requestedProxy) {
+ if (connection.getRoute().getProxy() != client.getProxy()) {
// Update the request line if the proxy changed; it may need a host name.
requestHeaders.getHeaders().setRequestLine(getRequestLine());
}
@@ -300,6 +309,8 @@ public class HttpEngine {
* pool. Subclasses use this hook to get a reference to the TLS data.
*/
protected void connected(Connection connection) {
+ policy.setSelectedProxy(connection.getRoute().getProxy());
+ connected = true;
}
/**
@@ -328,7 +339,7 @@ public class HttpEngine {
}
boolean hasRequestBody() {
- return method.equals("POST") || method.equals("PUT");
+ return method.equals("POST") || method.equals("PUT") || method.equals("PATCH");
}
/** Returns the request body or null if this request doesn't have a body. */
@@ -387,17 +398,20 @@ public class HttpEngine {
private void maybeCache() throws IOException {
// Are we caching at all?
- if (!policy.getUseCaches() || policy.responseCache == null) {
- return;
- }
+ if (!policy.getUseCaches()) return;
+ OkResponseCache responseCache = client.getOkResponseCache();
+ if (responseCache == null) return;
+
+ HttpURLConnection connectionToCache = policy.getHttpConnectionToCache();
// Should we cache this response for this request?
if (!responseHeaders.isCacheable(requestHeaders)) {
+ responseCache.maybeRemove(connectionToCache.getRequestMethod(), uri);
return;
}
// Offer this request to the cache.
- cacheRequest = policy.responseCache.put(uri, policy.getHttpConnectionToCache());
+ cacheRequest = responseCache.put(uri, connectionToCache);
}
/**
@@ -409,7 +423,7 @@ public class HttpEngine {
public final void automaticallyReleaseConnectionToPool() {
automaticallyReleaseConnectionToPool = true;
if (connection != null && connectionReleased) {
- policy.connectionPool.recycle(connection);
+ client.getConnectionPool().recycle(connection);
connection = null;
}
}
@@ -419,7 +433,7 @@ public class HttpEngine {
* closed. Also call {@link #automaticallyReleaseConnectionToPool} unless
* the connection will be used to follow a redirect.
*/
- public final void release(boolean streamCancelled) {
+ public final void release(boolean streamCanceled) {
// If the response body comes from the cache, close it.
if (responseBodyIn == cachedResponseBody) {
Util.closeQuietly(responseBodyIn);
@@ -428,12 +442,12 @@ public class HttpEngine {
if (!connectionReleased && connection != null) {
connectionReleased = true;
- if (transport == null || !transport.makeReusable(streamCancelled, requestBodyOut,
- responseTransferIn)) {
+ if (transport == null
+ || !transport.makeReusable(streamCanceled, requestBodyOut, responseTransferIn)) {
Util.closeQuietly(connection);
connection = null;
} else if (automaticallyReleaseConnectionToPool) {
- policy.connectionPool.recycle(connection);
+ client.getConnectionPool().recycle(connection);
connection = null;
}
}
@@ -521,7 +535,7 @@ public class HttpEngine {
requestHeaders.setIfModifiedSince(new Date(ifModifiedSince));
}
- CookieHandler cookieHandler = policy.cookieHandler;
+ CookieHandler cookieHandler = client.getCookieHandler();
if (cookieHandler != null) {
requestHeaders.addCookies(
cookieHandler.get(uri, requestHeaders.getHeaders().toMultimap(false)));
@@ -635,9 +649,17 @@ public class HttpEngine {
if (cachedResponseHeaders.validate(responseHeaders)) {
release(false);
ResponseHeaders combinedHeaders = cachedResponseHeaders.combine(responseHeaders);
- setResponse(combinedHeaders, cachedResponseBody);
- policy.responseCache.trackConditionalCacheHit();
- policy.responseCache.update(cacheResponse, policy.getHttpConnectionToCache());
+ this.responseHeaders = combinedHeaders;
+
+ // Update the cache after applying the combined headers but before initializing the content
+ // stream, otherwise the Content-Encoding header (if present) will be stripped from the
+ // combined headers and not end up in the cache file if transparent gzip compression is
+ // turned on.
+ OkResponseCache responseCache = client.getOkResponseCache();
+ responseCache.trackConditionalCacheHit();
+ responseCache.update(cacheResponse, policy.getHttpConnectionToCache());
+
+ initContentStream(cachedResponseBody);
return;
} else {
Util.closeQuietly(cachedResponseBody);
@@ -656,7 +678,7 @@ public class HttpEngine {
}
public void receiveHeaders(RawHeaders headers) throws IOException {
- CookieHandler cookieHandler = policy.cookieHandler;
+ CookieHandler cookieHandler = client.getCookieHandler();
if (cookieHandler != null) {
cookieHandler.put(uri, headers.toMultimap(true));
}
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java b/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java
deleted file mode 100644
index 8735166..0000000
--- a/framework/src/com/squareup/okhttp/internal/http/HttpResponseCache.java
+++ /dev/null
@@ -1,608 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * 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.http;
-
-import com.squareup.okhttp.OkResponseCache;
-import com.squareup.okhttp.ResponseSource;
-import com.squareup.okhttp.internal.Base64;
-import com.squareup.okhttp.internal.DiskLruCache;
-import com.squareup.okhttp.internal.StrictLineReader;
-import com.squareup.okhttp.internal.Util;
-import java.io.BufferedWriter;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FilterInputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
-import java.net.CacheRequest;
-import java.net.CacheResponse;
-import java.net.HttpURLConnection;
-import java.net.ResponseCache;
-import java.net.SecureCacheResponse;
-import java.net.URI;
-import java.net.URLConnection;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.Principal;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLPeerUnverifiedException;
-
-import static com.squareup.okhttp.internal.Util.US_ASCII;
-import static com.squareup.okhttp.internal.Util.UTF_8;
-
-/**
- * Cache responses in a directory on the file system. Most clients should use
- * {@code android.net.HttpResponseCache}, the stable, documented front end for
- * this.
- */
-public final class HttpResponseCache extends ResponseCache implements OkResponseCache {
- private static final char[] DIGITS =
- { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
-
- // TODO: add APIs to iterate the cache?
- private static final int VERSION = 201105;
- private static final int ENTRY_METADATA = 0;
- private static final int ENTRY_BODY = 1;
- private static final int ENTRY_COUNT = 2;
-
- private final DiskLruCache cache;
-
- /* read and write statistics, all guarded by 'this' */
- private int writeSuccessCount;
- private int writeAbortCount;
- private int networkCount;
- private int hitCount;
- private int requestCount;
-
- public HttpResponseCache(File directory, long maxSize) throws IOException {
- cache = DiskLruCache.open(directory, VERSION, ENTRY_COUNT, maxSize);
- }
-
- private String uriToKey(URI uri) {
- try {
- MessageDigest messageDigest = MessageDigest.getInstance("MD5");
- byte[] md5bytes = messageDigest.digest(uri.toString().getBytes("UTF-8"));
- return bytesToHexString(md5bytes);
- } catch (NoSuchAlgorithmException e) {
- throw new AssertionError(e);
- } catch (UnsupportedEncodingException e) {
- throw new AssertionError(e);
- }
- }
-
- private static String bytesToHexString(byte[] bytes) {
- char[] digits = DIGITS;
- char[] buf = new char[bytes.length * 2];
- int c = 0;
- for (byte b : bytes) {
- buf[c++] = digits[(b >> 4) & 0xf];
- buf[c++] = digits[b & 0xf];
- }
- return new String(buf);
- }
-
- @Override public CacheResponse get(URI uri, String requestMethod,
- Map<String, List<String>> requestHeaders) {
- String key = uriToKey(uri);
- DiskLruCache.Snapshot snapshot;
- Entry entry;
- try {
- snapshot = cache.get(key);
- if (snapshot == null) {
- return null;
- }
- entry = new Entry(snapshot.getInputStream(ENTRY_METADATA));
- } catch (IOException e) {
- // Give up because the cache cannot be read.
- return null;
- }
-
- if (!entry.matches(uri, requestMethod, requestHeaders)) {
- snapshot.close();
- return null;
- }
-
- return entry.isHttps() ? new EntrySecureCacheResponse(entry, snapshot)
- : new EntryCacheResponse(entry, snapshot);
- }
-
- @Override public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
- if (!(urlConnection instanceof HttpURLConnection)) {
- return null;
- }
-
- HttpURLConnection httpConnection = (HttpURLConnection) urlConnection;
- String requestMethod = httpConnection.getRequestMethod();
- String key = uriToKey(uri);
-
- if (requestMethod.equals("POST") || requestMethod.equals("PUT") || requestMethod.equals(
- "DELETE")) {
- try {
- cache.remove(key);
- } catch (IOException ignored) {
- // The cache cannot be written.
- }
- return null;
- } else if (!requestMethod.equals("GET")) {
- // Don't cache non-GET responses. We're technically allowed to cache
- // HEAD requests and some POST requests, but the complexity of doing
- // so is high and the benefit is low.
- return null;
- }
-
- HttpEngine httpEngine = getHttpEngine(httpConnection);
- if (httpEngine == null) {
- // Don't cache unless the HTTP implementation is ours.
- return null;
- }
-
- ResponseHeaders response = httpEngine.getResponseHeaders();
- if (response.hasVaryAll()) {
- return null;
- }
-
- RawHeaders varyHeaders =
- httpEngine.getRequestHeaders().getHeaders().getAll(response.getVaryFields());
- Entry entry = new Entry(uri, varyHeaders, httpConnection);
- DiskLruCache.Editor editor = null;
- try {
- editor = cache.edit(key);
- if (editor == null) {
- return null;
- }
- entry.writeTo(editor);
- return new CacheRequestImpl(editor);
- } catch (IOException e) {
- abortQuietly(editor);
- return null;
- }
- }
-
- /**
- * Handles a conditional request hit by updating the stored cache response
- * with the headers from {@code httpConnection}. The cached response body is
- * not updated. If the stored response has changed since {@code
- * conditionalCacheHit} was returned, this does nothing.
- */
- @Override public void update(CacheResponse conditionalCacheHit, HttpURLConnection httpConnection)
- throws IOException {
- HttpEngine httpEngine = getHttpEngine(httpConnection);
- URI uri = httpEngine.getUri();
- ResponseHeaders response = httpEngine.getResponseHeaders();
- RawHeaders varyHeaders =
- httpEngine.getRequestHeaders().getHeaders().getAll(response.getVaryFields());
- Entry entry = new Entry(uri, varyHeaders, httpConnection);
- DiskLruCache.Snapshot snapshot = (conditionalCacheHit instanceof EntryCacheResponse)
- ? ((EntryCacheResponse) conditionalCacheHit).snapshot
- : ((EntrySecureCacheResponse) conditionalCacheHit).snapshot;
- DiskLruCache.Editor editor = null;
- try {
- editor = snapshot.edit(); // returns null if snapshot is not current
- if (editor != null) {
- entry.writeTo(editor);
- editor.commit();
- }
- } catch (IOException e) {
- abortQuietly(editor);
- }
- }
-
- private void abortQuietly(DiskLruCache.Editor editor) {
- // Give up because the cache cannot be written.
- try {
- if (editor != null) {
- editor.abort();
- }
- } catch (IOException ignored) {
- }
- }
-
- private HttpEngine getHttpEngine(URLConnection httpConnection) {
- if (httpConnection instanceof HttpURLConnectionImpl) {
- return ((HttpURLConnectionImpl) httpConnection).getHttpEngine();
- } else if (httpConnection instanceof HttpsURLConnectionImpl) {
- return ((HttpsURLConnectionImpl) httpConnection).getHttpEngine();
- } else {
- return null;
- }
- }
-
- public DiskLruCache getCache() {
- return cache;
- }
-
- public synchronized int getWriteAbortCount() {
- return writeAbortCount;
- }
-
- public synchronized int getWriteSuccessCount() {
- return writeSuccessCount;
- }
-
- public synchronized void trackResponse(ResponseSource source) {
- requestCount++;
-
- switch (source) {
- case CACHE:
- hitCount++;
- break;
- case CONDITIONAL_CACHE:
- case NETWORK:
- networkCount++;
- break;
- }
- }
-
- public synchronized void trackConditionalCacheHit() {
- hitCount++;
- }
-
- public synchronized int getNetworkCount() {
- return networkCount;
- }
-
- public synchronized int getHitCount() {
- return hitCount;
- }
-
- public synchronized int getRequestCount() {
- return requestCount;
- }
-
- private final class CacheRequestImpl extends CacheRequest {
- private final DiskLruCache.Editor editor;
- private OutputStream cacheOut;
- private boolean done;
- private OutputStream body;
-
- public CacheRequestImpl(final DiskLruCache.Editor editor) throws IOException {
- this.editor = editor;
- this.cacheOut = editor.newOutputStream(ENTRY_BODY);
- this.body = new FilterOutputStream(cacheOut) {
- @Override public void close() throws IOException {
- synchronized (HttpResponseCache.this) {
- if (done) {
- return;
- }
- done = true;
- writeSuccessCount++;
- }
- super.close();
- editor.commit();
- }
-
- @Override
- public void write(byte[] buffer, int offset, int length) throws IOException {
- // Since we don't override "write(int oneByte)", we can write directly to "out"
- // and avoid the inefficient implementation from the FilterOutputStream.
- out.write(buffer, offset, length);
- }
- };
- }
-
- @Override public void abort() {
- synchronized (HttpResponseCache.this) {
- if (done) {
- return;
- }
- done = true;
- writeAbortCount++;
- }
- Util.closeQuietly(cacheOut);
- try {
- editor.abort();
- } catch (IOException ignored) {
- }
- }
-
- @Override public OutputStream getBody() throws IOException {
- return body;
- }
- }
-
- private static final class Entry {
- private final String uri;
- private final RawHeaders varyHeaders;
- private final String requestMethod;
- private final RawHeaders responseHeaders;
- private final String cipherSuite;
- private final Certificate[] peerCertificates;
- private final Certificate[] localCertificates;
-
- /**
- * Reads an entry from an input stream. A typical entry looks like this:
- * <pre>{@code
- * http://google.com/foo
- * GET
- * 2
- * Accept-Language: fr-CA
- * Accept-Charset: UTF-8
- * HTTP/1.1 200 OK
- * 3
- * Content-Type: image/png
- * Content-Length: 100
- * Cache-Control: max-age=600
- * }</pre>
- *
- * <p>A typical HTTPS file looks like this:
- * <pre>{@code
- * https://google.com/foo
- * GET
- * 2
- * Accept-Language: fr-CA
- * Accept-Charset: UTF-8
- * HTTP/1.1 200 OK
- * 3
- * Content-Type: image/png
- * Content-Length: 100
- * Cache-Control: max-age=600
- *
- * AES_256_WITH_MD5
- * 2
- * base64-encoded peerCertificate[0]
- * base64-encoded peerCertificate[1]
- * -1
- * }</pre>
- * The file is newline separated. The first two lines are the URL and
- * the request method. Next is the number of HTTP Vary request header
- * lines, followed by those lines.
- *
- * <p>Next is the response status line, followed by the number of HTTP
- * response header lines, followed by those lines.
- *
- * <p>HTTPS responses also contain SSL session information. This begins
- * with a blank line, and then a line containing the cipher suite. Next
- * is the length of the peer certificate chain. These certificates are
- * base64-encoded and appear each on their own line. The next line
- * contains the length of the local certificate chain. These
- * certificates are also base64-encoded and appear each on their own
- * line. A length of -1 is used to encode a null array.
- */
- public Entry(InputStream in) throws IOException {
- try {
- StrictLineReader reader = new StrictLineReader(in, US_ASCII);
- uri = reader.readLine();
- requestMethod = reader.readLine();
- varyHeaders = new RawHeaders();
- int varyRequestHeaderLineCount = reader.readInt();
- for (int i = 0; i < varyRequestHeaderLineCount; i++) {
- varyHeaders.addLine(reader.readLine());
- }
-
- responseHeaders = new RawHeaders();
- responseHeaders.setStatusLine(reader.readLine());
- int responseHeaderLineCount = reader.readInt();
- for (int i = 0; i < responseHeaderLineCount; i++) {
- responseHeaders.addLine(reader.readLine());
- }
-
- if (isHttps()) {
- String blank = reader.readLine();
- if (!blank.isEmpty()) {
- throw new IOException("expected \"\" but was \"" + blank + "\"");
- }
- cipherSuite = reader.readLine();
- peerCertificates = readCertArray(reader);
- localCertificates = readCertArray(reader);
- } else {
- cipherSuite = null;
- peerCertificates = null;
- localCertificates = null;
- }
- } finally {
- in.close();
- }
- }
-
- public Entry(URI uri, RawHeaders varyHeaders, HttpURLConnection httpConnection)
- throws IOException {
- this.uri = uri.toString();
- this.varyHeaders = varyHeaders;
- this.requestMethod = httpConnection.getRequestMethod();
- this.responseHeaders = RawHeaders.fromMultimap(httpConnection.getHeaderFields(), true);
-
- if (isHttps()) {
- HttpsURLConnection httpsConnection = (HttpsURLConnection) httpConnection;
- cipherSuite = httpsConnection.getCipherSuite();
- Certificate[] peerCertificatesNonFinal = null;
- try {
- peerCertificatesNonFinal = httpsConnection.getServerCertificates();
- } catch (SSLPeerUnverifiedException ignored) {
- }
- peerCertificates = peerCertificatesNonFinal;
- localCertificates = httpsConnection.getLocalCertificates();
- } else {
- cipherSuite = null;
- peerCertificates = null;
- localCertificates = null;
- }
- }
-
- public void writeTo(DiskLruCache.Editor editor) throws IOException {
- OutputStream out = editor.newOutputStream(ENTRY_METADATA);
- Writer writer = new BufferedWriter(new OutputStreamWriter(out, UTF_8));
-
- writer.write(uri + '\n');
- writer.write(requestMethod + '\n');
- writer.write(Integer.toString(varyHeaders.length()) + '\n');
- for (int i = 0; i < varyHeaders.length(); i++) {
- writer.write(varyHeaders.getFieldName(i) + ": " + varyHeaders.getValue(i) + '\n');
- }
-
- writer.write(responseHeaders.getStatusLine() + '\n');
- writer.write(Integer.toString(responseHeaders.length()) + '\n');
- for (int i = 0; i < responseHeaders.length(); i++) {
- writer.write(responseHeaders.getFieldName(i) + ": " + responseHeaders.getValue(i) + '\n');
- }
-
- if (isHttps()) {
- writer.write('\n');
- writer.write(cipherSuite + '\n');
- writeCertArray(writer, peerCertificates);
- writeCertArray(writer, localCertificates);
- }
- writer.close();
- }
-
- private boolean isHttps() {
- return uri.startsWith("https://");
- }
-
- private Certificate[] readCertArray(StrictLineReader reader) throws IOException {
- int length = reader.readInt();
- if (length == -1) {
- return null;
- }
- try {
- CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
- Certificate[] result = new Certificate[length];
- for (int i = 0; i < result.length; i++) {
- String line = reader.readLine();
- byte[] bytes = Base64.decode(line.getBytes("US-ASCII"));
- result[i] = certificateFactory.generateCertificate(new ByteArrayInputStream(bytes));
- }
- return result;
- } catch (CertificateException e) {
- throw new IOException(e);
- }
- }
-
- private void writeCertArray(Writer writer, Certificate[] certificates) throws IOException {
- if (certificates == null) {
- writer.write("-1\n");
- return;
- }
- try {
- writer.write(Integer.toString(certificates.length) + '\n');
- for (Certificate certificate : certificates) {
- byte[] bytes = certificate.getEncoded();
- String line = Base64.encode(bytes);
- writer.write(line + '\n');
- }
- } catch (CertificateEncodingException e) {
- throw new IOException(e);
- }
- }
-
- public boolean matches(URI uri, String requestMethod,
- Map<String, List<String>> requestHeaders) {
- return this.uri.equals(uri.toString())
- && this.requestMethod.equals(requestMethod)
- && new ResponseHeaders(uri, responseHeaders).varyMatches(varyHeaders.toMultimap(false),
- requestHeaders);
- }
- }
-
- /**
- * Returns an input stream that reads the body of a snapshot, closing the
- * snapshot when the stream is closed.
- */
- private static InputStream newBodyInputStream(final DiskLruCache.Snapshot snapshot) {
- return new FilterInputStream(snapshot.getInputStream(ENTRY_BODY)) {
- @Override public void close() throws IOException {
- snapshot.close();
- super.close();
- }
- };
- }
-
- static class EntryCacheResponse extends CacheResponse {
- private final Entry entry;
- private final DiskLruCache.Snapshot snapshot;
- private final InputStream in;
-
- public EntryCacheResponse(Entry entry, DiskLruCache.Snapshot snapshot) {
- this.entry = entry;
- this.snapshot = snapshot;
- this.in = newBodyInputStream(snapshot);
- }
-
- @Override public Map<String, List<String>> getHeaders() {
- return entry.responseHeaders.toMultimap(true);
- }
-
- @Override public InputStream getBody() {
- return in;
- }
- }
-
- static class EntrySecureCacheResponse extends SecureCacheResponse {
- private final Entry entry;
- private final DiskLruCache.Snapshot snapshot;
- private final InputStream in;
-
- public EntrySecureCacheResponse(Entry entry, DiskLruCache.Snapshot snapshot) {
- this.entry = entry;
- this.snapshot = snapshot;
- this.in = newBodyInputStream(snapshot);
- }
-
- @Override public Map<String, List<String>> getHeaders() {
- return entry.responseHeaders.toMultimap(true);
- }
-
- @Override public InputStream getBody() {
- return in;
- }
-
- @Override public String getCipherSuite() {
- return entry.cipherSuite;
- }
-
- @Override public List<Certificate> getServerCertificateChain()
- throws SSLPeerUnverifiedException {
- if (entry.peerCertificates == null || entry.peerCertificates.length == 0) {
- throw new SSLPeerUnverifiedException(null);
- }
- return Arrays.asList(entry.peerCertificates.clone());
- }
-
- @Override public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
- if (entry.peerCertificates == null || entry.peerCertificates.length == 0) {
- throw new SSLPeerUnverifiedException(null);
- }
- return ((X509Certificate) entry.peerCertificates[0]).getSubjectX500Principal();
- }
-
- @Override public List<Certificate> getLocalCertificateChain() {
- if (entry.localCertificates == null || entry.localCertificates.length == 0) {
- return null;
- }
- return Arrays.asList(entry.localCertificates.clone());
- }
-
- @Override public Principal getLocalPrincipal() {
- if (entry.localCertificates == null || entry.localCertificates.length == 0) {
- return null;
- }
- return ((X509Certificate) entry.localCertificates[0]).getSubjectX500Principal();
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java b/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java
old mode 100644
new mode 100755
index f6d77b2..c967830
--- a/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java
@@ -78,18 +78,23 @@ public final class HttpTransport implements Transport {
}
// Stream a request body of a known length.
- int fixedContentLength = httpEngine.policy.getFixedContentLength();
+ long fixedContentLength = httpEngine.policy.getFixedContentLength();
if (fixedContentLength != -1) {
httpEngine.requestHeaders.setContentLength(fixedContentLength);
writeRequestHeaders();
return new FixedLengthOutputStream(requestOut, fixedContentLength);
}
+ long contentLength = httpEngine.requestHeaders.getContentLength();
+ if (contentLength > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("Use setFixedLengthStreamingMode() or "
+ + "setChunkedStreamingMode() for requests larger than 2 GiB.");
+ }
+
// Buffer a request body of a known length.
- int contentLength = httpEngine.requestHeaders.getContentLength();
if (contentLength != -1) {
writeRequestHeaders();
- return new RetryableOutputStream(contentLength);
+ return new RetryableOutputStream((int) contentLength);
}
// Buffer a request body of an unknown length. Don't write request
@@ -127,15 +132,18 @@ public final class HttpTransport implements Transport {
}
@Override public ResponseHeaders readResponseHeaders() throws IOException {
- RawHeaders headers = RawHeaders.fromBytes(socketIn);
- httpEngine.connection.setHttpMinorVersion(headers.getHttpMinorVersion());
- httpEngine.receiveHeaders(headers);
- return new ResponseHeaders(httpEngine.uri, headers);
+ RawHeaders rawHeaders = RawHeaders.fromBytes(socketIn);
+ httpEngine.connection.setHttpMinorVersion(rawHeaders.getHttpMinorVersion());
+ httpEngine.receiveHeaders(rawHeaders);
+
+ ResponseHeaders headers = new ResponseHeaders(httpEngine.uri, rawHeaders);
+ headers.setTransport("http/1.1");
+ return headers;
}
- public boolean makeReusable(boolean streamCancelled, OutputStream requestBodyOut,
+ public boolean makeReusable(boolean streamCanceled, OutputStream requestBodyOut,
InputStream responseBodyIn) {
- if (streamCancelled) {
+ if (streamCanceled) {
return false;
}
@@ -169,6 +177,10 @@ public final class HttpTransport implements Transport {
* Discards the response body so that the connection can be reused. This
* needs to be done judiciously, since it delays the current request in
* order to speed up a potential future request that may never occur.
+ *
+ * <p>A stream may be discarded to encourage response caching (a response
+ * cannot be cached unless it is consumed completely) or to enable connection
+ * reuse.
*/
private static boolean discardStream(HttpEngine httpEngine, InputStream responseBodyIn) {
Connection connection = httpEngine.connection;
@@ -212,9 +224,9 @@ public final class HttpTransport implements Transport {
/** An HTTP body with a fixed length known in advance. */
private static final class FixedLengthOutputStream extends AbstractOutputStream {
private final OutputStream socketOut;
- private int bytesRemaining;
+ private long bytesRemaining;
- private FixedLengthOutputStream(OutputStream socketOut, int bytesRemaining) {
+ private FixedLengthOutputStream(OutputStream socketOut, long bytesRemaining) {
this.socketOut = socketOut;
this.bytesRemaining = bytesRemaining;
}
@@ -358,14 +370,14 @@ public final class HttpTransport implements Transport {
/** An HTTP body with a fixed length specified in advance. */
private static class FixedLengthInputStream extends AbstractHttpInputStream {
- private int bytesRemaining;
+ private long bytesRemaining;
public FixedLengthInputStream(InputStream is, CacheRequest cacheRequest, HttpEngine httpEngine,
- int length) throws IOException {
+ long length) throws IOException {
super(is, httpEngine, cacheRequest);
bytesRemaining = length;
if (bytesRemaining == 0) {
- endOfInput(false);
+ endOfInput();
}
}
@@ -375,7 +387,7 @@ public final class HttpTransport implements Transport {
if (bytesRemaining == 0) {
return -1;
}
- int read = in.read(buffer, offset, Math.min(count, bytesRemaining));
+ int read = in.read(buffer, offset, (int) Math.min(count, bytesRemaining));
if (read == -1) {
unexpectedEndOfInput(); // the server didn't supply the promised content length
throw new ProtocolException("unexpected end of stream");
@@ -383,14 +395,14 @@ public final class HttpTransport implements Transport {
bytesRemaining -= read;
cacheWrite(buffer, offset, read);
if (bytesRemaining == 0) {
- endOfInput(false);
+ endOfInput();
}
return read;
}
@Override public int available() throws IOException {
checkNotClosed();
- return bytesRemaining == 0 ? 0 : Math.min(in.available(), bytesRemaining);
+ return bytesRemaining == 0 ? 0 : (int) Math.min(in.available(), bytesRemaining);
}
@Override public void close() throws IOException {
@@ -460,7 +472,7 @@ public final class HttpTransport implements Transport {
RawHeaders rawResponseHeaders = httpEngine.responseHeaders.getHeaders();
RawHeaders.readHeaders(transport.socketIn, rawResponseHeaders);
httpEngine.receiveHeaders(rawResponseHeaders);
- endOfInput(false);
+ endOfInput();
}
}
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/e16cab6b/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java b/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
old mode 100644
new mode 100755
index eabe649..fb4a704
--- a/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
@@ -18,33 +18,28 @@
package com.squareup.okhttp.internal.http;
import com.squareup.okhttp.Connection;
-import com.squareup.okhttp.ConnectionPool;
import com.squareup.okhttp.OkHttpClient;
-import com.squareup.okhttp.Route;
-import com.squareup.okhttp.internal.AbstractOutputStream;
-import com.squareup.okhttp.internal.FaultRecoveringOutputStream;
+import com.squareup.okhttp.internal.Platform;
import com.squareup.okhttp.internal.Util;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.CookieHandler;
import java.net.HttpRetryException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.ProtocolException;
import java.net.Proxy;
-import java.net.ProxySelector;
import java.net.SocketPermission;
import java.net.URL;
import java.security.Permission;
import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import javax.net.ssl.HostnameVerifier;
+import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLSocketFactory;
import static com.squareup.okhttp.internal.Util.getEffectivePort;
@@ -62,10 +57,10 @@ import static com.squareup.okhttp.internal.Util.getEffectivePort;
* connection} field on this class for null/non-null to determine of an instance
* is currently connected to a server.
*/
-public class HttpURLConnectionImpl extends HttpURLConnection {
+public class HttpURLConnectionImpl extends HttpURLConnection implements Policy {
/** Numeric status code, 307: Temporary Redirect. */
- static final int HTTP_TEMP_REDIRECT = 307;
+ public static final int HTTP_TEMP_REDIRECT = 307;
/**
* How many redirects should we follow? Chrome follows 21; Firefox, curl,
@@ -73,51 +68,19 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
*/
private static final int MAX_REDIRECTS = 20;
- /**
- * The minimum number of request body bytes to transmit before we're willing
- * to let a routine {@link IOException} bubble up to the user. This is used to
- * size a buffer for data that will be replayed upon error.
- */
- private static final int MAX_REPLAY_BUFFER_LENGTH = 8192;
-
- private final boolean followProtocolRedirects;
-
- /** The proxy requested by the client, or null for a proxy to be selected automatically. */
- final Proxy requestedProxy;
-
- final ProxySelector proxySelector;
- final CookieHandler cookieHandler;
- final OkResponseCache responseCache;
- final ConnectionPool connectionPool;
- /* SSL configuration; necessary for HTTP requests that get redirected to HTTPS. */
- SSLSocketFactory sslSocketFactory;
- HostnameVerifier hostnameVerifier;
- final Set<Route> failedRoutes;
+ final OkHttpClient client;
private final RawHeaders rawRequestHeaders = new RawHeaders();
-
+ /** Like the superclass field of the same name, but a long and available on all platforms. */
+ private long fixedContentLength = -1;
private int redirectionCount;
- private FaultRecoveringOutputStream faultRecoveringRequestBody;
-
protected IOException httpEngineFailure;
protected HttpEngine httpEngine;
+ private Proxy selectedProxy;
- public HttpURLConnectionImpl(URL url, OkHttpClient client, OkResponseCache responseCache,
- Set<Route> failedRoutes) {
+ public HttpURLConnectionImpl(URL url, OkHttpClient client) {
super(url);
- this.followProtocolRedirects = client.getFollowProtocolRedirects();
- this.failedRoutes = failedRoutes;
- this.requestedProxy = client.getProxy();
- this.proxySelector = client.getProxySelector();
- this.cookieHandler = client.getCookieHandler();
- this.connectionPool = client.getConnectionPool();
- this.sslSocketFactory = client.getSslSocketFactory();
- this.hostnameVerifier = client.getHostnameVerifier();
- this.responseCache = responseCache;
- }
-
- Set<Route> getFailedRoutes() {
- return failedRoutes;
+ this.client = client;
}
@Override public final void connect() throws IOException {
@@ -197,7 +160,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
try {
return getResponse().getResponseHeaders().getHeaders().toMultimap(true);
} catch (IOException e) {
- return null;
+ return Collections.emptyMap();
}
}
@@ -241,29 +204,14 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
throw new ProtocolException("cannot write request body after response has been read");
}
- if (faultRecoveringRequestBody == null) {
- faultRecoveringRequestBody = new FaultRecoveringOutputStream(MAX_REPLAY_BUFFER_LENGTH, out) {
- @Override protected OutputStream replacementStream(IOException e) throws IOException {
- if (httpEngine.getRequestBody() instanceof AbstractOutputStream
- && ((AbstractOutputStream) httpEngine.getRequestBody()).isClosed()) {
- return null; // Don't recover once the underlying stream has been closed.
- }
- if (handleFailure(e)) {
- return httpEngine.getRequestBody();
- }
- return null; // This is a permanent failure.
- }
- };
- }
-
- return faultRecoveringRequestBody;
+ return out;
}
@Override public final Permission getPermission() throws IOException {
String hostName = getURL().getHost();
int hostPort = Util.getEffectivePort(getURL());
if (usingProxy()) {
- InetSocketAddress proxyAddress = (InetSocketAddress) requestedProxy.address();
+ InetSocketAddress proxyAddress = (InetSocketAddress) client.getProxy().address();
hostName = proxyAddress.getHostName();
hostPort = proxyAddress.getPort();
}
@@ -277,6 +225,22 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
return rawRequestHeaders.get(field);
}
+ @Override public void setConnectTimeout(int timeoutMillis) {
+ client.setConnectTimeout(timeoutMillis, TimeUnit.MILLISECONDS);
+ }
+
+ @Override public int getConnectTimeout() {
+ return client.getConnectTimeout();
+ }
+
+ @Override public void setReadTimeout(int timeoutMillis) {
+ client.setReadTimeout(timeoutMillis, TimeUnit.MILLISECONDS);
+ }
+
+ @Override public int getReadTimeout() {
+ return client.getReadTimeout();
+ }
+
private void initHttpEngine() throws IOException {
if (httpEngineFailure != null) {
throw httpEngineFailure;
@@ -290,8 +254,8 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
if (method.equals("GET")) {
// they are requesting a stream to write to. This implies a POST method
method = "POST";
- } else if (!method.equals("POST") && !method.equals("PUT")) {
- // If the request method is neither POST nor PUT, then you're not writing
+ } else if (!method.equals("POST") && !method.equals("PUT") && !method.equals("PATCH")) {
+ // If the request method is neither POST nor PUT nor PATCH, then you're not writing
throw new ProtocolException(method + " does not support writing");
}
}
@@ -302,17 +266,16 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
}
}
- protected HttpURLConnection getHttpConnectionToCache() {
+ @Override public HttpURLConnection getHttpConnectionToCache() {
return this;
}
private HttpEngine newHttpEngine(String method, RawHeaders requestHeaders,
Connection connection, RetryableOutputStream requestBody) throws IOException {
if (url.getProtocol().equals("http")) {
- return new HttpEngine(this, method, requestHeaders, connection, requestBody);
+ return new HttpEngine(client, this, method, requestHeaders, connection, requestBody);
} else if (url.getProtocol().equals("https")) {
- return new HttpsURLConnectionImpl.HttpsEngine(
- this, method, requestHeaders, connection, requestBody);
+ return new HttpsEngine(client, this, method, requestHeaders, connection, requestBody);
} else {
throw new AssertionError();
}
@@ -348,7 +311,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
// Although RFC 2616 10.3.2 specifies that a HTTP_MOVED_PERM
// redirect should keep the same method, Chrome, Firefox and the
// RI all issue GETs when following any redirect.
- int responseCode = getResponseCode();
+ int responseCode = httpEngine.getResponseCode();
if (responseCode == HTTP_MULT_CHOICE
|| responseCode == HTTP_MOVED_PERM
|| responseCode == HTTP_MOVED_TEMP
@@ -358,8 +321,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
}
if (requestBody != null && !(requestBody instanceof RetryableOutputStream)) {
- throw new HttpRetryException("Cannot retry streamed HTTP body",
- httpEngine.getResponseCode());
+ throw new HttpRetryException("Cannot retry streamed HTTP body", responseCode);
}
if (retry == Retry.DIFFERENT_CONNECTION) {
@@ -370,6 +332,11 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
httpEngine = newHttpEngine(retryMethod, rawRequestHeaders, httpEngine.getConnection(),
(RetryableOutputStream) requestBody);
+
+ if (requestBody == null) {
+ // Drop the Content-Length header when redirected from POST to GET.
+ httpEngine.getRequestHeaders().removeContentLength();
+ }
}
}
@@ -384,6 +351,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
if (readResponse) {
httpEngine.readResponse();
}
+
return true;
} catch (IOException e) {
if (handleFailure(e)) {
@@ -407,8 +375,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
OutputStream requestBody = httpEngine.getRequestBody();
boolean canRetryRequestBody = requestBody == null
- || requestBody instanceof RetryableOutputStream
- || (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable());
+ || requestBody instanceof RetryableOutputStream;
if (routeSelector == null && httpEngine.connection == null // No connection.
|| routeSelector != null && !routeSelector.hasNext() // No more routes to attempt.
|| !isRecoverable(e)
@@ -418,15 +385,9 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
}
httpEngine.release(true);
- RetryableOutputStream retryableOutputStream = requestBody instanceof RetryableOutputStream
- ? (RetryableOutputStream) requestBody
- : null;
+ RetryableOutputStream retryableOutputStream = (RetryableOutputStream) requestBody;
httpEngine = newHttpEngine(method, rawRequestHeaders, null, retryableOutputStream);
httpEngine.routeSelector = routeSelector; // Keep the same routeSelector.
- if (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable()) {
- httpEngine.sendRequest();
- faultRecoveringRequestBody.replaceStream(httpEngine.getRequestBody());
- }
return true;
}
@@ -451,13 +412,13 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
/**
* Returns the retry action to take for the current response headers. The
- * headers, proxy and target URL or this connection may be adjusted to
+ * headers, proxy and target URL for this connection may be adjusted to
* prepare for a follow up request.
*/
private Retry processResponseHeaders() throws IOException {
Proxy selectedProxy = httpEngine.connection != null
? httpEngine.connection.getRoute().getProxy()
- : requestedProxy;
+ : client.getProxy();
final int responseCode = getResponseCode();
switch (responseCode) {
case HTTP_PROXY_AUTH:
@@ -466,8 +427,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(client.getAuthenticator(),
+ getResponseCode(), httpEngine.getResponseHeaders().getHeaders(), rawRequestHeaders,
+ selectedProxy, url);
return credentialsFound ? Retry.SAME_CONNECTION : Retry.NONE;
case HTTP_MULT_CHOICE:
@@ -496,7 +458,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
return Retry.NONE; // Don't follow redirects to unsupported protocols.
}
boolean sameProtocol = previousUrl.getProtocol().equals(url.getProtocol());
- if (!sameProtocol && !followProtocolRedirects) {
+ if (!sameProtocol && !client.getFollowProtocolRedirects()) {
return Retry.NONE; // This client doesn't follow redirects across protocols.
}
boolean sameHost = previousUrl.getHost().equals(url.getHost());
@@ -513,17 +475,29 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
}
/** @see java.net.HttpURLConnection#setFixedLengthStreamingMode(int) */
- final int getFixedContentLength() {
+ @Override public final long getFixedContentLength() {
return fixedContentLength;
}
- /** @see java.net.HttpURLConnection#setChunkedStreamingMode(int) */
- final int getChunkLength() {
+ @Override public final int getChunkLength() {
return chunkLength;
}
@Override public final boolean usingProxy() {
- return (requestedProxy != null && requestedProxy.type() != Proxy.Type.DIRECT);
+ if (selectedProxy != null) {
+ return isValidNonDirectProxy(selectedProxy);
+ }
+
+ // This behavior is a bit odd (but is probably justified by the
+ // oddness of the APIs involved). Before a connection is established,
+ // this method will return true only if this connection was explicitly
+ // opened with a Proxy. We don't attempt to query the ProxySelector
+ // at all.
+ return isValidNonDirectProxy(client.getProxy());
+ }
+
+ private static boolean isValidNonDirectProxy(Proxy proxy) {
+ return proxy != null && proxy.type() != Proxy.Type.DIRECT;
}
@Override public String getResponseMessage() throws IOException {
@@ -541,7 +515,21 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
if (field == null) {
throw new NullPointerException("field == null");
}
- rawRequestHeaders.set(field, newValue);
+ if (newValue == null) {
+ // Silently ignore null header values for backwards compatibility with older
+ // android versions as well as with other URLConnection implementations.
+ //
+ // Some implementations send a malformed HTTP header when faced with
+ // such requests, we respect the spec and ignore the header.
+ Platform.get().logW("Ignoring header " + field + " because its value was null.");
+ return;
+ }
+
+ if ("X-Android-Transports".equals(field)) {
+ setTransports(newValue, false /* append */);
+ } else {
+ rawRequestHeaders.set(field, newValue);
+ }
}
@Override public final void addRequestProperty(String field, String value) {
@@ -551,6 +539,52 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
if (field == null) {
throw new NullPointerException("field == null");
}
- rawRequestHeaders.add(field, value);
+ if (value == null) {
+ // Silently ignore null header values for backwards compatibility with older
+ // android versions as well as with other URLConnection implementations.
+ //
+ // Some implementations send a malformed HTTP header when faced with
+ // such requests, we respect the spec and ignore the header.
+ Platform.get().logW("Ignoring header " + field + " because its value was null.");
+ return;
+ }
+
+ if ("X-Android-Transports".equals(field)) {
+ setTransports(value, true /* append */);
+ } else {
+ rawRequestHeaders.add(field, value);
+ }
+ }
+
+ /*
+ * Splits and validates a comma-separated string of transports.
+ * When append == false, we require that the transport list contains "http/1.1".
+ */
+ private void setTransports(String transportsString, boolean append) {
+ List<String> transportsList = new ArrayList<String>();
+ if (append) {
+ transportsList.addAll(client.getTransports());
+ }
+ for (String transport : transportsString.split(",", -1)) {
+ transportsList.add(transport);
+ }
+ client.setTransports(transportsList);
+ }
+
+ @Override public void setFixedLengthStreamingMode(int contentLength) {
+ setFixedLengthStreamingMode((long) contentLength);
+ }
+
+ // @Override Don't override: this overload method doesn't exist prior to Java 1.7.
+ public void setFixedLengthStreamingMode(long contentLength) {
+ if (super.connected) throw new IllegalStateException("Already connected");
+ if (chunkLength > 0) throw new IllegalStateException("Already in chunked mode");
+ if (contentLength < 0) throw new IllegalArgumentException("contentLength < 0");
+ this.fixedContentLength = contentLength;
+ super.fixedContentLength = (int) Math.min(contentLength, Integer.MAX_VALUE);
+ }
+
+ @Override public final void setSelectedProxy(Proxy proxy) {
+ this.selectedProxy = proxy;
}
}