Merge "Update OkHttp to commit cc4633943800572673c03b32d0b2bc9a08ae184d"
diff --git a/README.md b/README.md
index c3a44ee..e206f23 100644
--- a/README.md
+++ b/README.md
@@ -1,44 +1,46 @@
 OkHttp
 ======
 
-An HTTP+SPDY client for Android and Java applications.
+An HTTP & SPDY client for Android and Java applications.
+
+For more information please see [the website][1].
+
 
 
 Download
 --------
 
-Downloadable .jars can be found on the [GitHub download page][1].
-
-You can also depend on the .jar through Maven:
+Download [the latest JAR][2] or grab via Maven:
 
 ```xml
 <dependency>
-    <groupId>com.squareup</groupId>
+    <groupId>com.squareup.okhttp</groupId>
     <artifactId>okhttp</artifactId>
     <version>(insert latest version)</version>
 </dependency>
 ```
 
 
-Known Issues
-------------
-
-OkHttp uses the platform's [ProxySelector][2]. Prior to Android 4.0, `ProxySelector` didn't honor the `proxyHost` and `proxyPort` system properties for HTTPS connections. Work around this by specifying the `https.proxyHost` and `https.proxyPort` system properties when using a proxy with HTTPS.
-
-OkHttp's test suite creates an in-process HTTPS server. Prior to Android 2.3, SSL server sockets were broken, and so HTTPS tests will time out when run on such devices.
-
-
 Building
 --------
 
 ### On the Desktop
-Run OkHttp tests on the desktop with Maven. Running SPDY tests on the desktop uses [Jetty-NPN](http://wiki.eclipse.org/Jetty/Feature/NPN) which requires OpenJDK 7+.
+
+Run OkHttp tests on the desktop with Maven. Running SPDY tests on the desktop uses
+[Jetty-NPN][3] which requires OpenJDK 7+.
+
 ```
 mvn clean test
 ```
 
 ### On a Device
-Test on a USB-attached Android using [Vogar](https://code.google.com/p/vogar/). Unfortunately `dx` requires that you build with Java 6, otherwise the test class will be silently omitted from the `.dex` file.
+
+OkHttp's test suite creates an in-process HTTPS server. Prior to Android 2.3, SSL server sockets
+were broken, and so HTTPS tests will time out when run on such devices.
+
+Test on a USB-attached Android using [Vogar][4]. Unfortunately `dx` requires that you build with
+Java 6, otherwise the test class will be silently omitted from the `.dex` file.
+
 ```
 mvn clean
 mvn package -DskipTests
@@ -67,5 +69,8 @@
 
 
 
- [1]: http://github.com/square/okhttp/downloads
- [2]: http://developer.android.com/reference/java/net/ProxySelector.html
+
+ [1]: http://square.github.io/okhttp
+ [2]: http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.squareup.okhttp&a=okhttp&v=LATEST
+ [3]: http://wiki.eclipse.org/Jetty/Feature/NPN
+ [4]: https://code.google.com/p/vogar/
diff --git a/pom.xml b/pom.xml
index a36c2e6..853f0b9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
   <parent>
     <groupId>com.squareup.okhttp</groupId>
     <artifactId>parent</artifactId>
-    <version>1.0.3-SNAPSHOT</version>
+    <version>1.1.2-SNAPSHOT</version>
   </parent>
 
   <artifactId>okhttp</artifactId>
diff --git a/src/main/java/com/squareup/okhttp/Connection.java b/src/main/java/com/squareup/okhttp/Connection.java
index 73c4b56..cfda281 100644
--- a/src/main/java/com/squareup/okhttp/Connection.java
+++ b/src/main/java/com/squareup/okhttp/Connection.java
@@ -31,6 +31,7 @@
 import java.io.OutputStream;
 import java.net.Proxy;
 import java.net.Socket;
+import java.net.SocketTimeoutException;
 import java.net.URL;
 import java.util.Arrays;
 import javax.net.ssl.SSLSocket;
@@ -192,6 +193,39 @@
     return !socket.isClosed() && !socket.isInputShutdown() && !socket.isOutputShutdown();
   }
 
+  /**
+   * Returns true if we are confident that we can read data from this
+   * connection. This is more expensive and more accurate than {@link
+   * #isAlive()}; callers should check {@link #isAlive()} first.
+   */
+  public boolean isReadable() {
+    if (!(in instanceof BufferedInputStream)) {
+      return true; // Optimistic.
+    }
+    if (isSpdy()) {
+      return true; // Optimistic. We can't test SPDY because its streams are in use.
+    }
+    BufferedInputStream bufferedInputStream = (BufferedInputStream) in;
+    try {
+      int readTimeout = socket.getSoTimeout();
+      try {
+        socket.setSoTimeout(1);
+        bufferedInputStream.mark(1);
+        if (bufferedInputStream.read() == -1) {
+          return false; // Stream is exhausted; socket is closed.
+        }
+        bufferedInputStream.reset();
+        return true;
+      } finally {
+        socket.setSoTimeout(readTimeout);
+      }
+    } catch (SocketTimeoutException ignored) {
+      return true; // Read timed out; socket is good.
+    } catch (IOException e) {
+      return false; // Couldn't read; socket is closed.
+    }
+  }
+
   public void resetIdleStartTime() {
     if (spdyConnection != null) {
       throw new IllegalStateException("spdyConnection != null");
diff --git a/src/main/java/com/squareup/okhttp/ConnectionPool.java b/src/main/java/com/squareup/okhttp/ConnectionPool.java
index 009f025..42b70b9 100644
--- a/src/main/java/com/squareup/okhttp/ConnectionPool.java
+++ b/src/main/java/com/squareup/okhttp/ConnectionPool.java
@@ -216,8 +216,6 @@
    * <p>It is an error to use {@code connection} after calling this method.
    */
   public void recycle(Connection connection) {
-    executorService.submit(connectionsCleanupCallable);
-
     if (connection.isSpdy()) {
       return;
     }
@@ -240,6 +238,8 @@
       connections.addFirst(connection);
       connection.resetIdleStartTime();
     }
+
+    executorService.submit(connectionsCleanupCallable);
   }
 
   /**
diff --git a/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java b/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java
index 51fd2a7..d6b6001 100644
--- a/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java
+++ b/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java
@@ -286,7 +286,7 @@
       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(client.getConnectTimeout(), client.getReadTimeout(), getTunnelConfig());
       client.getConnectionPool().maybeShare(connection);
diff --git a/src/main/java/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java b/src/main/java/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
index e8c198f..214f25a 100644
--- a/src/main/java/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
+++ b/src/main/java/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java
@@ -19,8 +19,6 @@
 
 import com.squareup.okhttp.Connection;
 import com.squareup.okhttp.OkHttpClient;
-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;
@@ -69,20 +67,12 @@
    */
   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;
-
   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;
 
@@ -212,22 +202,7 @@
       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 {
@@ -393,8 +368,7 @@
 
     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)
@@ -404,15 +378,9 @@
     }
 
     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;
   }
 
diff --git a/src/main/java/com/squareup/okhttp/internal/http/RawHeaders.java b/src/main/java/com/squareup/okhttp/internal/http/RawHeaders.java
index e5abd2c..e1fdcf4 100644
--- a/src/main/java/com/squareup/okhttp/internal/http/RawHeaders.java
+++ b/src/main/java/com/squareup/okhttp/internal/http/RawHeaders.java
@@ -123,23 +123,6 @@
     this.httpMinorVersion = httpMinorVersion;
   }
 
-  public void computeResponseStatusLineFromSpdyHeaders() throws IOException {
-    String status = null;
-    String version = null;
-    for (int i = 0; i < namesAndValues.size(); i += 2) {
-      String name = namesAndValues.get(i);
-      if (":status".equals(name)) {
-        status = namesAndValues.get(i + 1);
-      } else if (":version".equals(name)) {
-        version = namesAndValues.get(i + 1);
-      }
-    }
-    if (status == null || version == null) {
-      throw new ProtocolException("Expected ':status' and ':version' headers not present");
-    }
-    setStatusLine(version + " " + status);
-  }
-
   /**
    * @param method like "GET", "POST", "HEAD", etc.
    * @param path like "/foo/bar.html"
@@ -425,10 +408,13 @@
     return result;
   }
 
-  public static RawHeaders fromNameValueBlock(List<String> nameValueBlock) {
+  /** Returns headers for a name value block containing a SPDY response. */
+  public static RawHeaders fromNameValueBlock(List<String> nameValueBlock) throws IOException {
     if (nameValueBlock.size() % 2 != 0) {
       throw new IllegalArgumentException("Unexpected name value block: " + nameValueBlock);
     }
+    String status = null;
+    String version = null;
     RawHeaders result = new RawHeaders();
     for (int i = 0; i < nameValueBlock.size(); i += 2) {
       String name = nameValueBlock.get(i);
@@ -438,11 +424,21 @@
         if (end == -1) {
           end = values.length();
         }
-        result.namesAndValues.add(name);
-        result.namesAndValues.add(values.substring(start, end));
+        String value = values.substring(start, end);
+        if (":status".equals(name)) {
+          status = value;
+        } else if (":version".equals(name)) {
+          version = value;
+        } else {
+          result.namesAndValues.add(name);
+          result.namesAndValues.add(value);
+        }
         start = end + 1;
       }
     }
+    if (status == null) throw new ProtocolException("Expected ':status' header not present");
+    if (version == null) throw new ProtocolException("Expected ':version' header not present");
+    result.setStatusLine(version + " " + status);
     return result;
   }
 }
diff --git a/src/main/java/com/squareup/okhttp/internal/http/RouteSelector.java b/src/main/java/com/squareup/okhttp/internal/http/RouteSelector.java
index bab9df2..1055e4f 100644
--- a/src/main/java/com/squareup/okhttp/internal/http/RouteSelector.java
+++ b/src/main/java/com/squareup/okhttp/internal/http/RouteSelector.java
@@ -102,11 +102,11 @@
    *
    * @throws NoSuchElementException if there are no more routes to attempt.
    */
-  public Connection next() throws IOException {
+  public Connection next(String method) throws IOException {
     // Always prefer pooled connections over new connections.
-    Connection pooled = pool.get(address);
-    if (pooled != null) {
-      return pooled;
+    for (Connection pooled; (pooled = pool.get(address)) != null; ) {
+      if (method.equals("GET") || pooled.isReadable()) return pooled;
+      pooled.close();
     }
 
     // Compute the next route to attempt.
@@ -131,7 +131,7 @@
       postponedRoutes.add(route);
       // We will only recurse in order to skip previously failed routes. They will be
       // tried last.
-      return next();
+      return next(method);
     }
 
     return new Connection(route);
diff --git a/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java b/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java
index daa4e80..a37a91c 100644
--- a/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java
+++ b/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java
@@ -69,7 +69,6 @@
   @Override public ResponseHeaders readResponseHeaders() throws IOException {
     List<String> nameValueBlock = stream.getResponseHeaders();
     RawHeaders rawHeaders = RawHeaders.fromNameValueBlock(nameValueBlock);
-    rawHeaders.computeResponseStatusLineFromSpdyHeaders();
     httpEngine.receiveHeaders(rawHeaders);
 
     ResponseHeaders headers = new ResponseHeaders(httpEngine.uri, rawHeaders);
diff --git a/src/test/java/com/squareup/okhttp/ConnectionPoolTest.java b/src/test/java/com/squareup/okhttp/ConnectionPoolTest.java
index e26e563..6820b43 100644
--- a/src/test/java/com/squareup/okhttp/ConnectionPoolTest.java
+++ b/src/test/java/com/squareup/okhttp/ConnectionPoolTest.java
@@ -395,7 +395,8 @@
     Util.closeQuietly(httpA); // Include a closed connection in the pool.
     pool.recycle(httpB);
     pool.maybeShare(spdyA);
-    assertEquals(3, pool.getConnectionCount());
+    int connectionCount = pool.getConnectionCount();
+    assertTrue(connectionCount == 2 || connectionCount == 3);
 
     pool.evictAll();
     assertEquals(0, pool.getConnectionCount());
diff --git a/src/test/java/com/squareup/okhttp/internal/http/RawHeadersTest.java b/src/test/java/com/squareup/okhttp/internal/http/RawHeadersTest.java
index 474e507..7d8ecf3 100644
--- a/src/test/java/com/squareup/okhttp/internal/http/RawHeadersTest.java
+++ b/src/test/java/com/squareup/okhttp/internal/http/RawHeadersTest.java
@@ -23,7 +23,7 @@
 import static org.junit.Assert.assertEquals;
 
 public final class RawHeadersTest {
-  @Test public void parseNameValueBlock() {
+  @Test public void parseNameValueBlock() throws IOException {
     List<String> nameValueBlock =
         Arrays.asList("cache-control", "no-cache, no-store", "set-cookie", "Cookie1\u0000Cookie2",
             ":status", "200 OK");
diff --git a/src/test/java/com/squareup/okhttp/internal/http/RouteSelectorTest.java b/src/test/java/com/squareup/okhttp/internal/http/RouteSelectorTest.java
index 1cdcb1d..a92db9e 100644
--- a/src/test/java/com/squareup/okhttp/internal/http/RouteSelectorTest.java
+++ b/src/test/java/com/squareup/okhttp/internal/http/RouteSelectorTest.java
@@ -88,12 +88,13 @@
 
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(255, 1);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
+        false);
     dns.assertRequests(uriHost);
 
     assertFalse(routeSelector.hasNext());
     try {
-      routeSelector.next();
+      routeSelector.next("GET");
       fail();
     } catch (NoSuchElementException expected) {
     }
@@ -106,14 +107,15 @@
 
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(255, 1);
-    Connection connection = routeSelector.next();
+    Connection connection = routeSelector.next("GET");
     RouteDatabase routeDatabase = new RouteDatabase();
     routeDatabase.failed(connection.getRoute(), new IOException());
     routeSelector = new RouteSelector(address, uri, proxySelector, pool, dns, routeDatabase);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
+        false);
     assertFalse(routeSelector.hasNext());
     try {
-      routeSelector.next();
+      routeSelector.next("GET");
       fail();
     } catch (NoSuchElementException expected) {
     }
@@ -126,9 +128,9 @@
 
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(255, 2);
-    assertConnection(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort,
+    assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
         false);
-    assertConnection(routeSelector.next(), address, proxyA, dns.inetAddresses[1], proxyAPort,
+    assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort,
         false);
 
     assertFalse(routeSelector.hasNext());
@@ -144,8 +146,10 @@
 
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(255, 2);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, false);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[1], uriPort, false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
+        false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort,
+        false);
 
     assertFalse(routeSelector.hasNext());
     dns.assertRequests(uri.getHost());
@@ -162,7 +166,8 @@
 
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(255, 1);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
+        false);
     dns.assertRequests(uriHost);
 
     assertFalse(routeSelector.hasNext());
@@ -175,8 +180,10 @@
 
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(255, 2);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, false);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[1], uriPort, false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
+        false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort,
+        false);
 
     assertFalse(routeSelector.hasNext());
     dns.assertRequests(uri.getHost());
@@ -195,23 +202,24 @@
     // First try the IP addresses of the first proxy, in sequence.
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(255, 2);
-    assertConnection(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort,
+    assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
         false);
-    assertConnection(routeSelector.next(), address, proxyA, dns.inetAddresses[1], proxyAPort,
+    assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort,
         false);
     dns.assertRequests(proxyAHost);
 
     // Next try the IP address of the second proxy.
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(254, 1);
-    assertConnection(routeSelector.next(), address, proxyB, dns.inetAddresses[0], proxyBPort,
+    assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[0], proxyBPort,
         false);
     dns.assertRequests(proxyBHost);
 
     // Finally try the only IP address of the origin server.
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(253, 1);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
+        false);
     dns.assertRequests(uriHost);
 
     assertFalse(routeSelector.hasNext());
@@ -228,7 +236,8 @@
     // Only the origin server will be attempted.
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(255, 1);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
+        false);
     dns.assertRequests(uriHost);
 
     assertFalse(routeSelector.hasNext());
@@ -246,14 +255,14 @@
 
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(255, 1);
-    assertConnection(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort,
+    assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
         false);
     dns.assertRequests(proxyAHost);
 
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = null;
     try {
-      routeSelector.next();
+      routeSelector.next("GET");
       fail();
     } catch (UnknownHostException expected) {
     }
@@ -261,13 +270,14 @@
 
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(255, 1);
-    assertConnection(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort,
+    assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
         false);
     dns.assertRequests(proxyAHost);
 
     assertTrue(routeSelector.hasNext());
     dns.inetAddresses = makeFakeAddresses(254, 1);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
+        false);
     dns.assertRequests(uriHost);
 
     assertFalse(routeSelector.hasNext());
@@ -281,7 +291,7 @@
         routeDatabase);
 
     dns.inetAddresses = makeFakeAddresses(255, 1);
-    Connection connection = routeSelector.next();
+    Connection connection = routeSelector.next("GET");
     routeSelector.connectFailed(connection, new IOException("Non SSL exception"));
     assertTrue(routeDatabase.failedRoutesCount() == 2);
   }
@@ -294,7 +304,7 @@
         routeDatabase);
 
     dns.inetAddresses = makeFakeAddresses(255, 1);
-    Connection connection = routeSelector.next();
+    Connection connection = routeSelector.next("GET");
     routeSelector.connectFailed(connection, new SSLHandshakeException("SSL exception"));
     assertTrue(routeDatabase.failedRoutesCount() == 1);
   }
@@ -309,31 +319,39 @@
 
     // Proxy A
     dns.inetAddresses = makeFakeAddresses(255, 2);
-    assertConnection(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort, true);
+    assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
+        true);
     dns.assertRequests(proxyAHost);
-    assertConnection(routeSelector.next(), address, proxyA, dns.inetAddresses[0], proxyAPort,
+    assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[0], proxyAPort,
         false);
-    assertConnection(routeSelector.next(), address, proxyA, dns.inetAddresses[1], proxyAPort, true);
-    assertConnection(routeSelector.next(), address, proxyA, dns.inetAddresses[1], proxyAPort,
+    assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort,
+        true);
+    assertConnection(routeSelector.next("GET"), address, proxyA, dns.inetAddresses[1], proxyAPort,
         false);
 
     // Proxy B
     dns.inetAddresses = makeFakeAddresses(254, 2);
-    assertConnection(routeSelector.next(), address, proxyB, dns.inetAddresses[0], proxyBPort, true);
+    assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[0], proxyBPort,
+        true);
     dns.assertRequests(proxyBHost);
-    assertConnection(routeSelector.next(), address, proxyB, dns.inetAddresses[0], proxyBPort,
+    assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[0], proxyBPort,
         false);
-    assertConnection(routeSelector.next(), address, proxyB, dns.inetAddresses[1], proxyBPort, true);
-    assertConnection(routeSelector.next(), address, proxyB, dns.inetAddresses[1], proxyBPort,
+    assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[1], proxyBPort,
+        true);
+    assertConnection(routeSelector.next("GET"), address, proxyB, dns.inetAddresses[1], proxyBPort,
         false);
 
     // Origin
     dns.inetAddresses = makeFakeAddresses(253, 2);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, true);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
+        true);
     dns.assertRequests(uriHost);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[0], uriPort, false);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[1], uriPort, true);
-    assertConnection(routeSelector.next(), address, NO_PROXY, dns.inetAddresses[1], uriPort, false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[0], uriPort,
+        false);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort,
+        true);
+    assertConnection(routeSelector.next("GET"), address, NO_PROXY, dns.inetAddresses[1], uriPort,
+        false);
 
     assertFalse(routeSelector.hasNext());
   }
@@ -350,7 +368,7 @@
     // Extract the regular sequence of routes from selector.
     List<Connection> regularRoutes = new ArrayList<Connection>();
     while (routeSelector.hasNext()) {
-      regularRoutes.add(routeSelector.next());
+      regularRoutes.add(routeSelector.next("GET"));
     }
 
     // Check that we do indeed have more than one route.
@@ -362,7 +380,7 @@
 
     List<Connection> routesWithFailedRoute = new ArrayList<Connection>();
     while (routeSelector.hasNext()) {
-      routesWithFailedRoute.add(routeSelector.next());
+      routesWithFailedRoute.add(routeSelector.next("GET"));
     }
 
     assertEquals(regularRoutes.get(0).getRoute(),
diff --git a/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java b/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java
index 29b5cab..5abe477 100644
--- a/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java
+++ b/src/test/java/com/squareup/okhttp/internal/http/URLConnectionTest.java
@@ -2282,7 +2282,7 @@
   }
 
   // This test is ignored because we don't (yet) reliably recover for large request bodies.
-  @Test @Ignore public void postFailsWithBufferedRequestForLargeRequest() throws Exception {
+  @Test public void postFailsWithBufferedRequestForLargeRequest() throws Exception {
     reusedConnectionFailsWithPost(TransferKind.END_OF_STREAM, 16384);
   }
 
@@ -2290,8 +2290,7 @@
     reusedConnectionFailsWithPost(TransferKind.CHUNKED, 1024);
   }
 
-  // This test is ignored because we don't (yet) reliably recover for large request bodies.
-  @Test @Ignore public void postFailsWithChunkedRequestForLargeRequest() throws Exception {
+  @Test public void postFailsWithChunkedRequestForLargeRequest() throws Exception {
     reusedConnectionFailsWithPost(TransferKind.CHUNKED, 16384);
   }
 
@@ -2299,8 +2298,7 @@
     reusedConnectionFailsWithPost(TransferKind.FIXED_LENGTH, 1024);
   }
 
-  // This test is ignored because we don't (yet) reliably recover for large request bodies.
-  @Test @Ignore public void postFailsWithFixedLengthRequestForLargeRequest() throws Exception {
+  @Test public void postFailsWithFixedLengthRequestForLargeRequest() throws Exception {
     reusedConnectionFailsWithPost(TransferKind.FIXED_LENGTH, 16384);
   }
 
diff --git a/src/test/java/com/squareup/okhttp/internal/spdy/HttpOverSpdyTest.java b/src/test/java/com/squareup/okhttp/internal/spdy/HttpOverSpdyTest.java
index 5970088..e17a120 100644
--- a/src/test/java/com/squareup/okhttp/internal/spdy/HttpOverSpdyTest.java
+++ b/src/test/java/com/squareup/okhttp/internal/spdy/HttpOverSpdyTest.java
@@ -90,12 +90,14 @@
   }
 
   @Test public void get() throws Exception {
-    MockResponse response = new MockResponse().setBody("ABCDE");
+    MockResponse response = new MockResponse().setBody("ABCDE").setStatus("HTTP/1.1 200 Sweet");
     server.enqueue(response);
     server.play();
 
     HttpURLConnection connection = client.open(server.getUrl("/foo"));
     assertContent("ABCDE", connection, Integer.MAX_VALUE);
+    assertEquals(200, connection.getResponseCode());
+    assertEquals("Sweet", connection.getResponseMessage());
 
     RecordedRequest request = server.takeRequest();
     assertEquals("GET /foo HTTP/1.1", request.getRequestLine());
@@ -211,6 +213,23 @@
     assertEquals(2, cache.getHitCount());
   }
 
+  @Test public void conditionalCache() throws IOException {
+    client.setResponseCache(cache);
+
+    server.enqueue(new MockResponse().addHeader("ETag: v1").setBody("A"));
+    server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+    server.play();
+
+    assertContent("A", client.open(server.getUrl("/")), Integer.MAX_VALUE);
+    assertEquals(1, cache.getRequestCount());
+    assertEquals(1, cache.getNetworkCount());
+    assertEquals(0, cache.getHitCount());
+    assertContent("A", client.open(server.getUrl("/")), Integer.MAX_VALUE);
+    assertEquals(2, cache.getRequestCount());
+    assertEquals(2, cache.getNetworkCount());
+    assertEquals(1, cache.getHitCount());
+  }
+
   @Test public void acceptAndTransmitCookies() throws Exception {
     CookieManager cookieManager = new CookieManager();
     client.setCookieHandler(cookieManager);