cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From i..@apache.org
Subject [6/7] [CB-2431] Update to okhttp to include jwilson's recovery fixes [f38fec5b]
Date Mon, 29 Apr 2013 13:39:55 GMT
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/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
index 93f1754..3ddc693 100644
--- a/framework/src/com/squareup/okhttp/internal/StrictLineReader.java
+++ b/framework/src/com/squareup/okhttp/internal/StrictLineReader.java
@@ -21,26 +21,23 @@ import java.io.Closeable;
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
 import java.nio.charset.Charset;
 
-import static com.squareup.okhttp.internal.Util.ISO_8859_1;
-import static com.squareup.okhttp.internal.Util.US_ASCII;
-import static com.squareup.okhttp.internal.Util.UTF_8;
-
 /**
  * Buffers input from an {@link InputStream} for reading lines.
  *
- * This class is used for buffered reading of lines. For purposes of this class, a line ends with
+ * <p>This class is used for buffered reading of lines. For purposes of this class, a line ends with
  * "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated line at
  * end of input is invalid and will be ignored, the caller may use {@code hasUnterminatedLine()}
  * to detect it after catching the {@code EOFException}.
  *
- * This class is intended for reading input that strictly consists of lines, such as line-based
- * cache entries or cache journal. Unlike the {@link BufferedReader} which in conjunction with
- * {@link InputStreamReader} provides similar functionality, this class uses different
+ * <p>This class is intended for reading input that strictly consists of lines, such as line-based
+ * cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
+ * with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
  * end-of-input reporting and a more restrictive definition of a line.
  *
- * This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
+ * <p>This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
  * and 10, respectively, and the representation of no other character contains these values.
  * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
  * The default charset is US_ASCII.
@@ -52,42 +49,22 @@ public class StrictLineReader implements Closeable {
   private final InputStream in;
   private final Charset charset;
 
-  // Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
-  // and the data in the range [pos, end) is buffered for reading. At end of input, if there is
-  // an unterminated line, we set end == -1, otherwise end == pos. If the underlying
-  // {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
+  /*
+   * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
+   * and the data in the range [pos, end) is buffered for reading. At end of input, if there is
+   * an unterminated line, we set end == -1, otherwise end == pos. If the underlying
+   * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
+   */
   private byte[] buf;
   private int pos;
   private int end;
 
   /**
-   * Constructs a new {@code StrictLineReader} with the default capacity and charset.
-   *
-   * @param in the {@code InputStream} to read data from.
-   * @throws NullPointerException if {@code in} is null.
-   */
-  public StrictLineReader(InputStream in) {
-    this(in, 8192);
-  }
-
-  /**
-   * Constructs a new {@code LineReader} with the specified capacity and the default charset.
-   *
-   * @param in the {@code InputStream} to read data from.
-   * @param capacity the capacity of the buffer.
-   * @throws NullPointerException if {@code in} is null.
-   * @throws IllegalArgumentException for negative or zero {@code capacity}.
-   */
-  public StrictLineReader(InputStream in, int capacity) {
-    this(in, capacity, US_ASCII);
-  }
-
-  /**
    * Constructs a new {@code LineReader} with the specified charset and the default capacity.
    *
    * @param in the {@code InputStream} to read data from.
-   * @param charset the charset used to decode data.
-   * Only US-ASCII, UTF-8 and ISO-8859-1 is supported.
+   * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
+   *     supported.
    * @throws NullPointerException if {@code in} or {@code charset} is null.
    * @throws IllegalArgumentException if the specified charset is not supported.
    */
@@ -100,11 +77,11 @@ public class StrictLineReader implements Closeable {
    *
    * @param in the {@code InputStream} to read data from.
    * @param capacity the capacity of the buffer.
-   * @param charset the charset used to decode data.
-   * Only US-ASCII, UTF-8 and ISO-8859-1 is supported.
+   * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
+   *     supported.
    * @throws NullPointerException if {@code in} or {@code charset} is null.
    * @throws IllegalArgumentException if {@code capacity} is negative or zero
-   * or the specified charset is not supported.
+   *     or the specified charset is not supported.
    */
   public StrictLineReader(InputStream in, int capacity, Charset charset) {
     if (in == null || charset == null) {
@@ -113,7 +90,7 @@ public class StrictLineReader implements Closeable {
     if (capacity < 0) {
       throw new IllegalArgumentException("capacity <= 0");
     }
-    if (!(charset.equals(US_ASCII) || charset.equals(UTF_8) || charset.equals(ISO_8859_1))) {
+    if (!(charset.equals(Util.US_ASCII))) {
       throw new IllegalArgumentException("Unsupported encoding");
     }
 
@@ -128,7 +105,6 @@ public class StrictLineReader implements Closeable {
    *
    * @throws IOException for errors when closing the underlying {@code InputStream}.
    */
-  @Override
   public void close() throws IOException {
     synchronized (in) {
       if (buf != null) {
@@ -162,7 +138,7 @@ public class StrictLineReader implements Closeable {
       for (int i = pos; i != end; ++i) {
         if (buf[i] == LF) {
           int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
-          String res = new String(buf, pos, lineEnd - pos, charset);
+          String res = new String(buf, pos, lineEnd - pos, charset.name());
           pos = i + 1;
           return res;
         }
@@ -173,7 +149,11 @@ public class StrictLineReader implements Closeable {
         @Override
         public String toString() {
           int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
-          return new String(buf, 0, length, charset);
+          try {
+            return new String(buf, 0, length, charset.name());
+          } catch (UnsupportedEncodingException e) {
+            throw new AssertionError(e); // Since we control the charset this will never happen.
+          }
         }
       };
 
@@ -215,9 +195,6 @@ public class StrictLineReader implements Closeable {
   /**
    * Reads new input data into the buffer. Call only with pos == end or end == -1,
    * depending on the desired outcome if the function throws.
-   *
-   * @throws IOException for underlying {@code InputStream} errors.
-   * @throws EOFException for the end of source stream.
    */
   private void fillBuf() throws IOException {
     int result = in.read(buf, 0, buf.length);

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/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
index dc914cc..290e5ea 100644
--- a/framework/src/com/squareup/okhttp/internal/Util.java
+++ b/framework/src/com/squareup/okhttp/internal/Util.java
@@ -149,12 +149,14 @@ public final class Util {
     throw new AssertionError(thrown);
   }
 
-  /** Recursively delete everything in {@code dir}. */
-  // TODO: this should specify paths as Strings rather than as Files
+  /**
+   * Deletes the contents of {@code dir}. Throws an IOException if any file
+   * could not be deleted, or if {@code dir} is not a readable directory.
+   */
   public static void deleteContents(File dir) throws IOException {
     File[] files = dir.listFiles();
     if (files == null) {
-      throw new IllegalArgumentException("not a directory: " + dir);
+      throw new IOException("not a readable directory: " + dir);
     }
     for (File file : files) {
       if (file.isDirectory()) {

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/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
index 9caeb19..7a06dca 100644
--- a/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpEngine.java
@@ -19,7 +19,6 @@ package com.squareup.okhttp.internal.http;
 
 import com.squareup.okhttp.Address;
 import com.squareup.okhttp.Connection;
-import com.squareup.okhttp.OkResponseCache;
 import com.squareup.okhttp.ResponseSource;
 import com.squareup.okhttp.TunnelRequest;
 import com.squareup.okhttp.internal.Dns;
@@ -154,7 +153,7 @@ public class HttpEngine {
     try {
       uri = Platform.get().toUriLenient(policy.getURL());
     } catch (URISyntaxException e) {
-      throw new IOException(e);
+      throw new IOException(e.getMessage());
     }
 
     this.requestHeaders = new RequestHeaders(uri, new RawHeaders(requestHeaders));
@@ -176,8 +175,8 @@ public class HttpEngine {
 
     prepareRawRequestHeaders();
     initResponseSource();
-    if (policy.responseCache instanceof OkResponseCache) {
-      ((OkResponseCache) policy.responseCache).trackResponse(responseSource);
+    if (policy.responseCache != null) {
+      policy.responseCache.trackResponse(responseSource);
     }
 
     // The raw response source may require the network, but the request
@@ -198,6 +197,7 @@ public class HttpEngine {
       sendSocketRequest();
     } else if (connection != null) {
       policy.connectionPool.recycle(connection);
+      policy.getFailedRoutes().remove(connection.getRoute());
       connection = null;
     }
   }
@@ -279,16 +279,17 @@ public class HttpEngine {
       }
       Address address = new Address(uriHost, getEffectivePort(uri), sslSocketFactory,
           hostnameVerifier, policy.requestedProxy);
-      routeSelector =
-          new RouteSelector(address, uri, policy.proxySelector, policy.connectionPool, Dns.DEFAULT);
+      routeSelector = new RouteSelector(address, uri, policy.proxySelector, policy.connectionPool,
+          Dns.DEFAULT, policy.getFailedRoutes());
     }
     connection = routeSelector.next();
     if (!connection.isConnected()) {
       connection.connect(policy.getConnectTimeout(), policy.getReadTimeout(), getTunnelConfig());
       policy.connectionPool.maybeShare(connection);
+      policy.getFailedRoutes().remove(connection.getRoute());
     }
     connected(connection);
-    if (connection.getProxy() != policy.requestedProxy) {
+    if (connection.getRoute().getProxy() != policy.requestedProxy) {
       // Update the request line if the proxy changed; it may need a host name.
       requestHeaders.getHeaders().setRequestLine(getRequestLine());
     }
@@ -574,7 +575,7 @@ public class HttpEngine {
   protected boolean includeAuthorityInRequestLine() {
     return connection == null
         ? policy.usingProxy() // A proxy was requested.
-        : connection.getProxy().type() == Proxy.Type.HTTP; // A proxy was selected.
+        : connection.getRoute().getProxy().type() == Proxy.Type.HTTP; // A proxy was selected.
   }
 
   public static String getDefaultUserAgent() {
@@ -635,11 +636,8 @@ public class HttpEngine {
         release(false);
         ResponseHeaders combinedHeaders = cachedResponseHeaders.combine(responseHeaders);
         setResponse(combinedHeaders, cachedResponseBody);
-        if (policy.responseCache instanceof OkResponseCache) {
-          OkResponseCache httpResponseCache = (OkResponseCache) policy.responseCache;
-          httpResponseCache.trackConditionalCacheHit();
-          httpResponseCache.update(cacheResponse, policy.getHttpConnectionToCache());
-        }
+        policy.responseCache.trackConditionalCacheHit();
+        policy.responseCache.update(cacheResponse, policy.getHttpConnectionToCache());
         return;
       } else {
         Util.closeQuietly(cachedResponseBody);

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/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
index dd7a38d..f6d77b2 100644
--- a/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpTransport.java
@@ -17,8 +17,8 @@
 package com.squareup.okhttp.internal.http;
 
 import com.squareup.okhttp.Connection;
+import com.squareup.okhttp.internal.AbstractOutputStream;
 import com.squareup.okhttp.internal.Util;
-import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -31,14 +31,6 @@ import static com.squareup.okhttp.internal.Util.checkOffsetAndCount;
 
 public final class HttpTransport implements Transport {
   /**
-   * The maximum number of bytes to buffer when sending headers and a request
-   * body. When the headers and body can be sent in a single write, the
-   * request completes sooner. In one WiFi benchmark, using a large enough
-   * buffer sped up some uploads by half.
-   */
-  private static final int MAX_REQUEST_BUFFER_LENGTH = 32768;
-
-  /**
    * The timeout to use while discarding a stream of input data. Since this is
    * used for connection reuse, this timeout should be significantly less than
    * the time it takes to establish a new connection.
@@ -129,14 +121,8 @@ public final class HttpTransport implements Transport {
    */
   public void writeRequestHeaders() throws IOException {
     httpEngine.writingRequestHeaders();
-    int contentLength = httpEngine.requestHeaders.getContentLength();
     RawHeaders headersToSend = httpEngine.requestHeaders.getHeaders();
     byte[] bytes = headersToSend.toBytes();
-
-    if (contentLength != -1 && bytes.length + contentLength <= MAX_REQUEST_BUFFER_LENGTH) {
-      requestOut = new BufferedOutputStream(socketOut, bytes.length + contentLength);
-    }
-
     requestOut.write(bytes);
   }
 
@@ -154,7 +140,7 @@ public final class HttpTransport implements Transport {
     }
 
     // We cannot reuse sockets that have incomplete output.
-    if (requestBodyOut != null && !((AbstractHttpOutputStream) requestBodyOut).closed) {
+    if (requestBodyOut != null && !((AbstractOutputStream) requestBodyOut).isClosed()) {
       return false;
     }
 
@@ -224,7 +210,7 @@ public final class HttpTransport implements Transport {
   }
 
   /** An HTTP body with a fixed length known in advance. */
-  private static final class FixedLengthOutputStream extends AbstractHttpOutputStream {
+  private static final class FixedLengthOutputStream extends AbstractOutputStream {
     private final OutputStream socketOut;
     private int bytesRemaining;
 
@@ -266,7 +252,7 @@ public final class HttpTransport implements Transport {
    * buffered until {@code maxChunkLength} bytes are ready, at which point the
    * chunk is written and the buffer is cleared.
    */
-  private static final class ChunkedOutputStream extends AbstractHttpOutputStream {
+  private static final class ChunkedOutputStream extends AbstractOutputStream {
     private static final byte[] CRLF = { '\r', '\n' };
     private static final byte[] HEX_DIGITS = {
         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/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
index 25e8c3c..eabe649 100644
--- a/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
@@ -20,6 +20,9 @@ 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.Util;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -32,13 +35,13 @@ import java.net.InetSocketAddress;
 import java.net.ProtocolException;
 import java.net.Proxy;
 import java.net.ProxySelector;
-import java.net.ResponseCache;
 import java.net.SocketPermission;
 import java.net.URL;
 import java.security.Permission;
 import java.security.cert.CertificateException;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSocketFactory;
@@ -70,6 +73,13 @@ 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. */
@@ -77,29 +87,37 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
 
   final ProxySelector proxySelector;
   final CookieHandler cookieHandler;
-  final ResponseCache responseCache;
+  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;
 
   private final RawHeaders rawRequestHeaders = new RawHeaders();
 
   private int redirectionCount;
+  private FaultRecoveringOutputStream faultRecoveringRequestBody;
 
   protected IOException httpEngineFailure;
   protected HttpEngine httpEngine;
 
-  public HttpURLConnectionImpl(URL url, OkHttpClient client) {
+  public HttpURLConnectionImpl(URL url, OkHttpClient client, OkResponseCache responseCache,
+      Set<Route> failedRoutes) {
     super(url);
     this.followProtocolRedirects = client.getFollowProtocolRedirects();
+    this.failedRoutes = failedRoutes;
     this.requestedProxy = client.getProxy();
     this.proxySelector = client.getProxySelector();
     this.cookieHandler = client.getCookieHandler();
-    this.responseCache = client.getResponseCache();
     this.connectionPool = client.getConnectionPool();
     this.sslSocketFactory = client.getSslSocketFactory();
     this.hostnameVerifier = client.getHostnameVerifier();
+    this.responseCache = responseCache;
+  }
+
+  Set<Route> getFailedRoutes() {
+    return failedRoutes;
   }
 
   @Override public final void connect() throws IOException {
@@ -216,14 +234,29 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
   @Override public final OutputStream getOutputStream() throws IOException {
     connect();
 
-    OutputStream result = httpEngine.getRequestBody();
-    if (result == null) {
+    OutputStream out = httpEngine.getRequestBody();
+    if (out == null) {
       throw new ProtocolException("method does not support a request body: " + method);
     } else if (httpEngine.hasResponse()) {
       throw new ProtocolException("cannot write request body after response has been read");
     }
 
-    return result;
+    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;
   }
 
   @Override public final Permission getPermission() throws IOException {
@@ -353,27 +386,48 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
       }
       return true;
     } catch (IOException e) {
-      RouteSelector routeSelector = httpEngine.routeSelector;
-      if (routeSelector != null && httpEngine.connection != null) {
-        routeSelector.connectFailed(httpEngine.connection, e);
-      }
-      if (routeSelector == null && httpEngine.connection == null) {
-        throw e; // If we failed before finding a route or a connection, give up.
-      }
-
-      // The connection failure isn't fatal if there's another route to attempt.
-      OutputStream requestBody = httpEngine.getRequestBody();
-      if ((routeSelector == null || routeSelector.hasNext()) && isRecoverable(e) && (requestBody
-          == null || requestBody instanceof RetryableOutputStream)) {
-        httpEngine.release(true);
-        httpEngine =
-            newHttpEngine(method, rawRequestHeaders, null, (RetryableOutputStream) requestBody);
-        httpEngine.routeSelector = routeSelector; // Keep the same routeSelector.
+      if (handleFailure(e)) {
         return false;
+      } else {
+        throw e;
       }
+    }
+  }
+
+  /**
+   * Report and attempt to recover from {@code e}. Returns true if the HTTP
+   * engine was replaced and the request should be retried. Otherwise the
+   * failure is permanent.
+   */
+  private boolean handleFailure(IOException e) throws IOException {
+    RouteSelector routeSelector = httpEngine.routeSelector;
+    if (routeSelector != null && httpEngine.connection != null) {
+      routeSelector.connectFailed(httpEngine.connection, e);
+    }
+
+    OutputStream requestBody = httpEngine.getRequestBody();
+    boolean canRetryRequestBody = requestBody == null
+        || requestBody instanceof RetryableOutputStream
+        || (faultRecoveringRequestBody != null && faultRecoveringRequestBody.isRecoverable());
+    if (routeSelector == null && httpEngine.connection == null // No connection.
+        || routeSelector != null && !routeSelector.hasNext() // No more routes to attempt.
+        || !isRecoverable(e)
+        || !canRetryRequestBody) {
       httpEngineFailure = e;
-      throw e;
+      return false;
+    }
+
+    httpEngine.release(true);
+    RetryableOutputStream retryableOutputStream = requestBody instanceof RetryableOutputStream
+        ? (RetryableOutputStream) requestBody
+        : null;
+    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;
   }
 
   private boolean isRecoverable(IOException e) {
@@ -385,7 +439,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
     return !sslFailure && !protocolFailure;
   }
 
-  HttpEngine getHttpEngine() {
+  public HttpEngine getHttpEngine() {
     return httpEngine;
   }
 
@@ -402,7 +456,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
    */
   private Retry processResponseHeaders() throws IOException {
     Proxy selectedProxy = httpEngine.connection != null
-        ? httpEngine.connection.getProxy()
+        ? httpEngine.connection.getRoute().getProxy()
         : requestedProxy;
     final int responseCode = getResponseCode();
     switch (responseCode) {

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/framework/src/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java b/framework/src/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java
index c224270..235f862 100644
--- a/framework/src/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java
+++ b/framework/src/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java
@@ -18,6 +18,7 @@ package com.squareup.okhttp.internal.http;
 
 import com.squareup.okhttp.Connection;
 import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Route;
 import com.squareup.okhttp.TunnelRequest;
 import java.io.IOException;
 import java.io.InputStream;
@@ -32,6 +33,7 @@ import java.security.Principal;
 import java.security.cert.Certificate;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLPeerUnverifiedException;
@@ -45,9 +47,10 @@ public final class HttpsURLConnectionImpl extends HttpsURLConnection {
   /** HttpUrlConnectionDelegate allows reuse of HttpURLConnectionImpl. */
   private final HttpUrlConnectionDelegate delegate;
 
-  public HttpsURLConnectionImpl(URL url, OkHttpClient client) {
+  public HttpsURLConnectionImpl(URL url, OkHttpClient client, OkResponseCache responseCache,
+      Set<Route> failedRoutes) {
     super(url);
-    delegate = new HttpUrlConnectionDelegate(url, client);
+    delegate = new HttpUrlConnectionDelegate(url, client, responseCache, failedRoutes);
   }
 
   @Override public String getCipherSuite() {
@@ -112,7 +115,7 @@ public final class HttpsURLConnectionImpl extends HttpsURLConnection {
     return null;
   }
 
-  HttpEngine getHttpEngine() {
+  public HttpEngine getHttpEngine() {
     return delegate.getHttpEngine();
   }
 
@@ -399,8 +402,9 @@ public final class HttpsURLConnectionImpl extends HttpsURLConnection {
   }
 
   private final class HttpUrlConnectionDelegate extends HttpURLConnectionImpl {
-    private HttpUrlConnectionDelegate(URL url, OkHttpClient client) {
-      super(url, client);
+    private HttpUrlConnectionDelegate(URL url, OkHttpClient client, OkResponseCache responseCache,
+        Set<Route> failedRoutes) {
+      super(url, client, responseCache, failedRoutes);
     }
 
     @Override protected HttpURLConnection getHttpConnectionToCache() {
@@ -425,8 +429,7 @@ public final class HttpsURLConnectionImpl extends HttpsURLConnection {
      * @param policy the HttpURLConnectionImpl with connection configuration
      */
     public HttpsEngine(HttpURLConnectionImpl policy, String method, RawHeaders requestHeaders,
-        Connection connection, RetryableOutputStream requestBody)
-        throws IOException {
+        Connection connection, RetryableOutputStream requestBody) throws IOException {
       super(policy, method, requestHeaders, connection, requestBody);
       this.sslSocket = connection != null ? (SSLSocket) connection.getSocket() : null;
     }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/framework/src/com/squareup/okhttp/internal/http/OkResponseCache.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/OkResponseCache.java b/framework/src/com/squareup/okhttp/internal/http/OkResponseCache.java
new file mode 100644
index 0000000..5829f02
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/http/OkResponseCache.java
@@ -0,0 +1,55 @@
+/*
+ * 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.http;
+
+import com.squareup.okhttp.ResponseSource;
+import java.io.IOException;
+import java.net.CacheRequest;
+import java.net.CacheResponse;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URLConnection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An extended response cache API. Unlike {@link java.net.ResponseCache}, this
+ * interface supports conditional caching and statistics.
+ *
+ * <p>Along with the rest of the {@code internal} package, this is not a public
+ * API. Applications wishing to supply their own caches must use the more
+ * limited {@link java.net.ResponseCache} interface.
+ */
+public interface OkResponseCache {
+  CacheResponse get(URI uri, String requestMethod, Map<String, List<String>> requestHeaders)
+      throws IOException;
+
+  CacheRequest put(URI uri, URLConnection urlConnection) throws IOException;
+
+  /**
+   * 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.
+   */
+  void update(CacheResponse conditionalCacheHit, HttpURLConnection connection) throws IOException;
+
+  /** Track an conditional GET that was satisfied by this cache. */
+  void trackConditionalCacheHit();
+
+  /** Track an HTTP response being satisfied by {@code source}. */
+  void trackResponse(ResponseSource source);
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/framework/src/com/squareup/okhttp/internal/http/OkResponseCacheAdapter.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/OkResponseCacheAdapter.java b/framework/src/com/squareup/okhttp/internal/http/OkResponseCacheAdapter.java
new file mode 100644
index 0000000..2ac915a
--- /dev/null
+++ b/framework/src/com/squareup/okhttp/internal/http/OkResponseCacheAdapter.java
@@ -0,0 +1,53 @@
+/*
+ * 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.http;
+
+import com.squareup.okhttp.ResponseSource;
+import java.io.IOException;
+import java.net.CacheRequest;
+import java.net.CacheResponse;
+import java.net.HttpURLConnection;
+import java.net.ResponseCache;
+import java.net.URI;
+import java.net.URLConnection;
+import java.util.List;
+import java.util.Map;
+
+public final class OkResponseCacheAdapter implements OkResponseCache {
+  private final ResponseCache responseCache;
+  public OkResponseCacheAdapter(ResponseCache responseCache) {
+    this.responseCache = responseCache;
+  }
+
+  @Override public CacheResponse get(URI uri, String requestMethod,
+      Map<String, List<String>> requestHeaders) throws IOException {
+    return responseCache.get(uri, requestMethod, requestHeaders);
+  }
+
+  @Override public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
+    return responseCache.put(uri, urlConnection);
+  }
+
+  @Override public void update(CacheResponse conditionalCacheHit, HttpURLConnection connection)
+      throws IOException {
+  }
+
+  @Override public void trackConditionalCacheHit() {
+  }
+
+  @Override public void trackResponse(ResponseSource source) {
+  }
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/framework/src/com/squareup/okhttp/internal/http/RawHeaders.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/RawHeaders.java b/framework/src/com/squareup/okhttp/internal/http/RawHeaders.java
index c121abc..eba887e 100644
--- a/framework/src/com/squareup/okhttp/internal/http/RawHeaders.java
+++ b/framework/src/com/squareup/okhttp/internal/http/RawHeaders.java
@@ -17,7 +17,6 @@
 
 package com.squareup.okhttp.internal.http;
 
-import com.squareup.okhttp.internal.Platform;
 import com.squareup.okhttp.internal.Util;
 import java.io.IOException;
 import java.io.InputStream;
@@ -186,25 +185,27 @@ public final class RawHeaders {
   public void addLine(String line) {
     int index = line.indexOf(":");
     if (index == -1) {
-      add("", line);
+      addLenient("", line);
     } else {
-      add(line.substring(0, index), line.substring(index + 1));
+      addLenient(line.substring(0, index), line.substring(index + 1));
     }
   }
 
   /** Add a field with the specified value. */
   public void add(String fieldName, String value) {
-    if (fieldName == null) {
-      throw new IllegalArgumentException("fieldName == null");
-    }
-    if (value == null) {
-      // Given null values, the RI sends a malformed field line like
-      // "Accept\r\n". For platform compatibility and HTTP compliance, we
-      // print a warning and ignore null values.
-      Platform.get()
-          .logW("Ignoring HTTP header field '" + fieldName + "' because its value is null");
-      return;
+    if (fieldName == null) throw new IllegalArgumentException("fieldname == null");
+    if (value == null) throw new IllegalArgumentException("value == null");
+    if (fieldName.length() == 0 || fieldName.indexOf('\0') != -1 || value.indexOf('\0') != -1) {
+      throw new IllegalArgumentException("Unexpected header: " + fieldName + ": " + value);
     }
+    addLenient(fieldName, value);
+  }
+
+  /**
+   * Add a field with the specified value without any validation. Only
+   * appropriate for headers from the remote peer.
+   */
+  private void addLenient(String fieldName, String value) {
     namesAndValues.add(fieldName);
     namesAndValues.add(value.trim());
   }
@@ -351,7 +352,9 @@ public final class RawHeaders {
       String fieldName = entry.getKey();
       List<String> values = entry.getValue();
       if (fieldName != null) {
-        result.addAll(fieldName, values);
+        for (String value : values) {
+          result.addLenient(fieldName, value);
+        }
       } else if (!values.isEmpty()) {
         result.setStatusLine(values.get(values.size() - 1));
       }
@@ -371,14 +374,6 @@ public final class RawHeaders {
       String name = namesAndValues.get(i).toLowerCase(Locale.US);
       String value = namesAndValues.get(i + 1);
 
-      // TODO: promote this check to where names and values are created
-      if (name.length() == 0
-          || value.length() == 0
-          || name.indexOf('\0') != -1
-          || value.indexOf('\0') != -1) {
-        throw new IllegalArgumentException("Unexpected header: " + name + ": " + value);
-      }
-
       // Drop headers that are forbidden when layering HTTP over SPDY.
       if (name.equals("connection")
           || name.equals("host")

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/framework/src/com/squareup/okhttp/internal/http/RequestHeaders.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/RequestHeaders.java b/framework/src/com/squareup/okhttp/internal/http/RequestHeaders.java
index 2544cee..5ec4fcc 100644
--- a/framework/src/com/squareup/okhttp/internal/http/RequestHeaders.java
+++ b/framework/src/com/squareup/okhttp/internal/http/RequestHeaders.java
@@ -22,7 +22,7 @@ import java.util.List;
 import java.util.Map;
 
 /** Parsed HTTP request headers. */
-final class RequestHeaders {
+public final class RequestHeaders {
   private final URI uri;
   private final RawHeaders headers;
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/framework/src/com/squareup/okhttp/internal/http/ResponseHeaders.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/ResponseHeaders.java b/framework/src/com/squareup/okhttp/internal/http/ResponseHeaders.java
index 22d8c5c..2ab564d 100644
--- a/framework/src/com/squareup/okhttp/internal/http/ResponseHeaders.java
+++ b/framework/src/com/squareup/okhttp/internal/http/ResponseHeaders.java
@@ -31,7 +31,7 @@ import java.util.concurrent.TimeUnit;
 import static com.squareup.okhttp.internal.Util.equal;
 
 /** Parsed HTTP response headers. */
-final class ResponseHeaders {
+public final class ResponseHeaders {
 
   /** HTTP header name for the local time when the request was sent. */
   private static final String SENT_MILLIS = "X-Android-Sent-Millis";
@@ -410,7 +410,8 @@ final class ResponseHeaders {
       if (ageMillis + minFreshMillis >= freshMillis) {
         headers.add("Warning", "110 HttpURLConnection \"Response is stale\"");
       }
-      if (ageMillis > TimeUnit.HOURS.toMillis(24) && isFreshnessLifetimeHeuristic()) {
+      long oneDayMillis = 24 * 60 * 60 * 1000L;
+      if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
         headers.add("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
       }
       return ResponseSource.CACHE;

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/framework/src/com/squareup/okhttp/internal/http/RetryableOutputStream.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/RetryableOutputStream.java b/framework/src/com/squareup/okhttp/internal/http/RetryableOutputStream.java
index 325327d..5eb6b76 100644
--- a/framework/src/com/squareup/okhttp/internal/http/RetryableOutputStream.java
+++ b/framework/src/com/squareup/okhttp/internal/http/RetryableOutputStream.java
@@ -16,6 +16,7 @@
 
 package com.squareup.okhttp.internal.http;
 
+import com.squareup.okhttp.internal.AbstractOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -28,7 +29,7 @@ import static com.squareup.okhttp.internal.Util.checkOffsetAndCount;
  * the post body to be transparently re-sent if the HTTP request must be
  * sent multiple times.
  */
-final class RetryableOutputStream extends AbstractHttpOutputStream {
+final class RetryableOutputStream extends AbstractOutputStream {
   private final int limit;
   private final ByteArrayOutputStream content;
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/framework/src/com/squareup/okhttp/internal/http/RouteSelector.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/http/RouteSelector.java b/framework/src/com/squareup/okhttp/internal/http/RouteSelector.java
index 798cff3..ce0a71d 100644
--- a/framework/src/com/squareup/okhttp/internal/http/RouteSelector.java
+++ b/framework/src/com/squareup/okhttp/internal/http/RouteSelector.java
@@ -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.Route;
 import com.squareup.okhttp.internal.Dns;
 import java.io.IOException;
 import java.net.InetAddress;
@@ -28,8 +29,11 @@ import java.net.SocketAddress;
 import java.net.URI;
 import java.net.UnknownHostException;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Set;
+import javax.net.ssl.SSLHandshakeException;
 
 import static com.squareup.okhttp.internal.Util.getEffectivePort;
 
@@ -51,6 +55,7 @@ public final class RouteSelector {
   private final ProxySelector proxySelector;
   private final ConnectionPool pool;
   private final Dns dns;
+  private final Set<Route> failedRoutes;
 
   /* The most recently attempted route. */
   private Proxy lastProxy;
@@ -64,19 +69,23 @@ public final class RouteSelector {
   /* State for negotiating the next InetSocketAddress to use. */
   private InetAddress[] socketAddresses;
   private int nextSocketAddressIndex;
-  private String socketHost;
   private int socketPort;
 
   /* State for negotiating the next TLS configuration */
   private int nextTlsMode = TLS_MODE_NULL;
 
+  /* State for negotiating failed routes */
+  private final List<Route> postponedRoutes;
+
   public RouteSelector(Address address, URI uri, ProxySelector proxySelector, ConnectionPool pool,
-      Dns dns) {
+      Dns dns, Set<Route> failedRoutes) {
     this.address = address;
     this.uri = uri;
     this.proxySelector = proxySelector;
     this.pool = pool;
     this.dns = dns;
+    this.failedRoutes = failedRoutes;
+    this.postponedRoutes = new LinkedList<Route>();
 
     resetNextProxy(uri, address.getProxy());
   }
@@ -86,7 +95,7 @@ public final class RouteSelector {
    * least one route.
    */
   public boolean hasNext() {
-    return hasNextTlsMode() || hasNextInetSocketAddress() || hasNextProxy();
+    return hasNextTlsMode() || hasNextInetSocketAddress() || hasNextProxy() || hasNextPostponed();
   }
 
   /**
@@ -105,7 +114,10 @@ public final class RouteSelector {
     if (!hasNextTlsMode()) {
       if (!hasNextInetSocketAddress()) {
         if (!hasNextProxy()) {
-          throw new NoSuchElementException();
+          if (!hasNextPostponed()) {
+            throw new NoSuchElementException();
+          }
+          return new Connection(nextPostponed());
         }
         lastProxy = nextProxy();
         resetNextInetSocketAddress(lastProxy);
@@ -113,9 +125,17 @@ public final class RouteSelector {
       lastInetSocketAddress = nextInetSocketAddress();
       resetNextTlsMode();
     }
+
     boolean modernTls = nextTlsMode() == TLS_MODE_MODERN;
+    Route route = new Route(address, lastProxy, lastInetSocketAddress, modernTls);
+    if (failedRoutes.contains(route)) {
+      postponedRoutes.add(route);
+      // We will only recurse in order to skip previously failed routes. They will be
+      // tried last.
+      return next();
+    }
 
-    return new Connection(address, lastProxy, lastInetSocketAddress, modernTls);
+    return new Connection(route);
   }
 
   /**
@@ -123,9 +143,17 @@ public final class RouteSelector {
    * failure on a connection returned by this route selector.
    */
   public void connectFailed(Connection connection, IOException failure) {
-    if (connection.getProxy().type() != Proxy.Type.DIRECT && proxySelector != null) {
+    Route failedRoute = connection.getRoute();
+    if (failedRoute.getProxy().type() != Proxy.Type.DIRECT && proxySelector != null) {
       // Tell the proxy selector when we fail to connect on a fresh connection.
-      proxySelector.connectFailed(uri, connection.getProxy().address(), failure);
+      proxySelector.connectFailed(uri, failedRoute.getProxy().address(), failure);
+    }
+
+    failedRoutes.add(failedRoute);
+    if (!(failure instanceof SSLHandshakeException)) {
+      // If the problem was not related to SSL then it will also fail with
+      // a different Tls mode therefore we can be proactive about it.
+      failedRoutes.add(failedRoute.flipTlsMode());
     }
   }
 
@@ -175,6 +203,7 @@ public final class RouteSelector {
   private void resetNextInetSocketAddress(Proxy proxy) throws UnknownHostException {
     socketAddresses = null; // Clear the addresses. Necessary if getAllByName() below throws!
 
+    String socketHost;
     if (proxy.type() == Proxy.Type.DIRECT) {
       socketHost = uri.getHost();
       socketPort = getEffectivePort(uri);
@@ -233,4 +262,14 @@ public final class RouteSelector {
       throw new AssertionError();
     }
   }
+
+  /** Returns true if there is another postponed route to try. */
+  private boolean hasNextPostponed() {
+    return !postponedRoutes.isEmpty();
+  }
+
+  /** Returns the next postponed route to try. */
+  private Route nextPostponed() {
+    return postponedRoutes.remove(0);
+  }
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java b/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
index b3e248c..fccd14f 100644
--- a/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
+++ b/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
@@ -139,17 +139,17 @@ public final class SpdyConnection implements Closeable {
     return stream;
   }
 
-  private void setIdle(boolean value) {
+  private synchronized void setIdle(boolean value) {
     idleStartTimeNs = value ? System.nanoTime() : 0L;
   }
 
   /** Returns true if this connection is idle. */
-  public boolean isIdle() {
+  public synchronized boolean isIdle() {
     return idleStartTimeNs != 0L;
   }
 
   /** Returns the time in ns when this connection became idle or 0L if connection is not idle. */
-  public long getIdleStartTimeNs() {
+  public synchronized long getIdleStartTimeNs() {
     return idleStartTimeNs;
   }
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/cbb0bd5e/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java
----------------------------------------------------------------------
diff --git a/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java b/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java
index 7a7b198..7d3f2bd 100644
--- a/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java
+++ b/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java
@@ -21,6 +21,7 @@ import java.io.Closeable;
 import java.io.DataInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
 import java.net.ProtocolException;
 import java.util.ArrayList;
 import java.util.List;
@@ -31,39 +32,46 @@ import java.util.zip.InflaterInputStream;
 
 /** Read spdy/3 frames. */
 final class SpdyReader implements Closeable {
-  static final byte[] DICTIONARY = ("\u0000\u0000\u0000\u0007options\u0000\u0000\u0000\u0004hea"
-      + "d\u0000\u0000\u0000\u0004post\u0000\u0000\u0000\u0003put\u0000\u0000\u0000\u0006dele"
-      + "te\u0000\u0000\u0000\u0005trace\u0000\u0000\u0000\u0006accept\u0000\u0000\u0000"
-      + "\u000Eaccept-charset\u0000\u0000\u0000\u000Faccept-encoding\u0000\u0000\u0000\u000Fa"
-      + "ccept-language\u0000\u0000\u0000\raccept-ranges\u0000\u0000\u0000\u0003age\u0000"
-      + "\u0000\u0000\u0005allow\u0000\u0000\u0000\rauthorization\u0000\u0000\u0000\rcache-co"
-      + "ntrol\u0000\u0000\u0000\nconnection\u0000\u0000\u0000\fcontent-base\u0000\u0000"
-      + "\u0000\u0010content-encoding\u0000\u0000\u0000\u0010content-language\u0000\u0000"
-      + "\u0000\u000Econtent-length\u0000\u0000\u0000\u0010content-location\u0000\u0000\u0000"
-      + "\u000Bcontent-md5\u0000\u0000\u0000\rcontent-range\u0000\u0000\u0000\fcontent-type"
-      + "\u0000\u0000\u0000\u0004date\u0000\u0000\u0000\u0004etag\u0000\u0000\u0000\u0006expe"
-      + "ct\u0000\u0000\u0000\u0007expires\u0000\u0000\u0000\u0004from\u0000\u0000\u0000"
-      + "\u0004host\u0000\u0000\u0000\bif-match\u0000\u0000\u0000\u0011if-modified-since"
-      + "\u0000\u0000\u0000\rif-none-match\u0000\u0000\u0000\bif-range\u0000\u0000\u0000"
-      + "\u0013if-unmodified-since\u0000\u0000\u0000\rlast-modified\u0000\u0000\u0000\blocati"
-      + "on\u0000\u0000\u0000\fmax-forwards\u0000\u0000\u0000\u0006pragma\u0000\u0000\u0000"
-      + "\u0012proxy-authenticate\u0000\u0000\u0000\u0013proxy-authorization\u0000\u0000"
-      + "\u0000\u0005range\u0000\u0000\u0000\u0007referer\u0000\u0000\u0000\u000Bretry-after"
-      + "\u0000\u0000\u0000\u0006server\u0000\u0000\u0000\u0002te\u0000\u0000\u0000\u0007trai"
-      + "ler\u0000\u0000\u0000\u0011transfer-encoding\u0000\u0000\u0000\u0007upgrade\u0000"
-      + "\u0000\u0000\nuser-agent\u0000\u0000\u0000\u0004vary\u0000\u0000\u0000\u0003via"
-      + "\u0000\u0000\u0000\u0007warning\u0000\u0000\u0000\u0010www-authenticate\u0000\u0000"
-      + "\u0000\u0006method\u0000\u0000\u0000\u0003get\u0000\u0000\u0000\u0006status\u0000"
-      + "\u0000\u0000\u0006200 OK\u0000\u0000\u0000\u0007version\u0000\u0000\u0000\bHTTP/1.1"
-      + "\u0000\u0000\u0000\u0003url\u0000\u0000\u0000\u0006public\u0000\u0000\u0000\nset-coo"
-      + "kie\u0000\u0000\u0000\nkeep-alive\u0000\u0000\u0000\u0006origin100101201202205206300"
-      + "302303304305306307402405406407408409410411412413414415416417502504505203 Non-Authori"
-      + "tative Information204 No Content301 Moved Permanently400 Bad Request401 Unauthorized"
-      + "403 Forbidden404 Not Found500 Internal Server Error501 Not Implemented503 Service Un"
-      + "availableJan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec 00:00:00 Mon, Tue, Wed, Th"
-      + "u, Fri, Sat, Sun, GMTchunked,text/html,image/png,image/jpg,image/gif,application/xml"
-      + ",application/xhtml+xml,text/plain,text/javascript,publicprivatemax-age=gzip,deflate,"
-      + "sdchcharset=utf-8charset=iso-8859-1,utf-,*,enq=0.").getBytes(Util.UTF_8);
+  static final byte[] DICTIONARY;
+  static {
+    try {
+      DICTIONARY = ("\u0000\u0000\u0000\u0007options\u0000\u0000\u0000\u0004hea"
+          + "d\u0000\u0000\u0000\u0004post\u0000\u0000\u0000\u0003put\u0000\u0000\u0000\u0006dele"
+          + "te\u0000\u0000\u0000\u0005trace\u0000\u0000\u0000\u0006accept\u0000\u0000\u0000"
+          + "\u000Eaccept-charset\u0000\u0000\u0000\u000Faccept-encoding\u0000\u0000\u0000\u000Fa"
+          + "ccept-language\u0000\u0000\u0000\raccept-ranges\u0000\u0000\u0000\u0003age\u0000"
+          + "\u0000\u0000\u0005allow\u0000\u0000\u0000\rauthorization\u0000\u0000\u0000\rcache-co"
+          + "ntrol\u0000\u0000\u0000\nconnection\u0000\u0000\u0000\fcontent-base\u0000\u0000"
+          + "\u0000\u0010content-encoding\u0000\u0000\u0000\u0010content-language\u0000\u0000"
+          + "\u0000\u000Econtent-length\u0000\u0000\u0000\u0010content-location\u0000\u0000\u0000"
+          + "\u000Bcontent-md5\u0000\u0000\u0000\rcontent-range\u0000\u0000\u0000\fcontent-type"
+          + "\u0000\u0000\u0000\u0004date\u0000\u0000\u0000\u0004etag\u0000\u0000\u0000\u0006expe"
+          + "ct\u0000\u0000\u0000\u0007expires\u0000\u0000\u0000\u0004from\u0000\u0000\u0000"
+          + "\u0004host\u0000\u0000\u0000\bif-match\u0000\u0000\u0000\u0011if-modified-since"
+          + "\u0000\u0000\u0000\rif-none-match\u0000\u0000\u0000\bif-range\u0000\u0000\u0000"
+          + "\u0013if-unmodified-since\u0000\u0000\u0000\rlast-modified\u0000\u0000\u0000\blocati"
+          + "on\u0000\u0000\u0000\fmax-forwards\u0000\u0000\u0000\u0006pragma\u0000\u0000\u0000"
+          + "\u0012proxy-authenticate\u0000\u0000\u0000\u0013proxy-authorization\u0000\u0000"
+          + "\u0000\u0005range\u0000\u0000\u0000\u0007referer\u0000\u0000\u0000\u000Bretry-after"
+          + "\u0000\u0000\u0000\u0006server\u0000\u0000\u0000\u0002te\u0000\u0000\u0000\u0007trai"
+          + "ler\u0000\u0000\u0000\u0011transfer-encoding\u0000\u0000\u0000\u0007upgrade\u0000"
+          + "\u0000\u0000\nuser-agent\u0000\u0000\u0000\u0004vary\u0000\u0000\u0000\u0003via"
+          + "\u0000\u0000\u0000\u0007warning\u0000\u0000\u0000\u0010www-authenticate\u0000\u0000"
+          + "\u0000\u0006method\u0000\u0000\u0000\u0003get\u0000\u0000\u0000\u0006status\u0000"
+          + "\u0000\u0000\u0006200 OK\u0000\u0000\u0000\u0007version\u0000\u0000\u0000\bHTTP/1.1"
+          + "\u0000\u0000\u0000\u0003url\u0000\u0000\u0000\u0006public\u0000\u0000\u0000\nset-coo"
+          + "kie\u0000\u0000\u0000\nkeep-alive\u0000\u0000\u0000\u0006origin100101201202205206300"
+          + "302303304305306307402405406407408409410411412413414415416417502504505203 Non-Authori"
+          + "tative Information204 No Content301 Moved Permanently400 Bad Request401 Unauthorized"
+          + "403 Forbidden404 Not Found500 Internal Server Error501 Not Implemented503 Service Un"
+          + "availableJan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec 00:00:00 Mon, Tue, Wed, Th"
+          + "u, Fri, Sat, Sun, GMTchunked,text/html,image/png,image/jpg,image/gif,application/xml"
+          + ",application/xhtml+xml,text/plain,text/javascript,publicprivatemax-age=gzip,deflate,"
+          + "sdchcharset=utf-8charset=iso-8859-1,utf-,*,enq=0.").getBytes(Util.UTF_8.name());
+    } catch (UnsupportedEncodingException e) {
+      throw new AssertionError();
+    }
+  }
 
   private final DataInputStream in;
   private final DataInputStream nameValueBlockIn;
@@ -252,7 +260,7 @@ final class SpdyReader implements Closeable {
 
       return entries;
     } catch (DataFormatException e) {
-      throw new IOException(e);
+      throw new IOException(e.getMessage());
     }
   }
 


Mime
View raw message