am 00efe680: Merge "Make Apache HttpClient play nice with large kernel socket buffers."

* commit '00efe680b4cc6e9849cb270058ac5967af92ffef':
  Make Apache HttpClient play nice with large kernel socket buffers.
diff --git a/src/org/apache/http/impl/AbstractHttpClientConnection.java b/src/org/apache/http/impl/AbstractHttpClientConnection.java
index ebfaabb..ac3f4d1 100644
--- a/src/org/apache/http/impl/AbstractHttpClientConnection.java
+++ b/src/org/apache/http/impl/AbstractHttpClientConnection.java
@@ -47,6 +47,7 @@
 import org.apache.http.impl.entity.StrictContentLengthStrategy;
 import org.apache.http.impl.io.HttpRequestWriter;
 import org.apache.http.impl.io.HttpResponseParser;
+import org.apache.http.impl.io.SocketInputBuffer;
 import org.apache.http.io.HttpMessageParser;
 import org.apache.http.io.HttpMessageWriter;
 import org.apache.http.io.SessionInputBuffer;
@@ -198,6 +199,12 @@
             return true;
         }
         try {
+            // BEGIN android-added
+            //     don't reuse connections when the socket input stream is exhausted
+            if (inbuffer instanceof SocketInputBuffer) {
+                return ((SocketInputBuffer) inbuffer).isStale();
+            }
+            // END android-added
             this.inbuffer.isDataAvailable(1);
             return false;
         } catch (IOException ex) {
diff --git a/src/org/apache/http/impl/client/DefaultHttpClient.java b/src/org/apache/http/impl/client/DefaultHttpClient.java
index 7dcb2a2..7b3a3fa 100644
--- a/src/org/apache/http/impl/client/DefaultHttpClient.java
+++ b/src/org/apache/http/impl/client/DefaultHttpClient.java
@@ -61,7 +61,7 @@
 import org.apache.http.impl.DefaultConnectionReuseStrategy;
 import org.apache.http.impl.auth.BasicSchemeFactory;
 import org.apache.http.impl.auth.DigestSchemeFactory;
-import org.apache.http.impl.conn.DefaultHttpRoutePlanner;
+import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
 import org.apache.http.impl.conn.SingleClientConnManager;
 import org.apache.http.impl.cookie.BestMatchSpecFactory;
 import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
@@ -326,8 +326,10 @@
 
     @Override
     protected HttpRoutePlanner createHttpRoutePlanner() {
-        return new DefaultHttpRoutePlanner
-            (getConnectionManager().getSchemeRegistry());
+        // BEGIN android-changed
+        //     Use the proxy specified by system properties
+        return new ProxySelectorRoutePlanner(getConnectionManager().getSchemeRegistry(), null);
+        // END android-changed
     }
 
 
diff --git a/src/org/apache/http/impl/client/DefaultRequestDirector.java b/src/org/apache/http/impl/client/DefaultRequestDirector.java
index a95c522..bfdddd6 100644
--- a/src/org/apache/http/impl/client/DefaultRequestDirector.java
+++ b/src/org/apache/http/impl/client/DefaultRequestDirector.java
@@ -67,6 +67,7 @@
 import org.apache.http.client.UserTokenHandler;
 import org.apache.http.client.methods.AbortableHttpRequest;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.client.params.ClientPNames;
 import org.apache.http.client.params.HttpClientParams;
 import org.apache.http.client.protocol.ClientContext;
@@ -357,7 +358,13 @@
                 // Reopen connection if needed
                 if (!managedConn.isOpen()) {
                     managedConn.open(route, context, params);
-                } 
+                }
+                // BEGIN android-added
+                else {
+                    // b/3241899 set the per request timeout parameter on reused connections
+                    managedConn.setSocketTimeout(HttpConnectionParams.getSoTimeout(params));
+                }
+                // END android-added
                 
                 try {
                     establishRoute(route, context);
@@ -569,8 +576,21 @@
                 ClientPNames.DEFAULT_HOST);
         }
         if (target == null) {
-            throw new IllegalStateException
-                ("Target host must not be null, or set in parameters.");
+            // BEGIN android-changed
+            //     If the URI was malformed, make it obvious where there's no host component
+            String scheme = null;
+            String host = null;
+            String path = null;
+            URI uri;
+            if (request instanceof HttpUriRequest
+                    && (uri = ((HttpUriRequest) request).getURI()) != null) {
+                scheme = uri.getScheme();
+                host = uri.getHost();
+                path = uri.getPath();
+            }
+            throw new IllegalStateException( "Target host must not be null, or set in parameters."
+                    + " scheme=" + scheme + ", host=" + host + ", path=" + path);
+            // END android-changed
         }
 
         return this.routePlanner.determineRoute(target, request, context);
diff --git a/src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java b/src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java
index 136caf4..daa7227 100644
--- a/src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java
+++ b/src/org/apache/http/impl/conn/ProxySelectorRoutePlanner.java
@@ -51,6 +51,7 @@
 import org.apache.http.conn.scheme.SchemeRegistry;
 
 import org.apache.http.conn.params.ConnRouteParams;
+import org.apache.http.conn.params.ConnRoutePNames;
 
 
 /**
@@ -140,7 +141,18 @@
 
         final InetAddress local =
             ConnRouteParams.getLocalAddress(request.getParams());
-        final HttpHost proxy = determineProxy(target, request, context);
+
+        // BEGIN android-changed
+        //     If the client or request explicitly specifies a proxy (or no
+        //     proxy), prefer that over the ProxySelector's VM-wide default.
+        HttpHost proxy = (HttpHost) request.getParams().getParameter(ConnRoutePNames.DEFAULT_PROXY);
+        if (proxy == null) {
+            proxy = determineProxy(target, request, context);
+        } else if (ConnRouteParams.NO_HOST.equals(proxy)) {
+            // value is explicitly unset
+            proxy = null;
+        }
+        // END android-changed
 
         final Scheme schm =
             this.schemeRegistry.getScheme(target.getSchemeName());
diff --git a/src/org/apache/http/impl/io/SocketInputBuffer.java b/src/org/apache/http/impl/io/SocketInputBuffer.java
index 4ca9e5f..7d4323b 100644
--- a/src/org/apache/http/impl/io/SocketInputBuffer.java
+++ b/src/org/apache/http/impl/io/SocketInputBuffer.java
@@ -36,6 +36,7 @@
 import java.net.Socket;
 import java.net.SocketTimeoutException;
 
+import java.net.SocketTimeoutException;
 import org.apache.http.params.HttpParams;
 
 
@@ -89,4 +90,30 @@
         return result;
     }    
         
+    // BEGIN android-added
+    /**
+     * Returns true if the connection is probably functional. It's insufficient
+     * to rely on isDataAvailable() returning normally; that approach cannot
+     * distinguish between an exhausted stream and a stream with zero bytes
+     * buffered.
+     *
+     * @hide
+     */
+    public boolean isStale() throws IOException {
+        if (hasBufferedData()) {
+            return false;
+        }
+        int oldTimeout = this.socket.getSoTimeout();
+        try {
+            this.socket.setSoTimeout(1);
+            return fillBuffer() == -1;
+        } catch (SocketTimeoutException e) {
+            return false; // the connection is not stale; hooray
+        } catch (IOException e) {
+            return true; // the connection is stale, the read or soTimeout failed.
+        } finally {
+            this.socket.setSoTimeout(oldTimeout);
+        }
+    }
+    // END android-added
 }