[SCM] httpcomponents-client: HTTP/1.1 compliant HTTP agent Java implementation branch, upstream, updated. upstream/4.1.1-2-gdbe3299

Damien Raude-Morvan drazzib at debian.org
Wed Dec 19 23:52:56 UTC 2012


The following commit has been merged in the upstream branch:
commit 0f3aa2b46e2fac9bb5ed5a71bcdd1134769bc38e
Author: Damien Raude-Morvan <drazzib at debian.org>
Date:   Sun Aug 19 15:05:13 2012 +0200

    Imported Upstream version 4.2.1

diff --git a/NOTICE.txt b/NOTICE.txt
index 3a98f45..ea1911c 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -1,8 +1,6 @@
 Apache HttpComponents Client
-Copyright 1999-2011 The Apache Software Foundation
+Copyright 1999-2012 The Apache Software Foundation
 
 This product includes software developed by
 The Apache Software Foundation (http://www.apache.org/).
 
-This project contains annotations derived from JCIP-ANNOTATIONS
-Copyright (c) 2005 Brian Goetz and Tim Peierls. See http://www.jcip.net
\ No newline at end of file
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index 31535ab..9113002 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -1,7 +1,297 @@
+Release 4.2.1 
+-------------------
+
+HttpClient 4.2.1 (GA) is a bug fix release that addresses a number of issues reported since 
+release 4.2.
+
+Users of HttpClient 4.2 are advised to upgrade.
+
+Changelog
+-------------------
+
+* [HTTPCLIENT-1209] Redirect URIs are now normalized.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1202] ResponseCachingPolicy should honor explicit cache-control
+  directives for other status codes
+  Contributed by Jon Moore <jonm at apache.org>
+
+* [HTTPCLIENT-1199] DecompressingHttpClient strips content from entity enclosing requests
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1198] HttpHost is not set in HttpContext in CachingHttpClient.
+  Contributed by Jon Moore <jonm at apache.org>
+
+* [HTTPCLIENT-1200] DecompressingHttpClient fails to generate correct HttpHost context attribute.
+  Contributed by Guillaume Castagnino <casta+jira at xwing.info> 
+
+* [HTTPCLIENT-1192] URIBuilder encodes query parameters twice.
+  Contributed by Oleg Kalnichevski <olegk at apache.org> and Sebastian Bazley <sebb at apache.org>.
+
+* [HTTPCLIENT-1196] Fixed NPE in UrlEncodedFormEntity constructor thrown if charset is null.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1193] Fixed regression in the route tracking logic of the default connection manager 
+  causing cross-site redirect failures.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+Release 4.2
+-------------------
+
+This is the first stable (GA) release of HttpClient 4.2. The most notable enhancements included 
+in this release are:
+
+* New facade API for HttpClient based on the concept of a fluent interface. The fluent API exposes
+  only the most fundamental functions of HttpClient and is intended for relatively simple use cases
+  that do not require the full flexibility of HttpClient. However, the fluent API almost fully
+  relieves the users from having to deal with connection management and resource deallocation.
+
+* Redesigned and rewritten connection management code. 
+
+* Enhanced HTTP authentication API that enables HttpClient to handle more complex authentication
+  scenarios. HttpClient 4.2 is now capable of making use of multiple authentication challenges
+  and retry authentication with a fall-back scheme in case the primary one fails. This can be
+  important for compatibility with Microsoft products that are often configured to use
+  SPNEGO/Kerberos as the preferred authentication scheme.
+
+
+Changelog
+-------------------
+
+* [HTTPCLIENT-1187] If a revalidation response is deemed too old CachingHttpClient fails to  
+  consume its content resulting in a connection leak.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1186] State of newly created connections in the connection pool is not always 
+  correctly updated potentially allowing those connections to be leased to users with a different 
+  security context. 
+  Contributed by Ralf Poehlmann <rpn at methodpark.de> 
+
+* [HTTPCLIENT-1179] Upgraded Commons Codec dependency to version 1.6
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1177] always remove fragments from request URIs
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+Incompatible changes
+--------------------
+[Compared to release version 4.1.3]
+
+The following fields have been deprecated for some time now and have been deleted:
+
+org.apache.http.client.params.ClientPNames#CONNECTION_MANAGER_FACTORY
+org.apache.http.impl.cookie.BrowserCompatSpec#DATE_PATTERNS
+
+The following methods have been deprecated for some time now and have been deleted:
+
+org.apache.http.client.params.ClientParamBean#setConnectionManagerFactory(org.apache.http.conn.ClientConnectionManagerFactory)
+org.apache.http.client.protocol.ClientContextConfigurer#setAuthSchemePref(java.util.List)
+org.apache.http.entity.mime.content.FileBody#writeTo(java.io.OutputStream, int)
+org.apache.http.entity.mime.content.InputStreamBody#writeTo(java.io.OutputStream, int)
+org.apache.http.entity.mime.content.StringBody#writeTo(java.io.OutputStream, int)
+
+The following classes have been deprecated for some while now and have been deleted:
+
+org.apache.http.impl.conn.tsccm.RefQueueHandler
+org.apache.http.impl.conn.tsccm.AbstractConnPool no longer implements interface org.apache.http.impl.conn.tsccm.RefQueueHandler
+org.apache.http.impl.conn.tsccm.ConnPoolByRoute no longer implements interface org.apache.http.impl.conn.tsccm.RefQueueHandler
+org.apache.http.impl.conn.tsccm.RefQueueWorker
+
+
+
+Release 4.2 BETA1
+-------------------
+
+This is the first BETA release of HttpClient 4.2. This release completes development of several
+notable enhancements in HttpClient:
+
+* New facade API for HttpClient based on the concept of a fluent interface. The fluent API exposes
+  only the most fundamental functions of HttpClient and is intended for relatively simple use cases
+  that do not require the full flexibility of HttpClient. However, the fluent API almost fully
+  relieves the users from having to deal with connection management and resource deallocation.
+
+* Redesigned and rewritten connection management code. As of release 4.2 HttpClient will be using
+  pooling connection manager per default.
+
+* Enhanced HTTP authentication API that enables HttpClient to handle more complex authentication
+  scenarios. HttpClient 4.2 is now capable of making use of multiple authentication challenges
+  and retry authentication with a fall-back scheme in case the primary one fails. This can be
+  important for compatibility with Microsoft products that are often configured to use
+  SPNEGO/Kerberos as the preferred authentication scheme.
+
+
+Changelog
+-------------------
+
+* [HTTPCLIENT-1164] Compressed entities are not being cached properly.
+  Contributed by Jon Moore <jonm at apache dot org>.
+
+* [HTTPCLIENT-1154] MemcachedHttpCacheStorage should allow client to
+  specify custom prefix string for keys.
+  Contributed by Jon Moore <jonm at apache dot org>.
+  
+* [HTTPCLIENT-1153] MemcachedHttpCacheStorage uses URL as cache key;
+  shouldn't due to fixed maximum-length memcached keys.
+  Contributed by Jon Moore <jonm at apache dot org>.
+  
+* [HTTPCLIENT-1157] MemcachedHttpCacheStroage should throw IOExceptions
+  instead of RuntimeExceptions.
+  Contributed by James Miller <jamesmiller01 at gmail dot com>.
+  
+* [HTTPCLIENT-1152] MemcachedHttpCacheStorage should verify class of
+  returned object before casting.
+  Contributed by Rajika Kumarasiri <rajika at wso2 dot com>.
+
+* [HTTPCLIENT-1155] CachingHttpClient fails to ensure that the response content gets fully consumed 
+  when using a ResponseHandler, which can potentially lead to connection leaks.
+  Contributed by James Miller <jamesmiller01 at gmail dot com>
+  
+* [HTTPCLIENT-1147] When HttpClient-Cache cannot open cache file, should act like miss.
+  Contributed by Joe Campbell <joseph.r.campbell at gmail.com>
+
+* [HTTPCLIENT-1137] Values for the Via header are cached and reused by httpclient-cache.
+  Contributed by Alin Vasile <alinachegalati at yahoo dot com>
+
+* [HTTPCLIENT-1142] Infinite loop on NTLM authentication failure.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+  
+* [HTTPCLIENT-1143] CachingHttpClient leaks connections with stale-if-error.
+  Contributed by James Miller <jamesmiller01 at gmail dot com>
+
+Release 4.2 ALPHA1
+-------------------
+
+This is the first ALPHA release of HttpClient 4.2. The 4.2 branch enhances HttpClient in several
+key areas and includes several notable features and improvements:
+
+* New facade API for HttpClient based on the concept of a fluent interface. The fluent API exposes
+  only the most fundamental functions of HttpClient and is intended for relatively simple use cases
+  that do not require the full flexibility of HttpClient. However, the fluent API almost fully
+  relieves the users from having to deal with connection management and resource deallocation.
+
+* Redesigned and rewritten connection management code. As of release 4.2 HttpClient will be using
+  pooling connection manager per default.
+
+* Enhanced HTTP authentication API that enables HttpClient to handle more complex authentication
+  scenarios. HttpClient 4.2 is now capable of making use of multiple authentication challenges
+  and retry authentication with a fall-back scheme in case the primary one fails. This can be
+  important for compatibility with Microsoft products that are often configured to use
+  SPNEGO/Kerberos as the preferred authentication scheme.
+
+Please note that new features included in this release are still considered experimental and
+their API may change in the future ALPHA releases.
+
+Changelog
+-------------------
+
+* [HTTPCLIENT-1128] SystemDefaultHttpClient (HttpClient implementation initialized using system
+  properties).
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1135] RandomAccessFile mode 'w' used by HttpClientCache is not valid.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1131] HttpClient to authenticate preemptively using BASIC scheme if a userinfo
+  attribute is specified in the request URI.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1134] make BasicResponseHandler consume response content in case of an unsuccessful
+  result (status code >= 300).
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1132] ProxyClient implementation.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1127] fixed dead-lock between SingleClientConnManager and AbstractPooledConnAdapter.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1107] Auth framework redesign.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1116] ResponseCachingPolicy uses integers for sizes
+  Contributed by Greg Bowyer <gbowyer at fastmail.co.uk >
+
+* [HTTPCLIENT-1123] Support for pluggable DNS resolvers.
+  Contributed by Alin Vasile <alinachegalati at yahoo dot com>
+
+* [HTTPCLIENT-1120] DefaultHttpRequestRetryHandler#retryRequest should not retry aborted requests.
+  Contributed by Alin Vasile <alinachegalati at yahoo dot com>
+
+* Support for auth-int qop (quality of protection) option in Digest auth scheme.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1076] Fluent facade API (Google summer of code 2011 project).
+  Contributed by Xu Lilu <cookieme at gmail.com>
+
+* UriBuilder implementation.
+  Contributed by Xu Lilu <cookieme at gmail.com>
+
+* Redesign of connection management classes based on new pooling components from HttpCore.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1111] Added #prepareSocket method to SSLSocketFactory.
+  Contributed by Pasi Eronen <pe at iki.fi>
+
+* Added #reset() and #releaseConnection() methods to HttpRequestBase.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1105] AutoRetryHttpClient: built-in way to do auto-retry for certain status codes.
+  Contributed by Dan Checkoway <dcheckoway at gmail.com>
+
+* [HTTPCLIENT-1094] Digest auth scheme refactoring.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Lax implementation of RedirectStrategy.
+  Contributed by Bartosz Firyn <songo.bercik at interia.pl>
+
+* [HTTPCLIENT-1044] HttpRequestRetryHandler implementation compliant with the definition of
+  idempotent methods given in the RFC 2616.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+Release 4.1.2
+-------------------
+
+The HttpClient 4.1.2 is a bug fix release that addresses a number of non-critical issues reported 
+since release 4.1.1.
+
+* [HTTPCLIENT-1100] Missing Content-Length header makes cached entry invalid
+  Contributed by Bart Robeyns <bart dot robeyns at gmail dot com>
+
+* [HTTPCLIENT-1098] Avoid expensive reverse DNS lookup on connect timeout exception. 
+  Contributed by Thomas Boettcher <tboett at gmail.com>
+
+* [HTTPCLIENT-1097] BrowserCompatHostnameVerifier and StrictHostnameVerifier should handle 
+  wildcards in SSL certificates better.
+  Contributed by Sebastian Bazley <sebb at apache.org>
+
+* [HTTPCLIENT-1092] If ClientPNames.VIRTUAL_HOST does not provide the port, derive it from the 
+  current request.
+  Contributed by Sebastian Bazley <sebb at apache.org>
+
+* [HTTPCLIENT-1087] NTLM proxy authentication fails on retry if the underlying connection is closed
+  as a result of a target authentication failure.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1079] Fixed Kerberos cross-realm support
+  Contributed by Michael Osipov <1983-01-06 at gmx.net> 
+
+* [HTTPCLIENT-1078] Decompressing entities (DeflateDecompressingEntity, GzipDecompressingEntity) 
+  do not close content stream in #writeTo() method.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1075] Decompressing entities (DeflateDecompressingEntity, GzipDecompressingEntity) 
+  do not correctly handle content streaming.
+  Contributed by James Abley <james.abley at gmail.com> 
+
+* [HTTPCLIENT-1051] Avoid reverse DNS lookups when opening SSL connections by IP address.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
 Release 4.1.1
 -------------------
 
-The HttpClient 4.1.1 is a bug fix release that addresses a number of issues reported since 
+HttpClient v4.1.1 is a bug fix release that addresses a number of issues reported since 
 release 4.1, including one critical security issue (HTTPCLIENT-1061). All users of HttpClient 4.0.x 
 and 4.1 are strongly encouraged to upgrade.
 
diff --git a/fluent-hc/pom.xml b/fluent-hc/pom.xml
new file mode 100644
index 0000000..d1983e9
--- /dev/null
+++ b/fluent-hc/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+   distributed with this work for additional information
+   regarding copyright ownership.  The ASF licenses this file
+   to you 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.
+   ====================================================================
+
+   This software consists of voluntary contributions made by many
+   individuals on behalf of the Apache Software Foundation.  For more
+   information on the Apache Software Foundation, please see
+   <http://www.apache.org />.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.httpcomponents</groupId>
+    <artifactId>httpcomponents-client</artifactId>
+    <version>4.2.1</version>
+  </parent>
+  <artifactId>fluent-hc</artifactId>
+  <name>Fluent HttpClient</name>
+  <inceptionYear>2011</inceptionYear>
+  <description>
+   HttpComponents Client fluent API
+  </description>
+  <url>http://hc.apache.org/httpcomponents-client</url>
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+      <version>${project.version}</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <classifier>tests</classifier>
+    </dependency>
+    <!-- direct dependency on logging -->
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- TODO: does not appear to be needed; remove? -->
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    <maven.compile.source>1.5</maven.compile.source>
+    <maven.compile.target>1.5</maven.compile.target>
+    <maven.compile.optimize>true</maven.compile.optimize>
+    <maven.compile.deprecation>true</maven.compile.deprecation>
+  </properties>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+        <includes>
+            <include>**/*.properties</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <source>${maven.compile.source}</source>
+          <target>${maven.compile.target}</target>
+          <optimize>${maven.compile.optimize}</optimize>
+          <showDeprecations>${maven.compile.deprecation}</showDeprecations>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+         <version>${hc.javadoc.version}</version>
+        <configuration>
+          <!-- reduce console output. Can override with -Dquiet=false -->
+          <quiet>true</quiet>
+          <source>1.5</source>
+          <links>
+            <link>http://download.oracle.com/javase/1.5.0/docs/api/</link>
+            <link>http://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/</link>
+            <link>http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/</link>
+          </links>
+        </configuration>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>javadoc</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-jxr-plugin</artifactId>
+         <version>${hc.jxr.version}</version>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-surefire-report-plugin</artifactId>
+         <version>${hc.surefire-report.version}</version>
+      </plugin>
+
+    </plugins>
+  </reporting>
+
+</project>
diff --git a/fluent-hc/src/examples/org/apache/http/client/fluent/FluentAsync.java b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentAsync.java
new file mode 100644
index 0000000..41985b2
--- /dev/null
+++ b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentAsync.java
@@ -0,0 +1,88 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import org.apache.http.concurrent.FutureCallback;
+
+/**
+ * This example demonstrates how the he HttpClient fluent API can be used to execute multiple
+ * requests asynchronously using background threads.
+ */
+public class FluentAsync {
+
+    public static void main(String[] args)throws Exception {
+        // Use pool of two threads
+        ExecutorService threadpool = Executors.newFixedThreadPool(2);
+        Async async = Async.newInstance().use(threadpool);
+        
+        Request[] requests = new Request[] {
+                Request.Get("http://www.google.com/"),
+                Request.Get("http://www.yahoo.com/"),
+                Request.Get("http://www.apache.com/"),
+                Request.Get("http://www.apple.com/")
+        };
+
+        
+        Queue<Future<Content>> queue = new LinkedList<Future<Content>>();
+        // Execute requests asynchronously
+        for (final Request request: requests) {
+            Future<Content> future = async.execute(request, new FutureCallback<Content>() {
+                
+                public void failed(final Exception ex) {
+                    System.out.println(ex.getMessage() + ": " + request);
+                }
+                
+                public void completed(final Content content) {
+                    System.out.println("Request completed: " + request);
+                }
+                
+                public void cancelled() {
+                }
+                
+            });
+            queue.add(future);
+        }
+
+        while(!queue.isEmpty()) {
+            Future<Content> future = queue.remove();
+            try {
+                future.get();
+            } catch (ExecutionException ex) {
+            }
+        }
+        System.out.println("Done");
+        threadpool.shutdown();
+    }
+
+}
diff --git a/fluent-hc/src/examples/org/apache/http/client/fluent/FluentExecutor.java b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentExecutor.java
new file mode 100644
index 0000000..30a4b44
--- /dev/null
+++ b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentExecutor.java
@@ -0,0 +1,72 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.File;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpVersion;
+import org.apache.http.entity.ContentType;
+
+/**
+ * This example demonstrates how the he HttpClient fluent API can be used to execute multiple
+ * requests within the same security context. The Executor class maintains a common context shared
+ * by all requests executed with it. The Executor is thread-safe and can be used to execute
+ * requests concurrently from multiple threads of execution.
+ */
+public class FluentExecutor {
+
+    public static void main(String[] args)throws Exception {
+        Executor executor = Executor.newInstance()
+                .auth(new HttpHost("somehost"), "username", "password")
+                .auth(new HttpHost("myproxy", 8080), "username", "password")
+                .authPreemptive(new HttpHost("myproxy", 8080));
+
+        // Execute a GET with timeout settings and return response content as String.
+        executor.execute(Request.Get("http://somehost/")
+                .connectTimeout(1000)
+                .socketTimeout(1000)
+                ).returnContent().asString();
+
+        // Execute a POST with the 'expect-continue' handshake, using HTTP/1.1,
+        // containing a request body as String and return response content as byte array.
+        executor.execute(Request.Post("http://somehost/do-stuff")
+                .useExpectContinue()
+                .version(HttpVersion.HTTP_1_1)
+                .bodyString("Important stuff", ContentType.DEFAULT_TEXT)
+                ).returnContent().asBytes();
+
+        // Execute a POST with a custom header through the proxy containing a request body
+        // as an HTML form and save the result to the file
+        executor.execute(Request.Post("http://somehost/some-form")
+                .addHeader("X-Custom-header", "stuff")
+                .viaProxy(new HttpHost("myproxy", 8080))
+                .bodyForm(Form.form().add("username", "vip").add("password", "secret").build())
+                ).saveContent(new File("result.dump"));
+    }
+
+}
diff --git a/fluent-hc/src/examples/org/apache/http/client/fluent/FluentQuickStart.java b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentQuickStart.java
new file mode 100644
index 0000000..74c7394
--- /dev/null
+++ b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentQuickStart.java
@@ -0,0 +1,42 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+public class FluentQuickStart {
+
+    public static void main(String[] args) throws Exception {
+        // The fluent API relieves the user from having to deal with manual 
+        // deallocation of system resources at the cost of having to buffer 
+        // response content in memory in some cases.
+        
+        Request.Get("http://targethost/homepage")
+            .execute().returnContent();
+        Request.Post("http://targethost/login")
+            .bodyForm(Form.form().add("username",  "vip").add("password",  "secret").build())
+            .execute().returnContent();
+    }
+}
diff --git a/fluent-hc/src/examples/org/apache/http/client/fluent/FluentRequests.java b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentRequests.java
new file mode 100644
index 0000000..70b9ca9
--- /dev/null
+++ b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentRequests.java
@@ -0,0 +1,64 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.File;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpVersion;
+import org.apache.http.entity.ContentType;
+
+/**
+ * This example demonstrates basics of request execution with the HttpClient fluent API.
+ */
+public class FluentRequests {
+
+    public static void main(String[] args)throws Exception {
+        // Execute a GET with timeout settings and return response content as String.
+        Request.Get("http://somehost/")
+                .connectTimeout(1000)
+                .socketTimeout(1000)
+                .execute().returnContent().asString();
+
+        // Execute a POST with the 'expect-continue' handshake, using HTTP/1.1,
+        // containing a request body as String and return response content as byte array.
+        Request.Post("http://somehost/do-stuff")
+                .useExpectContinue()
+                .version(HttpVersion.HTTP_1_1)
+                .bodyString("Important stuff", ContentType.DEFAULT_TEXT)
+                .execute().returnContent().asBytes();
+
+        // Execute a POST with a custom header through the proxy containing a request body
+        // as an HTML form and save the result to the file
+        Request.Post("http://somehost/some-form")
+                .addHeader("X-Custom-header", "stuff")
+                .viaProxy(new HttpHost("myproxy", 8080))
+                .bodyForm(Form.form().add("username", "vip").add("password", "secret").build())
+                .execute().saveContent(new File("result.dump"));
+    }
+
+}
diff --git a/fluent-hc/src/examples/org/apache/http/client/fluent/FluentResponseHandling.java b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentResponseHandling.java
new file mode 100644
index 0000000..6e6071f
--- /dev/null
+++ b/fluent-hc/src/examples/org/apache/http/client/fluent/FluentResponseHandling.java
@@ -0,0 +1,92 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.entity.ContentType;
+import org.apache.http.protocol.HTTP;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+/**
+ * This example demonstrates how the HttpClient fluent API can be used to handle HTTP responses
+ * without buffering content body in memory.
+ */
+public class FluentResponseHandling {
+
+    public static void main(String[] args)throws Exception {
+        Document result = Request.Get("http://somehost/content")
+                .execute().handleResponse(new ResponseHandler<Document>() {
+
+            public Document handleResponse(final HttpResponse response) throws IOException {
+                StatusLine statusLine = response.getStatusLine();
+                HttpEntity entity = response.getEntity();
+                if (statusLine.getStatusCode() >= 300) {
+                    throw new HttpResponseException(
+                            statusLine.getStatusCode(),
+                            statusLine.getReasonPhrase());
+                }
+                if (entity == null) {
+                    throw new ClientProtocolException("Response contains no content");
+                }
+                DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
+                try {
+                    DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
+                    ContentType contentType = ContentType.getOrDefault(entity);
+                    if (!contentType.equals(ContentType.APPLICATION_XML)) {
+                        throw new ClientProtocolException("Unexpected content type:" + contentType);
+                    }
+                    Charset charset = contentType.getCharset();
+                    if (charset == null) {
+                        charset = HTTP.DEF_CONTENT_CHARSET;
+                    }
+                    return docBuilder.parse(entity.getContent(), charset.name());
+                } catch (ParserConfigurationException ex) {
+                    throw new IllegalStateException(ex);
+                } catch (SAXException ex) {
+                    throw new ClientProtocolException("Malformed XML document", ex);
+                }
+            }
+
+            });
+        // Do something useful with the result
+        System.out.println(result);
+    }
+
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Async.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Async.java
new file mode 100644
index 0000000..9282ae7
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Async.java
@@ -0,0 +1,119 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.util.concurrent.Future;
+
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+
+public class Async {
+
+    private Executor executor;
+    private java.util.concurrent.Executor concurrentExec;
+
+    public static Async newInstance() {
+        return new Async();
+    }
+
+    Async() {
+        super();
+    }
+
+    public Async use(final Executor executor) {
+        this.executor = executor;
+        return this;
+    }
+    
+    public Async use(final java.util.concurrent.Executor concurrentExec) {
+        this.concurrentExec = concurrentExec;
+        return this;
+    }
+    
+    static class ExecRunnable<T> implements Runnable {
+        
+        private final BasicFuture<T> future;
+        private final Request request;
+        private final Executor executor;
+        private final ResponseHandler<T> handler;
+        
+        ExecRunnable(
+                final BasicFuture<T> future, 
+                final Request request, 
+                final Executor executor, 
+                final ResponseHandler<T> handler) {
+            super();
+            this.future = future;
+            this.request = request;
+            this.executor = executor;
+            this.handler = handler;
+        }
+
+        public void run() {
+            try {
+                Response response = this.executor.execute(this.request);
+                T result = response.handleResponse(this.handler);
+                this.future.completed(result);
+            } catch (Exception ex) {
+                this.future.failed(ex);
+            }
+        }
+        
+    }
+    
+    public <T> Future<T> execute(
+            final Request request, final ResponseHandler<T> handler, final FutureCallback<T> callback) {
+        BasicFuture<T> future = new BasicFuture<T>(callback);
+        ExecRunnable<T> runnable = new ExecRunnable<T>(
+                future,
+                request,
+                this.executor != null ? this.executor : Executor.newInstance(),
+                handler);
+        if (this.concurrentExec != null) {
+            this.concurrentExec.execute(runnable);
+        } else {
+            Thread t = new Thread(runnable);
+            t.setDaemon(true);
+            t.start();
+        }
+        return future;
+    }
+    
+    public <T> Future<T> execute(final Request request, final ResponseHandler<T> handler) {
+        return execute(request, handler, null);
+    }
+
+    public Future<Content> execute(final Request request, final FutureCallback<Content> callback) {
+        return execute(request, new ContentResponseHandler(), callback);
+    }
+    
+    public Future<Content> execute(final Request request) {
+        return execute(request, new ContentResponseHandler(), null);
+    }
+    
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java
new file mode 100644
index 0000000..7d1ff5f
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Content.java
@@ -0,0 +1,79 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+
+import org.apache.http.entity.ContentType;
+import org.apache.http.protocol.HTTP;
+
+public class Content {
+
+    public static final Content NO_CONTENT = new Content(new byte[] {}, ContentType.DEFAULT_BINARY); 
+    
+    private final byte[] raw;
+    private final ContentType type;
+
+    Content(final byte[] raw, final ContentType type) {
+        super();
+        this.raw = raw;
+        this.type = type;
+    }
+
+    public ContentType getType() {
+        return this.type;
+    }
+
+    public byte[] asBytes() {
+        return this.raw.clone();
+    }
+
+    public String asString() {
+        Charset charset = this.type.getCharset();
+        if (charset == null) {
+            charset = HTTP.DEF_CONTENT_CHARSET;
+        }
+        try {
+            return new String(this.raw, charset.name());
+        } catch (UnsupportedEncodingException ex) {
+            return new String(this.raw);
+        }
+    }
+
+    public InputStream asStream() {
+        return new ByteArrayInputStream(this.raw);
+    }
+
+    @Override
+    public String toString() {
+        return asString();
+    }
+    
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/ContentResponseHandler.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/ContentResponseHandler.java
new file mode 100644
index 0000000..acc2249
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/ContentResponseHandler.java
@@ -0,0 +1,59 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.entity.ContentType;
+import org.apache.http.util.EntityUtils;
+
+class ContentResponseHandler implements ResponseHandler<Content> {
+
+    public Content handleResponse(
+            final HttpResponse response) throws ClientProtocolException, IOException {
+        StatusLine statusLine = response.getStatusLine();
+        HttpEntity entity = response.getEntity();
+        if (statusLine.getStatusCode() >= 300) {
+            throw new HttpResponseException(statusLine.getStatusCode(),
+                    statusLine.getReasonPhrase());
+        }
+        if (entity != null) {
+            return new Content(
+                    EntityUtils.toByteArray(entity),
+                    ContentType.getOrDefault(entity));
+        } else {
+            return Content.NO_CONTENT;
+        }
+    }
+
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java
new file mode 100644
index 0000000..9c2cef4
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java
@@ -0,0 +1,199 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.ChallengeState;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.NTCredentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.scheme.SchemeSocketFactory;
+import org.apache.http.conn.ssl.SSLInitializationException;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.BasicAuthCache;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
+import org.apache.http.protocol.BasicHttpContext;
+
+public class Executor {
+
+    final static PoolingClientConnectionManager CONNMGR;
+    final static DefaultHttpClient CLIENT;
+
+    static {
+        SchemeRegistry schemeRegistry = new SchemeRegistry();
+        SchemeSocketFactory plain = PlainSocketFactory.getSocketFactory();
+        schemeRegistry.register(new Scheme("http", 80, plain));
+        SchemeSocketFactory ssl = null;
+        try {
+            ssl = SSLSocketFactory.getSystemSocketFactory();
+        } catch (SSLInitializationException ex) {
+            SSLContext sslcontext;
+            try {
+                sslcontext = SSLContext.getInstance(SSLSocketFactory.TLS);
+                sslcontext.init(null, null, null);
+                ssl = new SSLSocketFactory(sslcontext);
+            } catch (SecurityException ignore) {
+            } catch (KeyManagementException ignore) {
+            } catch (NoSuchAlgorithmException ignore) {
+            }
+        }
+        if (ssl != null) {
+            schemeRegistry.register(new Scheme("https", 443, ssl));
+        }
+        CONNMGR = new PoolingClientConnectionManager(schemeRegistry);
+        CONNMGR.setDefaultMaxPerRoute(100);
+        CONNMGR.setMaxTotal(200);
+        CLIENT = new DefaultHttpClient(CONNMGR);
+    }
+
+    public static Executor newInstance() {
+        return new Executor(CLIENT);
+    }
+
+    public static Executor newInstance(final HttpClient httpclient) {
+        return new Executor(httpclient != null ? httpclient : CLIENT);
+    }
+
+    private final HttpClient httpclient;
+    private final BasicHttpContext localContext;
+    private final AuthCache authCache;
+
+    private CredentialsProvider credentialsProvider;
+    private CookieStore cookieStore;
+
+    Executor(final HttpClient httpclient) {
+        super();
+        this.httpclient = httpclient;
+        this.localContext = new BasicHttpContext();
+        this.authCache = new BasicAuthCache();
+    }
+
+    public Executor auth(final AuthScope authScope, final Credentials creds) {
+        if (this.credentialsProvider == null) {
+            this.credentialsProvider = new BasicCredentialsProvider();
+        }
+        this.credentialsProvider.setCredentials(authScope, creds);
+        return this;
+    }
+
+    public Executor auth(final HttpHost host, final Credentials creds) {
+        AuthScope authScope = host != null ? new AuthScope(host) : AuthScope.ANY;
+        return auth(authScope, creds);
+    }
+
+    public Executor authPreemptive(final HttpHost host) {
+        this.authCache.put(host, new BasicScheme(ChallengeState.TARGET));
+        return this;
+    }
+
+    public Executor authPreemptiveProxy(final HttpHost host) {
+        this.authCache.put(host, new BasicScheme(ChallengeState.PROXY));
+        return this;
+    }
+
+    public Executor auth(final Credentials cred) {
+        return auth(AuthScope.ANY, cred);
+    }
+
+    public Executor auth(final String username, final String password) {
+        return auth(new UsernamePasswordCredentials(username, password));
+    }
+
+    public Executor auth(final String username, final String password,
+            final String workstation, final String domain) {
+        return auth(new NTCredentials(username, password, workstation, domain));
+    }
+
+    public Executor auth(final HttpHost host,
+            final String username, final String password) {
+        return auth(host, new UsernamePasswordCredentials(username, password));
+    }
+
+    public Executor auth(final HttpHost host,
+            final String username, final String password,
+            final String workstation, final String domain) {
+        return auth(host, new NTCredentials(username, password, workstation, domain));
+    }
+
+    public Executor clearAuth() {
+        if (this.credentialsProvider != null) {
+            this.credentialsProvider.clear();
+        }
+        return this;
+    }
+
+    public Executor cookieStore(final CookieStore cookieStore) {
+        this.cookieStore = cookieStore;
+        return this;
+    }
+
+    public Executor clearCookies() {
+        if (this.cookieStore != null) {
+            this.cookieStore.clear();
+        }
+        return this;
+    }
+
+    public Response execute(
+            final Request request) throws ClientProtocolException, IOException {
+        this.localContext.setAttribute(ClientContext.CREDS_PROVIDER, this.credentialsProvider);
+        this.localContext.setAttribute(ClientContext.AUTH_CACHE, this.authCache);
+        this.localContext.setAttribute(ClientContext.COOKIE_STORE, this.cookieStore);
+        HttpRequestBase httprequest = request.getHttpRequest();
+        httprequest.reset();
+        return new Response(this.httpclient.execute(httprequest, this.localContext));
+    }
+
+    public static void registerScheme(final Scheme scheme) {
+        CONNMGR.getSchemeRegistry().register(scheme);
+    }
+
+    public static void unregisterScheme(final String name) {
+        CONNMGR.getSchemeRegistry().unregister(name);
+    }
+
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Form.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Form.java
new file mode 100644
index 0000000..4d87d7f
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Form.java
@@ -0,0 +1,57 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicNameValuePair;
+
+public class Form {
+
+    private final List<NameValuePair> params;
+
+    public static Form form() {
+        return new Form();
+    }
+
+    Form() {
+        super();
+        this.params = new ArrayList<NameValuePair>();
+    }
+
+    public Form add(final String name, final String value) {
+        this.params.add(new BasicNameValuePair(name, value));
+        return this;
+    }
+
+    public List<NameValuePair> build() {
+        return new ArrayList<NameValuePair>(this.params);
+    }
+
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/HttpHeader.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/HttpHeader.java
new file mode 100644
index 0000000..85aa744
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/HttpHeader.java
@@ -0,0 +1,38 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+class HttpHeader {
+
+    public static final String CONTENT_LENGTH = "Content-Length";
+    public static final String DATE = "Date";
+    public static final String CACHE_CONTROL = "Cache-Control";
+    public static final String CONTENT_TYPE = "Content-Type";
+    public static final String IF_MODIFIED_SINCE = "If-Modified-Since";
+    public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
+
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Request.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Request.java
new file mode 100644
index 0000000..4e96125
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Request.java
@@ -0,0 +1,310 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpVersion;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpOptions;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.client.methods.HttpTrace;
+import org.apache.http.conn.params.ConnRoutePNames;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.FileEntity;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HTTP;
+
+public class Request {
+
+    public static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
+    public static final Locale DATE_LOCALE = Locale.US;
+    public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("GMT");
+
+    private final HttpRequestBase request;
+    private final HttpParams localParams;
+
+    private SimpleDateFormat dateFormatter;
+
+    public static Request Get(final URI uri) {
+        return new Request(new HttpGet(uri));
+    }
+
+    public static Request Get(final String uri) {
+        return new Request(new HttpGet(uri));
+    }
+
+    public static Request Head(final URI uri) {
+        return new Request(new HttpHead(uri));
+    }
+
+    public static Request Head(final String uri) {
+        return new Request(new HttpHead(uri));
+    }
+
+    public static Request Post(final URI uri) {
+        return new Request(new HttpPost(uri));
+    }
+
+    public static Request Post(final String uri) {
+        return new Request(new HttpPost(uri));
+    }
+
+    public static Request Put(final URI uri) {
+        return new Request(new HttpPut(uri));
+    }
+
+    public static Request Put(final String uri) {
+        return new Request(new HttpPut(uri));
+    }
+
+    public static Request Trace(final URI uri) {
+        return new Request(new HttpTrace(uri));
+    }
+
+    public static Request Trace(final String uri) {
+        return new Request(new HttpTrace(uri));
+    }
+
+    public static Request Delete(final URI uri) {
+        return new Request(new HttpDelete(uri));
+    }
+
+    public static Request Delete(final String uri) {
+        return new Request(new HttpDelete(uri));
+    }
+
+    public static Request Options(final URI uri) {
+        return new Request(new HttpOptions(uri));
+    }
+
+    public static Request Options(final String uri) {
+        return new Request(new HttpOptions(uri));
+    }
+
+    Request(final HttpRequestBase request) {
+        super();
+        this.request = request;
+        this.localParams = request.getParams();
+    }
+
+    HttpRequestBase getHttpRequest() {
+        return this.request;
+    }
+
+    public Response execute() throws ClientProtocolException, IOException {
+        return new Response(Executor.CLIENT.execute(this.request));
+    }
+
+    public void abort() throws UnsupportedOperationException {
+        this.request.abort();
+    }
+
+    //// HTTP header operations
+
+    public Request addHeader(final Header header) {
+        this.request.addHeader(header);
+        return this;
+    }
+
+    public Request addHeader(final String name, final String value) {
+        this.request.addHeader(name, value);
+        return this;
+    }
+
+    public Request removeHeader(final Header header) {
+        this.request.removeHeader(header);
+        return this;
+    }
+
+    public Request removeHeaders(final String name) {
+        this.request.removeHeaders(name);
+        return this;
+    }
+
+    public Request setHeaders(final Header[] headers) {
+        this.request.setHeaders(headers);
+        return this;
+    }
+
+    public Request setCacheControl(String cacheControl) {
+        this.request.setHeader(HttpHeader.CACHE_CONTROL, cacheControl);
+        return this;
+    }
+
+    private SimpleDateFormat getDateFormat() {
+        if (this.dateFormatter == null) {
+            this.dateFormatter = new SimpleDateFormat(DATE_FORMAT, DATE_LOCALE);
+            this.dateFormatter.setTimeZone(TIME_ZONE);
+        }
+        return this.dateFormatter;
+    }
+
+    public Request setDate(final Date date) {
+        this.request.setHeader(HttpHeader.DATE, getDateFormat().format(date));
+        return this;
+    }
+
+    public Request setIfModifiedSince(final Date date) {
+        this.request.setHeader(HttpHeader.IF_MODIFIED_SINCE, getDateFormat().format(date));
+        return this;
+    }
+
+    public Request setIfUnmodifiedSince(final Date date) {
+        this.request.setHeader(HttpHeader.IF_UNMODIFIED_SINCE, getDateFormat().format(date));
+        return this;
+    }
+
+    //// HTTP config parameter operations
+
+    public Request config(final String param, final Object object) {
+        this.localParams.setParameter(param, object);
+        return this;
+    }
+
+    public Request removeConfig(final String param) {
+        this.localParams.removeParameter(param);
+        return this;
+    }
+
+    //// HTTP protocol parameter operations
+
+    public Request version(final HttpVersion version) {
+        return config(CoreProtocolPNames.PROTOCOL_VERSION, version);
+    }
+
+    public Request elementCharset(final String charset) {
+        return config(CoreProtocolPNames.HTTP_ELEMENT_CHARSET, charset);
+    }
+
+    public Request useExpectContinue() {
+        return config(CoreProtocolPNames.USE_EXPECT_CONTINUE, true);
+    }
+
+    public Request userAgent(final String agent) {
+        return config(CoreProtocolPNames.USER_AGENT, agent);
+    }
+
+    //// HTTP connection parameter operations
+
+    public Request socketTimeout(int timeout) {
+        return config(CoreConnectionPNames.SO_TIMEOUT, timeout);
+    }
+
+    public Request connectTimeout(int timeout) {
+        return config(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
+    }
+
+    public Request staleConnectionCheck(boolean b) {
+        return config(CoreConnectionPNames.STALE_CONNECTION_CHECK, b);
+    }
+
+    //// HTTP connection route operations
+
+    public Request viaProxy(final HttpHost proxy) {
+        return config(ConnRoutePNames.DEFAULT_PROXY, proxy);
+    }
+
+    //// HTTP entity operations
+
+    public Request body(final HttpEntity entity) {
+        if (this.request instanceof HttpEntityEnclosingRequest) {
+            ((HttpEntityEnclosingRequest) this.request).setEntity(entity);
+        } else {
+            throw new IllegalStateException(this.request.getMethod()
+                    + " request cannot enclose an entity");
+        }
+        return this;
+    }
+
+    public Request bodyForm(final Iterable <? extends NameValuePair> formParams, final Charset charset) {
+        return body(new UrlEncodedFormEntity(formParams, charset));
+    }
+
+    public Request bodyForm(final Iterable <? extends NameValuePair> formParams) {
+        return bodyForm(formParams, HTTP.DEF_CONTENT_CHARSET);
+    }
+
+    public Request bodyForm(final NameValuePair... formParams) {
+        return bodyForm(Arrays.asList(formParams), HTTP.DEF_CONTENT_CHARSET);
+    }
+
+    public Request bodyString(final String s, final ContentType contentType) {
+        return body(new StringEntity(s, contentType));
+    }
+
+    public Request bodyFile(final File file, final ContentType contentType) {
+        return body(new FileEntity(file, contentType));
+    }
+
+    public Request bodyByteArray(final byte[] b) {
+        return body(new ByteArrayEntity(b));
+    }
+
+    public Request bodyByteArray(final byte[] b, int off, int len) {
+        return body(new ByteArrayEntity(b, off, len));
+    }
+
+    public Request bodyStream(final InputStream instream) {
+        return body(new InputStreamEntity(instream, -1));
+    }
+
+    public Request bodyStream(final InputStream instream, final ContentType contentType) {
+        return body(new InputStreamEntity(instream, -1, contentType));
+    }
+
+    @Override
+    public String toString() {
+        return this.request.getRequestLine().toString();
+    }
+    
+}
diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Response.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Response.java
new file mode 100644
index 0000000..3d7345b
--- /dev/null
+++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Response.java
@@ -0,0 +1,122 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.fluent;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.util.EntityUtils;
+
+public class Response {
+
+    private final HttpResponse response;
+    private boolean consumed;
+
+    Response(final HttpResponse response) {
+        super();
+        this.response = response;
+    }
+
+    private void assertNotConsumed() {
+        if (this.consumed) {
+            throw new IllegalStateException("Response content has been already consumed");
+        }
+    }
+
+    private void dispose() {
+        if (this.consumed) {
+            return;
+        }
+        try {
+            EntityUtils.consume(this.response.getEntity());
+        } catch (Exception ignore) {
+        } finally {
+            this.consumed = true;
+        }
+    }
+
+    public void discardContent() {
+        dispose();
+    }
+
+    public <T> T handleResponse(
+            final ResponseHandler<T> handler) throws ClientProtocolException, IOException {
+        assertNotConsumed();
+        try {
+            return handler.handleResponse(this.response);
+        } finally {
+            dispose();
+        }
+    }
+
+    public Content returnContent() throws ClientProtocolException, IOException {
+        return handleResponse(new ContentResponseHandler());
+    }
+
+    public HttpResponse returnResponse() throws IOException {
+        assertNotConsumed();
+        try {
+            HttpEntity entity = this.response.getEntity();
+            if (entity != null) {
+                this.response.setEntity(new ByteArrayEntity(EntityUtils.toByteArray(entity),
+                                ContentType.getOrDefault(entity)));
+            }
+            return this.response;
+        } finally {
+            this.consumed = true;
+        }
+    }
+
+    public void saveContent(final File file) throws IOException {
+        assertNotConsumed();
+        StatusLine statusLine = response.getStatusLine();
+        if (statusLine.getStatusCode() >= 300) {
+            throw new HttpResponseException(statusLine.getStatusCode(),
+                    statusLine.getReasonPhrase());
+        }
+        FileOutputStream out = new FileOutputStream(file);
+        try {
+            HttpEntity entity = this.response.getEntity();
+            if (entity != null) {
+                entity.writeTo(out);
+            }
+        } finally {
+            this.consumed = true;
+            out.close();
+        }
+    }
+
+}
diff --git a/fluent-hc/src/site/apt/index.apt b/fluent-hc/src/site/apt/index.apt
new file mode 100644
index 0000000..2e8b7c6
--- /dev/null
+++ b/fluent-hc/src/site/apt/index.apt
@@ -0,0 +1,101 @@
+~~ ====================================================================
+~~ Licensed to the Apache Software Foundation (ASF) under one
+~~ or more contributor license agreements.  See the NOTICE file
+~~ distributed with this work for additional information
+~~ regarding copyright ownership.  The ASF licenses this file
+~~ to you 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.
+~~ ====================================================================
+~~
+~~ This software consists of voluntary contributions made by many
+~~ individuals on behalf of the Apache Software Foundation.  For more
+~~ information on the Apache Software Foundation, please see
+~~ <http://www.apache.org/>.
+
+    ----------
+    HttpComponents Fluent HC Module
+    ----------
+    ----------
+    ----------
+
+Fluent HC
+
+		This module provides an easy to use facade API for HttpClient based on the concept of
+		a fluent interface. Fluent facade API exposes only the most fundamental functions of
+        HttpClient and is indended for simple use cases that do not require the full flexibility 
+        of HttpClient. For instance, fluent facade API relieves the users from having to deal with 
+        connection management and resource deallocation. In most cases, though, this comes at
+        a price of having to buffer content of response messages in memory. However, users can 
+        still use custom response handlers to process response streams of arbitrary length.
+
+----------------------------------
+String result1 = Request.Get("http://somehost/")
+        .version(HttpVersion.HTTP_1_1)
+        .connectTimeout(1000)
+        .socketTimeout(1000)
+        .viaProxy(new HttpHost("myproxy", 8080))
+        .execute().returnContent().asString();
+
+String result2 = Request.Post("http://somehost/do-stuff")
+        .version(HttpVersion.HTTP_1_1)
+        .useExpectContinue()
+        .viaProxy(new HttpHost("myproxy", 8080))
+        .bodyForm(Form.form().add("username", "vip").add("password", "secret").build())
+        .execute().returnContent().asString();
+----------------------------------
+
+----------------------------------
+Document result3 = Request.Get("http://somehost/content")
+    .execute().handleResponse(new ResponseHandler<Document>() {
+
+    public Document handleResponse(final HttpResponse response) throws IOException {
+        StatusLine statusLine = response.getStatusLine();
+        HttpEntity entity = response.getEntity();
+        if (statusLine.getStatusCode() >= 300) {
+            throw new HttpResponseException(
+                    statusLine.getStatusCode(),
+                    statusLine.getReasonPhrase());
+        }
+        if (entity == null) {
+            throw new ClientProtocolException("Response contains no content");
+        }
+        DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
+        try {
+            DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
+            ContentType contentType = ContentType.getOrDefault(entity);
+            if (!contentType.equals(ContentType.APPLICATION_XML)) {
+                throw new ClientProtocolException("Unexpected content type:" + contentType);
+            }
+            String charset = contentType.getCharset();
+            if (charset == null) {
+                charset = HTTP.DEFAULT_CONTENT_CHARSET;
+            }
+            return docBuilder.parse(entity.getContent(), charset);
+        } catch (ParserConfigurationException ex) {
+            throw new IllegalStateException(ex);
+        } catch (SAXException ex) {
+            throw new ClientProtocolException("Malformed XML document", ex);
+        }
+    }
+
+});
+----------------------------------
+
+    {{{./apidocs/index.html}Javadocs}}
+
+    {{{./xref/index.html}Project sources}}
+
+    {{{./dependencies.html}Dependencies}}
+
+    {{{./issue-tracking.html}Issue Tracking}}
+
diff --git a/httpclient-cache/src/site/resources/css/site.css b/fluent-hc/src/site/resources/css/site.css
similarity index 100%
copy from httpclient-cache/src/site/resources/css/site.css
copy to fluent-hc/src/site/resources/css/site.css
diff --git a/fluent-hc/src/site/site.xml b/fluent-hc/src/site/site.xml
new file mode 100644
index 0000000..cc8f293
--- /dev/null
+++ b/fluent-hc/src/site/site.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+   ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+   distributed with this work for additional information
+   regarding copyright ownership.  The ASF licenses this file
+   to you 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.
+   ====================================================================
+
+   This software consists of voluntary contributions made by many
+   individuals on behalf of the Apache Software Foundation.  For more
+   information on the Apache Software Foundation, please see
+   <http://www.apache.org/>.
+ -->
+
+<project xmlns="http://maven.apache.org/DECORATION/1.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 http://maven.apache.org/xsd/decoration-1.0.0.xsd"
+	name="HttpClient">
+
+  <body>
+    <menu name="HttpClient Overview">
+      <item name="Description" href="../index.html"/>
+      <item name="Quick Start" href="../quickstart.html"/>
+    </menu>
+    <menu ref="modules" />
+    <menu ref="reports"/>      
+  </body>
+</project>
diff --git a/httpclient-benchmark/pom.xml b/httpclient-benchmark/pom.xml
index 5218876..9ec560a 100644
--- a/httpclient-benchmark/pom.xml
+++ b/httpclient-benchmark/pom.xml
@@ -30,7 +30,7 @@
   <parent>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpcomponents-client</artifactId>
-    <version>4.1.1</version>
+    <version>4.2.1</version>
   </parent>
   <artifactId>httpclient-benchmark</artifactId>
   <name>HttpClient Benchmarks</name>
@@ -60,12 +60,6 @@
       <scope>compile</scope>
     </dependency>
     <dependency>
-      <groupId>org.apache.httpcomponents</groupId>
-      <artifactId>httpcore</artifactId>
-      <version>4.1</version>
-      <scope>compile</scope>
-    </dependency>
-    <dependency>
       <groupId>commons-httpclient</groupId>
       <artifactId>commons-httpclient</artifactId>
       <version>3.1</version>
@@ -74,13 +68,13 @@
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-server</artifactId>
-      <version>7.2.0.v20101020</version>
+      <version>7.3.1.v20110307</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>org.eclipse.jetty</groupId>
       <artifactId>jetty-client</artifactId>
-      <version>7.2.0.v20101020</version>
+      <version>7.3.1.v20110307</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
@@ -98,7 +92,7 @@
     <dependency>
       <groupId>com.ning</groupId>
       <artifactId>async-http-client</artifactId>
-      <version>1.4.0</version>
+      <version>1.6.4</version>
       <scope>compile</scope>
     </dependency>
   </dependencies>
diff --git a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/Benchmark.java b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/Benchmark.java
index ecda9aa..80a47bf 100644
--- a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/Benchmark.java
+++ b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/Benchmark.java
@@ -46,167 +46,168 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
 
 public class Benchmark {
 
-   public static void main(String[] args) throws Exception {
-
-       String ns = System.getProperty("hc.benchmark.n-requests", "200000");
-       String nc = System.getProperty("hc.benchmark.concurrent", "100");
-       String cls = System.getProperty("hc.benchmark.content-len", "2048");
-
-       int n = Integer.parseInt(ns);
-       int c = Integer.parseInt(nc);
-       int contentLen = Integer.parseInt(cls);
-
-       SocketConnector connector = new SocketConnector();
-       connector.setPort(0);
-       connector.setRequestBufferSize(12 * 1024);
-       connector.setResponseBufferSize(12 * 1024);
-       connector.setAcceptors(2);
-       connector.setAcceptQueueSize(c);
-
-       QueuedThreadPool threadpool = new QueuedThreadPool();
-       threadpool.setMinThreads(c);
-       threadpool.setMaxThreads(2000);
-
-       Server server = new Server();
-       server.addConnector(connector);
-       server.setThreadPool(threadpool);
-       server.setHandler(new RandomDataHandler());
-
-       server.start();
-       int port = connector.getLocalPort();
-
-       // Sleep a little
-       Thread.sleep(2000);
-
-       TestHttpAgent[] agents = new TestHttpAgent[] {
-               new TestHttpClient3(),
-               new TestHttpJRE(),
-               new TestHttpCore(),
-               new TestHttpClient4(),
-               new TestJettyHttpClient(),
-               new TestNingHttpClient()
-       };
-
-       byte[] content = new byte[contentLen];
-       int r = Math.abs(content.hashCode());
-       for (int i = 0; i < content.length; i++) {
-           content[i] = (byte) ((r + i) % 96 + 32);
-       }
-
-       URI target1 = new URI("http", null, "localhost", port, "/rnd", "c=" + contentLen, null);
-       URI target2 = new URI("http", null, "localhost", port, "/echo", null, null);
-
-       try {
-           for (TestHttpAgent agent: agents) {
-               agent.init();
-               try {
-                   System.out.println("=================================");
-                   System.out.println("HTTP agent: " + agent.getClientName());
-                   System.out.println("---------------------------------");
-                   System.out.println(n + " GET requests");
-                   System.out.println("---------------------------------");
-
-                   long startTime1 = System.currentTimeMillis();
-                   Stats stats1 = agent.get(target1, n, c);
-                   long finishTime1 = System.currentTimeMillis();
-                   Stats.printStats(target1, startTime1, finishTime1, stats1);
-                   System.out.println("---------------------------------");
-                   System.out.println(n + " POST requests");
-                   System.out.println("---------------------------------");
-
-                   long startTime2 = System.currentTimeMillis();
-                   Stats stats2 = agent.post(target2, content, n, c);
-                   long finishTime2 = System.currentTimeMillis();
-                   Stats.printStats(target2, startTime2, finishTime2, stats2);
-               } finally {
-                   agent.shutdown();
-               }
-               agent.init();
-               System.out.println("---------------------------------");
-           }
-       } finally {
-           server.stop();
-       }
-       server.join();
+    public static void main(String[] args) throws Exception {
+
+        String ns = System.getProperty("hc.benchmark.n-requests", "200000");
+        String nc = System.getProperty("hc.benchmark.concurrent", "20");
+        String cls = System.getProperty("hc.benchmark.content-len", "2048");
+
+        int n = Integer.parseInt(ns);
+        int c = Integer.parseInt(nc);
+        int contentLen = Integer.parseInt(cls);
+
+        SocketConnector connector = new SocketConnector();
+        connector.setPort(0);
+        connector.setRequestBufferSize(12 * 1024);
+        connector.setResponseBufferSize(12 * 1024);
+        connector.setAcceptors(2);
+        connector.setAcceptQueueSize(c);
+
+        QueuedThreadPool threadpool = new QueuedThreadPool();
+        threadpool.setMinThreads(c);
+        threadpool.setMaxThreads(2000);
+
+        Server server = new Server();
+        server.addConnector(connector);
+        server.setThreadPool(threadpool);
+        server.setHandler(new RandomDataHandler());
+
+        server.start();
+        int port = connector.getLocalPort();
+
+        TestHttpAgent[] agents = new TestHttpAgent[] {
+                new TestHttpClient3(),
+                new TestHttpJRE(),
+                new TestHttpCore(),
+                new TestHttpClient4(),
+                new TestJettyHttpClient(),
+                new TestNingHttpClient()
+        };
+
+        byte[] content = new byte[contentLen];
+        int r = Math.abs(content.hashCode());
+        for (int i = 0; i < content.length; i++) {
+            content[i] = (byte) ((r + i) % 96 + 32);
+        }
+
+        URI warmup = new URI("http", null, "localhost", port, "/rnd", "c=2048", null);
+        URI target1 = new URI("http", null, "localhost", port, "/rnd", "c=" + contentLen, null);
+        URI target2 = new URI("http", null, "localhost", port, "/echo", null, null);
+
+        try {
+            for (TestHttpAgent agent: agents) {
+                try {
+                    agent.init();
+                    // Warm up
+                    agent.get(warmup, 500, 2);
+                    // Sleep a little
+                    Thread.sleep(5000);
+
+                    System.out.println("=================================");
+                    System.out.println("HTTP agent: " + agent.getClientName());
+                    System.out.println("---------------------------------");
+                    System.out.println(n + " GET requests");
+                    System.out.println("---------------------------------");
+
+                    long startTime1 = System.currentTimeMillis();
+                    Stats stats1 = agent.get(target1, n, c);
+                    long finishTime1 = System.currentTimeMillis();
+                    Stats.printStats(target1, startTime1, finishTime1, stats1);
+                    System.out.println("---------------------------------");
+                    System.out.println(n + " POST requests");
+                    System.out.println("---------------------------------");
+
+                    long startTime2 = System.currentTimeMillis();
+                    Stats stats2 = agent.post(target2, content, n, c);
+                    long finishTime2 = System.currentTimeMillis();
+                    Stats.printStats(target2, startTime2, finishTime2, stats2);
+                } finally {
+                    agent.shutdown();
+                }
+                System.out.println("---------------------------------");
+            }
+        } finally {
+            server.stop();
+        }
+        server.join();
     }
 
-   static class RandomDataHandler extends AbstractHandler {
-
-       public RandomDataHandler() {
-           super();
-       }
-
-       public void handle(
-               final String target,
-               final Request baseRequest,
-               final HttpServletRequest request,
-               final HttpServletResponse response) throws IOException, ServletException {
-           if (target.equals("/rnd")) {
-               rnd(request, response);
-           } else if (target.equals("/echo")) {
-               echo(request, response);
-           } else {
-               response.setStatus(HttpStatus.NOT_FOUND_404);
-               Writer writer = response.getWriter();
-               writer.write("Target not found: " + target);
-               writer.flush();
-           }
-       }
-
-       private void rnd(
-               final HttpServletRequest request,
-               final HttpServletResponse response) throws IOException, ServletException {
-           int count = 100;
-           String s = request.getParameter("c");
-           try {
-               count = Integer.parseInt(s);
-           } catch (NumberFormatException ex) {
-               response.setStatus(500);
-               Writer writer = response.getWriter();
-               writer.write("Invalid query format: " + request.getQueryString());
-               writer.flush();
-               return;
-           }
-
-           response.setStatus(200);
-           response.setContentLength(count);
-
-           OutputStream outstream = response.getOutputStream();
-           byte[] tmp = new byte[1024];
-           int r = Math.abs(tmp.hashCode());
-           int remaining = count;
-           while (remaining > 0) {
-               int chunk = Math.min(tmp.length, remaining);
-               for (int i = 0; i < chunk; i++) {
-                   tmp[i] = (byte) ((r + i) % 96 + 32);
-               }
-               outstream.write(tmp, 0, chunk);
-               remaining -= chunk;
-           }
-           outstream.flush();
-       }
-
-       private void echo(
-               final HttpServletRequest request,
-               final HttpServletResponse response) throws IOException, ServletException {
-
-           ByteArrayOutputStream2 buffer = new ByteArrayOutputStream2();
-           InputStream instream = request.getInputStream();
-           if (instream != null) {
-               IO.copy(instream, buffer);
-               buffer.flush();
-           }
-           byte[] content = buffer.getBuf();
-
-           response.setStatus(200);
-           response.setContentLength(content.length);
-
-           OutputStream outstream = response.getOutputStream();
-           outstream.write(content);
-           outstream.flush();
-       }
-
-   }
+    static class RandomDataHandler extends AbstractHandler {
+
+        public RandomDataHandler() {
+            super();
+        }
+
+        public void handle(
+                final String target,
+                final Request baseRequest,
+                final HttpServletRequest request,
+                final HttpServletResponse response) throws IOException, ServletException {
+            if (target.equals("/rnd")) {
+                rnd(request, response);
+            } else if (target.equals("/echo")) {
+                echo(request, response);
+            } else {
+                response.setStatus(HttpStatus.NOT_FOUND_404);
+                Writer writer = response.getWriter();
+                writer.write("Target not found: " + target);
+                writer.flush();
+            }
+        }
+
+        private void rnd(
+                final HttpServletRequest request,
+                final HttpServletResponse response) throws IOException {
+            int count = 100;
+            String s = request.getParameter("c");
+            try {
+                count = Integer.parseInt(s);
+            } catch (NumberFormatException ex) {
+                response.setStatus(500);
+                Writer writer = response.getWriter();
+                writer.write("Invalid query format: " + request.getQueryString());
+                writer.flush();
+                return;
+            }
+
+            response.setStatus(200);
+            response.setContentLength(count);
+
+            OutputStream outstream = response.getOutputStream();
+            byte[] tmp = new byte[1024];
+            int r = Math.abs(tmp.hashCode());
+            int remaining = count;
+            while (remaining > 0) {
+                int chunk = Math.min(tmp.length, remaining);
+                for (int i = 0; i < chunk; i++) {
+                    tmp[i] = (byte) ((r + i) % 96 + 32);
+                }
+                outstream.write(tmp, 0, chunk);
+                remaining -= chunk;
+            }
+            outstream.flush();
+        }
+
+        private void echo(
+                final HttpServletRequest request,
+                final HttpServletResponse response) throws IOException {
+
+            ByteArrayOutputStream2 buffer = new ByteArrayOutputStream2();
+            InputStream instream = request.getInputStream();
+            if (instream != null) {
+                IO.copy(instream, buffer);
+                buffer.flush();
+            }
+            byte[] content = buffer.getBuf();
+
+            response.setStatus(200);
+            response.setContentLength(content.length);
+
+            OutputStream outstream = response.getOutputStream();
+            outstream.write(content);
+            outstream.flush();
+        }
 
-}
+    }
 
+}
diff --git a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/Stats.java b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/Stats.java
index c478839..5d39311 100644
--- a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/Stats.java
+++ b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/Stats.java
@@ -75,10 +75,6 @@ public class Stats {
         return failureCount;
     }
 
-    public void setFailureCount(int failureCount) {
-        this.failureCount = failureCount;
-    }
-
     public synchronized long getContentLen() {
         return contentLen;
     }
@@ -97,7 +93,6 @@ public class Stats {
             final URI targetURI, long startTime, long finishTime, final Stats stats) {
         float totalTimeSec = (float) (finishTime - startTime) / 1000;
         float reqsPerSec = (float) stats.getSuccessCount() / totalTimeSec;
-        float timePerReqMs = (float) (finishTime - startTime) / (float) stats.getSuccessCount();
 
         System.out.print("Document URI:\t\t");
         System.out.println(targetURI);
@@ -120,9 +115,6 @@ public class Stats {
         System.out.print("Requests per second:\t");
         System.out.print(reqsPerSec);
         System.out.println(" [#/sec] (mean)");
-        System.out.print("Time per request:\t");
-        System.out.print(timePerReqMs);
-        System.out.println(" [ms] (mean)");
     }
 
 }
\ No newline at end of file
diff --git a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpClient3.java b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpClient3.java
index c53afd5..d757c3c 100644
--- a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpClient3.java
+++ b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpClient3.java
@@ -125,7 +125,11 @@ public class TestHttpClient3 implements TestHttpAgent {
                             contentLen += l;
                         }
                     }
-                    this.stats.success(contentLen);
+                    if (httpmethod.getStatusCode() == 200) {
+                        this.stats.success(contentLen);
+                    } else {
+                        this.stats.failure(contentLen);
+                    }
                 } catch (IOException ex) {
                     this.stats.failure(contentLen);
                 } finally {
diff --git a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpClient4.java b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpClient4.java
index 8548fef..505e245 100644
--- a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpClient4.java
+++ b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpClient4.java
@@ -42,7 +42,7 @@ import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.conn.ssl.SSLSocketFactory;
 import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
 import org.apache.http.params.HttpProtocolParams;
@@ -52,7 +52,7 @@ import org.apache.http.util.VersionInfo;
 
 public class TestHttpClient4 implements TestHttpAgent {
 
-    private final ThreadSafeClientConnManager mgr;
+    private final PoolingClientConnectionManager mgr;
     private final DefaultHttpClient httpclient;
 
     public TestHttpClient4() {
@@ -71,7 +71,7 @@ public class TestHttpClient4 implements TestHttpAgent {
         SchemeRegistry schemeRegistry = new SchemeRegistry();
         schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
         schemeRegistry.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
-        this.mgr = new ThreadSafeClientConnManager(schemeRegistry);
+        this.mgr = new PoolingClientConnectionManager(schemeRegistry);
         this.httpclient = new DefaultHttpClient(this.mgr, params);
         this.httpclient.setHttpRequestRetryHandler(new HttpRequestRetryHandler() {
 
@@ -139,19 +139,23 @@ public class TestHttpClient4 implements TestHttpAgent {
                     HttpEntity entity = response.getEntity();
                     if (entity != null) {
                         InputStream instream = entity.getContent();
-                        try {
-                            contentLen = 0;
-                            if (instream != null) {
+                        contentLen = 0;
+                        if (instream != null) {
+                            try {
                                 int l = 0;
                                 while ((l = instream.read(buffer)) != -1) {
                                     contentLen += l;
                                 }
+                            } finally {
+                                instream.close();
                             }
-                        } finally {
-                            instream.close();
                         }
                     }
-                    this.stats.success(contentLen);
+                    if (response.getStatusLine().getStatusCode() == 200) {
+                        this.stats.success(contentLen);
+                    } else {
+                        this.stats.failure(contentLen);
+                    }
                 } catch (IOException ex) {
                     this.stats.failure(contentLen);
                     request.abort();
diff --git a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpCore.java b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpCore.java
index 75edadf..2c1a75e 100644
--- a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpCore.java
+++ b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestHttpCore.java
@@ -27,11 +27,13 @@ package org.apache.http.client.benchmark;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.Socket;
 import java.net.URI;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
 
 import org.apache.http.ConnectionReuseStrategy;
 import org.apache.http.HeaderIterator;
+import org.apache.http.HttpClientConnection;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpException;
 import org.apache.http.HttpHost;
@@ -41,7 +43,8 @@ import org.apache.http.HttpResponse;
 import org.apache.http.HttpVersion;
 import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.impl.DefaultConnectionReuseStrategy;
-import org.apache.http.impl.DefaultHttpClientConnection;
+import org.apache.http.impl.pool.BasicConnPool;
+import org.apache.http.impl.pool.BasicPoolEntry;
 import org.apache.http.message.BasicHttpEntityEnclosingRequest;
 import org.apache.http.message.BasicHttpRequest;
 import org.apache.http.params.HttpConnectionParams;
@@ -66,6 +69,7 @@ public class TestHttpCore implements TestHttpAgent {
     private final HttpProcessor httpproc;
     private final HttpRequestExecutor httpexecutor;
     private final ConnectionReuseStrategy connStrategy;
+    private final BasicConnPool pool;
 
     public TestHttpCore() {
         super();
@@ -91,6 +95,8 @@ public class TestHttpCore implements TestHttpAgent {
 
         this.httpexecutor = new HttpRequestExecutor();
         this.connStrategy = new DefaultConnectionReuseStrategy();
+
+        this.pool = new BasicConnPool(this.params);
     }
 
     public void init() {
@@ -100,6 +106,8 @@ public class TestHttpCore implements TestHttpAgent {
     }
 
     Stats execute(final URI target, final byte[] content, int n, int c) throws Exception {
+        this.pool.setMaxTotal(2000);
+        this.pool.setDefaultMaxPerRoute(c);
         HttpHost targetHost = new HttpHost(target.getHost(), target.getPort());
         StringBuilder buffer = new StringBuilder();
         buffer.append(target.getPath());
@@ -143,29 +151,27 @@ public class TestHttpCore implements TestHttpAgent {
         public void run() {
             byte[] buffer = new byte[4096];
             HttpContext context = new BasicHttpContext();
-            DefaultHttpClientConnection conn = new DefaultHttpClientConnection();
-            try {
-                while (!this.stats.isComplete()) {
-                    HttpRequest request;
-                    if (this.content == null) {
-                        BasicHttpRequest httpget = new BasicHttpRequest("GET", this.requestUri);
-                        request = httpget;
-                    } else {
-                        BasicHttpEntityEnclosingRequest httppost = new BasicHttpEntityEnclosingRequest("POST",
-                                this.requestUri);
-                        httppost.setEntity(new ByteArrayEntity(this.content));
-                        request = httppost;
-                    }
 
-                    long contentLen = 0;
-                    try {
-                        if (!conn.isOpen()) {
-                            Socket socket = new Socket(
-                                    this.targetHost.getHostName(),
-                                    this.targetHost.getPort() > 0 ? this.targetHost.getPort() : 80);
-                            conn.bind(socket, params);
-                        }
+            while (!this.stats.isComplete()) {
+                HttpRequest request;
+                if (this.content == null) {
+                    BasicHttpRequest httpget = new BasicHttpRequest("GET", this.requestUri);
+                    request = httpget;
+                } else {
+                    BasicHttpEntityEnclosingRequest httppost = new BasicHttpEntityEnclosingRequest("POST",
+                            this.requestUri);
+                    httppost.setEntity(new ByteArrayEntity(this.content));
+                    request = httppost;
+                }
 
+                long contentLen = 0;
+                boolean reusable = false;
+
+                Future<BasicPoolEntry> future = pool.lease(targetHost, null);
+                try {
+                    BasicPoolEntry entry = future.get();
+                    try {
+                        HttpClientConnection conn = entry.getConnection();
                         context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
                         context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, targetHost);
 
@@ -174,38 +180,44 @@ public class TestHttpCore implements TestHttpAgent {
                         httpexecutor.postProcess(response, httpproc, context);
 
                         HttpEntity entity = response.getEntity();
-                        if (entity != null) {
+                        if (entity != null) { // TODO can this be null?
                             InputStream instream = entity.getContent();
-                            try {
-                                contentLen = 0;
-                                if (instream != null) {
-                                    int l = 0;
-                                    while ((l = instream.read(buffer)) != -1) {
-                                        contentLen += l;
-                                    }
+                            contentLen = 0;
+                            if (instream != null) {
+                                try {
+                                        int l = 0;
+                                        while ((l = instream.read(buffer)) != -1) {
+                                            contentLen += l;
+                                        }
+                                } finally {
+                                    instream.close();
                                 }
-                            } finally {
-                                instream.close();
                             }
                         }
-                        if (!connStrategy.keepAlive(response, context)) {
-                            conn.close();
+                        if (connStrategy.keepAlive(response, context)) {
+                            reusable = true;
                         }
                         for (HeaderIterator it = request.headerIterator(); it.hasNext();) {
                             it.next();
                             it.remove();
                         }
-                        this.stats.success(contentLen);
-                    } catch (IOException ex) {
-                        this.stats.failure(contentLen);
-                    } catch (HttpException ex) {
-                        this.stats.failure(contentLen);
+                        if (response.getStatusLine().getStatusCode() == 200) {
+                            this.stats.success(contentLen);
+                        } else {
+                            this.stats.failure(contentLen);
+                        }
+                    } finally {
+                        pool.release(entry, reusable);
                     }
+                } catch (InterruptedException ex) {
+                    this.stats.failure(contentLen);
+                } catch (ExecutionException ex) {
+                    this.stats.failure(contentLen);
+                } catch (IOException ex) {
+                    this.stats.failure(contentLen);
+                } catch (HttpException ex) {
+                    this.stats.failure(contentLen);
                 }
-            } finally {
-                try {
-                    conn.shutdown();
-                } catch (IOException ignore) {}
             }
         }
 
diff --git a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestJettyHttpClient.java b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestJettyHttpClient.java
index f9e1a18..3676ca9 100644
--- a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestJettyHttpClient.java
+++ b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestJettyHttpClient.java
@@ -70,6 +70,7 @@ public class TestJettyHttpClient implements TestHttpAgent {
             try {
                 this.client.send(exchange);
             } catch (IOException ex) {
+                stats.failure(0L);
             }
         }
         stats.waitFor();
@@ -99,6 +100,7 @@ public class TestJettyHttpClient implements TestHttpAgent {
             this.stats = stats;
         }
 
+        @Override
         protected void onResponseStatus(
                 final Buffer version, int status, final Buffer reason) throws IOException {
             this.status = status;
@@ -135,7 +137,7 @@ public class TestJettyHttpClient implements TestHttpAgent {
             super.onException(x);
         }
 
-    };
+    }
 
     public static void main(String[] args) throws Exception {
         if (args.length < 2) {
diff --git a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestNingHttpClient.java b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestNingHttpClient.java
index 43b922f..ef7ba7e 100644
--- a/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestNingHttpClient.java
+++ b/httpclient-benchmark/src/main/java/org/apache/http/client/benchmark/TestNingHttpClient.java
@@ -94,7 +94,7 @@ public class TestNingHttpClient implements TestHttpAgent {
     }
 
     public String getClientName() {
-        return "Ning Async HTTP client 1.4.0";
+        return "Ning async HTTP client 1.6.4";
     }
 
     static class SimpleAsyncHandler implements AsyncHandler<Object> {
@@ -135,7 +135,7 @@ public class TestNingHttpClient implements TestHttpAgent {
             this.stats.failure(this.contentLen);
         }
 
-    };
+    }
 
     public static void main(String[] args) throws Exception {
         if (args.length < 2) {
diff --git a/httpclient-cache/pom.xml b/httpclient-cache/pom.xml
index 5fadc95..92361a7 100644
--- a/httpclient-cache/pom.xml
+++ b/httpclient-cache/pom.xml
@@ -30,27 +30,17 @@
   <parent>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpcomponents-client</artifactId>
-    <version>4.1.1</version>
+    <version>4.2.1</version>
   </parent>
   <artifactId>httpclient-cache</artifactId>
   <name>HttpClient Cache</name>
+  <inceptionYear>2010</inceptionYear>
   <description>
    HttpComponents HttpClient - Cache
   </description>
   <url>http://hc.apache.org/httpcomponents-client</url>
   <packaging>jar</packaging>
 
-  <repositories>
-    <repository>
-      <id>spy</id>
-      <name>Spy Repository</name>
-      <layout>default</layout>
-      <url>http://bleu.west.spy.net/~dustin/m2repo/</url>
-      <snapshots>
-        <enabled>false</enabled>
-      </snapshots>
-    </repository>
-  </repositories>
   <dependencies>
     <dependency>
       <groupId>org.apache.httpcomponents</groupId>
@@ -61,47 +51,41 @@
     <dependency>
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
-      <version>${commons-logging.version}</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
-      <groupId>junit</groupId>
-      <artifactId>junit</artifactId>
-      <version>${junit.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.easymock</groupId>
-      <artifactId>easymock</artifactId>
-      <version>${easymock.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.easymock</groupId>
-      <artifactId>easymockclassextension</artifactId>
-      <version>${easymock.version}</version>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>net.sf.ehcache</groupId>
       <artifactId>ehcache-core</artifactId>
-      <version>${ehcache.version}</version>
       <scope>compile</scope>
       <optional>true</optional>
     </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-jcl</artifactId>
-      <version>${slf4j.version}</version>
       <scope>compile</scope>
       <optional>true</optional>
     </dependency>
     <dependency>
       <groupId>spy</groupId>
-      <artifactId>memcached</artifactId>
-      <version>2.5</version>
+      <artifactId>spymemcached</artifactId>
+      <scope>compile</scope>
       <optional>true</optional>
     </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymock</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.easymock</groupId>
+      <artifactId>easymockclassextension</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <properties>
@@ -155,6 +139,13 @@
           </execution>
         </executions>
       </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+        <configuration>
+          <comparisonVersion>${comparisonVersion}</comparisonVersion>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
 
@@ -163,6 +154,7 @@
 
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
+         <version>${hc.javadoc.version}</version>
         <configuration>
           <!-- reduce console output. Can override with -Dquiet=false -->
           <quiet>true</quiet>
@@ -193,10 +185,12 @@
 
       <plugin>
         <artifactId>maven-jxr-plugin</artifactId>
+         <version>${hc.jxr.version}</version>
       </plugin>
 
       <plugin>
         <artifactId>maven-surefire-report-plugin</artifactId>
+         <version>${hc.surefire-report.version}</version>
       </plugin>
 
     </plugins>
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java
index 81899ab..aa94bdb 100644
--- a/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java
@@ -28,7 +28,7 @@ package org.apache.http.client.cache;
 
 /**
  * This enumeration represents the various ways a response can be generated
- * by the {@link org.apache.http.impl.client.cache.CachingHttpClient}; 
+ * by the {@link org.apache.http.impl.client.cache.CachingHttpClient};
  * if a request is executed with an {@link org.apache.http.protocol.HttpContext}
  * then a parameter with one of these values will be registered in the
  * context under the key
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/HeaderConstants.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/HeaderConstants.java
index e7fb0a2..0a25f24 100644
--- a/httpclient-cache/src/main/java/org/apache/http/client/cache/HeaderConstants.java
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/HeaderConstants.java
@@ -56,6 +56,9 @@ public class HeaderConstants {
     public static final String AGE = "Age";
     public static final String VARY = "Vary";
     public static final String ALLOW = "Allow";
+    public static final String VIA = "Via";
+    public static final String PUBLIC = "public";
+    public static final String PRIVATE = "private";
 
     public static final String CACHE_CONTROL = "Cache-Control";
     public static final String CACHE_CONTROL_NO_STORE = "no-store";
@@ -65,11 +68,14 @@ public class HeaderConstants {
     public static final String CACHE_CONTROL_MIN_FRESH = "min-fresh";
     public static final String CACHE_CONTROL_MUST_REVALIDATE = "must-revalidate";
     public static final String CACHE_CONTROL_PROXY_REVALIDATE = "proxy-revalidate";
+    public static final String STALE_IF_ERROR = "stale-if-error";
+    public static final String STALE_WHILE_REVALIDATE = "stale-while-revalidate";
 
     public static final String WARNING = "Warning";
     public static final String RANGE = "Range";
     public static final String CONTENT_RANGE = "Content-Range";
     public static final String WWW_AUTHENTICATE = "WWW-Authenticate";
     public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate";
+    public static final String AUTHORIZATION = "Authorization";
 
 }
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java
index 384c0fc..740e7fa 100644
--- a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntry.java
@@ -97,9 +97,6 @@ public class HttpCacheEntry implements Serializable {
         if (responseHeaders == null) {
             throw new IllegalArgumentException("Response headers may not be null");
         }
-        if (resource == null) {
-            throw new IllegalArgumentException("Resource may not be null");
-        }
         this.requestDate = requestDate;
         this.responseDate = responseDate;
         this.statusLine = statusLine;
@@ -110,7 +107,7 @@ public class HttpCacheEntry implements Serializable {
             ? new HashMap<String,String>(variantMap)
             : null;
     }
-    
+
     /**
      * Create a new {@link HttpCacheEntry}.
      *
@@ -207,7 +204,7 @@ public class HttpCacheEntry implements Serializable {
     public Resource getResource() {
         return this.resource;
     }
-    
+
     /**
      * Indicates whether the origin response indicated the associated
      * resource had variants (i.e. that the Vary header was set on the
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntrySerializer.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntrySerializer.java
index d95c119..c5f5074 100644
--- a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntrySerializer.java
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheEntrySerializer.java
@@ -33,7 +33,7 @@ import java.io.OutputStream;
 /**
  * Used by some {@link HttpCacheStorage} implementations to serialize
  * {@link HttpCacheEntry} instances to a byte representation before
- * storage. 
+ * storage.
  */
 public interface HttpCacheEntrySerializer {
 
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheStorage.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheStorage.java
index c4b2ecc..157e5b5 100644
--- a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheStorage.java
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheStorage.java
@@ -33,7 +33,7 @@ import java.io.IOException;
  * interface. They can then be plugged into the existing
  * {@link org.apache.http.impl.client.cache.CachingHttpClient}
  * implementation.
- * 
+ *
  * @since 4.1
  */
 public interface HttpCacheStorage {
@@ -68,7 +68,7 @@ public interface HttpCacheStorage {
      * Atomically applies the given callback to update an existing cache
      * entry under a given key.
      * @param key indicates which entry to modify
-     * @param callback performs the update; see 
+     * @param callback performs the update; see
      *   {@link HttpCacheUpdateCallback} for details, but roughly the
      *   callback expects to be handed the current entry and will return
      *   the new value for the entry.
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheUpdateCallback.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheUpdateCallback.java
index ea2e80b..8c560b7 100644
--- a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheUpdateCallback.java
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheUpdateCallback.java
@@ -49,4 +49,4 @@ public interface HttpCacheUpdateCallback {
      */
     HttpCacheEntry update(HttpCacheEntry existing) throws IOException;
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheUpdateException.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheUpdateException.java
index 956cc8b..6f819db 100644
--- a/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheUpdateException.java
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/HttpCacheUpdateException.java
@@ -45,4 +45,4 @@ public class HttpCacheUpdateException extends Exception {
         initCause(cause);
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/InputLimit.java b/httpclient-cache/src/main/java/org/apache/http/client/cache/InputLimit.java
index fd2cf98..1fef97e 100644
--- a/httpclient-cache/src/main/java/org/apache/http/client/cache/InputLimit.java
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/InputLimit.java
@@ -31,7 +31,7 @@ import org.apache.http.annotation.NotThreadSafe;
 /**
  * Used to limiting the size of an incoming response body of
  * unknown size that is optimistically being read in anticipation
- * of caching it. 
+ * of caching it.
  * @since 4.1
  */
 @NotThreadSafe // reached
diff --git a/httpclient-cache/src/main/java/org/apache/http/client/cache/package.html b/httpclient-cache/src/main/java/org/apache/http/client/cache/package.html
index 9e765c5..58a1e3f 100644
--- a/httpclient-cache/src/main/java/org/apache/http/client/cache/package.html
+++ b/httpclient-cache/src/main/java/org/apache/http/client/cache/package.html
@@ -12,7 +12,7 @@ to you under the Apache License, Version 2.0 (the
 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
@@ -51,7 +51,7 @@ the {@code CachingHttpClient} to indicate how the request was
 processed by the caching module itself.
 </p>
 <p>
-New storage backends will need to implement the 
+New storage backends will need to implement the
 {@link org.apache.http.client.cache.HttpCacheStorage}
 interface; they can then be passed to one of the {@code CachingHttpClient}
 constructors, which will happily make use of the new storage mechanism.
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidationRequest.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidationRequest.java
index 51d647e..fcfe93c 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidationRequest.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidationRequest.java
@@ -37,7 +37,7 @@ import org.apache.http.protocol.HttpContext;
 
 /**
  * Class used to represent an asynchronous revalidation event, such as with
- * "stale-while-revalidate" 
+ * "stale-while-revalidate"
  */
 class AsynchronousValidationRequest implements Runnable {
     private final AsynchronousValidator parent;
@@ -47,12 +47,12 @@ class AsynchronousValidationRequest implements Runnable {
     private final HttpContext context;
     private final HttpCacheEntry cacheEntry;
     private final String identifier;
-    
+
     private final Log log = LogFactory.getLog(getClass());
-    
+
     /**
      * Used internally by {@link AsynchronousValidator} to schedule a
-     * revalidation.   
+     * revalidation.
      * @param cachingClient
      * @param target
      * @param request
@@ -74,7 +74,7 @@ class AsynchronousValidationRequest implements Runnable {
         this.cacheEntry = cacheEntry;
         this.identifier = identifier;
     }
-    
+
     public void run() {
         try {
             cachingClient.revalidateCacheEntry(target, request, context, cacheEntry);
@@ -90,5 +90,5 @@ class AsynchronousValidationRequest implements Runnable {
     String getIdentifier() {
         return identifier;
     }
-    
+
 }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidator.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidator.java
index 023b397..0b5fd2e 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidator.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousValidator.java
@@ -51,20 +51,20 @@ class AsynchronousValidator {
     private final ExecutorService executor;
     private final Set<String> queued;
     private final CacheKeyGenerator cacheKeyGenerator;
-    
+
     private final Log log = LogFactory.getLog(getClass());
-    
+
     /**
      * Create AsynchronousValidator which will make revalidation requests
-     * using the supplied {@link CachingHttpClient}, and 
+     * using the supplied {@link CachingHttpClient}, and
      * a {@link ThreadPoolExecutor} generated according to the thread
-     * pool settings provided in the given {@link CacheConfig}. 
+     * pool settings provided in the given {@link CacheConfig}.
      * @param cachingClient used to execute asynchronous requests
      * @param config specifies thread pool settings. See
      * {@link CacheConfig#getAsynchronousWorkersMax()},
      * {@link CacheConfig#getAsynchronousWorkersCore()},
      * {@link CacheConfig#getAsynchronousWorkerIdleLifetimeSecs()},
-     * and {@link CacheConfig#getRevalidationQueueSize()}. 
+     * and {@link CacheConfig#getRevalidationQueueSize()}.
      */
     public AsynchronousValidator(CachingHttpClient cachingClient,
             CacheConfig config) {
@@ -76,7 +76,7 @@ class AsynchronousValidator {
                         new ArrayBlockingQueue<Runnable>(config.getRevalidationQueueSize()))
                 );
     }
-    
+
     /**
      * Create AsynchronousValidator which will make revalidation requests
      * using the supplied {@link CachingHttpClient} and
@@ -91,10 +91,10 @@ class AsynchronousValidator {
         this.queued = new HashSet<String>();
         this.cacheKeyGenerator = new CacheKeyGenerator();
     }
-    
+
     /**
      * Schedules an asynchronous revalidation
-     * 
+     *
      * @param target
      * @param request
      * @param context
@@ -104,7 +104,7 @@ class AsynchronousValidator {
             HttpRequest request, HttpContext context, HttpCacheEntry entry) {
         // getVariantURI will fall back on getURI if no variants exist
         String uri = cacheKeyGenerator.getVariantURI(target, request, entry);
-        
+
         if (!queued.contains(uri)) {
             AsynchronousValidationRequest revalidationRequest =
                 new AsynchronousValidationRequest(this, cachingClient, target,
@@ -121,7 +121,7 @@ class AsynchronousValidator {
 
     /**
      * Removes an identifier from the internal list of revalidation jobs in
-     * progress.  This is meant to be called by 
+     * progress.  This is meant to be called by
      * {@link AsynchronousValidationRequest#run()} once the revalidation is
      * complete, using the identifier passed in during constructions.
      * @param identifier
@@ -129,11 +129,11 @@ class AsynchronousValidator {
     synchronized void markComplete(String identifier) {
         queued.remove(identifier);
     }
-    
+
     Set<String> getScheduledIdentifiers() {
         return Collections.unmodifiableSet(queued);
     }
-    
+
     ExecutorService getExecutor() {
         return executor;
     }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java
index 1635b76..aa2acf5 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicHttpCache.java
@@ -47,12 +47,13 @@ import org.apache.http.client.cache.Resource;
 import org.apache.http.client.cache.ResourceFactory;
 import org.apache.http.entity.ByteArrayEntity;
 import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.protocol.HTTP;
 
 class BasicHttpCache implements HttpCache {
 
     private final CacheKeyGenerator uriExtractor;
     private final ResourceFactory resourceFactory;
-    private final int maxObjectSizeBytes;
+    private final long maxObjectSizeBytes;
     private final CacheEntryUpdater cacheEntryUpdater;
     private final CachedHttpResponseGenerator responseGenerator;
     private final CacheInvalidator cacheInvalidator;
@@ -64,7 +65,7 @@ class BasicHttpCache implements HttpCache {
         this.resourceFactory = resourceFactory;
         this.uriExtractor = new CacheKeyGenerator();
         this.cacheEntryUpdater = new CacheEntryUpdater(resourceFactory);
-        this.maxObjectSizeBytes = config.getMaxObjectSizeBytes();
+        this.maxObjectSizeBytes = config.getMaxObjectSize();
         this.responseGenerator = new CachedHttpResponseGenerator();
         this.storage = storage;
         this.cacheInvalidator = new CacheInvalidator(this.uriExtractor, this.storage);
@@ -83,7 +84,7 @@ class BasicHttpCache implements HttpCache {
         String uri = uriExtractor.getURI(host, request);
         storage.removeEntry(uri);
     }
-    
+
     public void flushInvalidatedCacheEntriesFor(HttpHost host, HttpRequest request, HttpResponse response) {
         cacheInvalidator.flushInvalidatedCacheEntries(host, request, response);
     }
@@ -157,7 +158,7 @@ class BasicHttpCache implements HttpCache {
             && status != HttpStatus.SC_PARTIAL_CONTENT) {
             return false;
         }
-        Header hdr = resp.getFirstHeader("Content-Length");
+        Header hdr = resp.getFirstHeader(HTTP.CONTENT_LEN);
         if (hdr == null) return false;
         int contentLength;
         try {
@@ -170,7 +171,7 @@ class BasicHttpCache implements HttpCache {
 
     HttpResponse generateIncompleteResponseError(HttpResponse response,
             Resource resource) {
-        int contentLength = Integer.parseInt(response.getFirstHeader("Content-Length").getValue());
+        int contentLength = Integer.parseInt(response.getFirstHeader(HTTP.CONTENT_LEN).getValue());
         HttpResponse error =
             new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_BAD_GATEWAY, "Bad Gateway");
         error.setHeader("Content-Type","text/plain;charset=UTF-8");
@@ -193,7 +194,7 @@ class BasicHttpCache implements HttpCache {
         if (src == null) {
             src = entry;
         }
-        
+
         Resource resource = resourceFactory.copy(requestId, src.getResource());
         Map<String,String> variantMap = new HashMap<String,String>(src.getVariantMap());
         variantMap.put(variantKey, variantCacheKey);
@@ -231,7 +232,7 @@ class BasicHttpCache implements HttpCache {
         storage.putEntry(cacheKey, updatedEntry);
         return updatedEntry;
     }
-    
+
     public HttpResponse cacheAndReturnResponse(HttpHost host, HttpRequest request,
             HttpResponse originResponse, Date requestSent, Date responseReceived)
             throws IOException {
@@ -300,4 +301,4 @@ class BasicHttpCache implements HttpCache {
         variants.put(etagHeader.getValue(), new Variant(variantKey, variantCacheKey, entry));
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicIdGenerator.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicIdGenerator.java
index b13e742..184a391 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicIdGenerator.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/BasicIdGenerator.java
@@ -82,4 +82,4 @@ class BasicIdGenerator {
         return buffer.toString();
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java
index 821a51a..f5dfcb5 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheConfig.java
@@ -33,19 +33,19 @@ package org.apache.http.impl.client.cache;
  * {@code CacheConfig} instance has sane and conservative defaults, so the
  * easiest way to specify options is to get an instance and then set just
  * the options you want to modify from their defaults.</p>
- * 
+ *
  * <p><b>N.B.</b> This class is only for caching-specific configuration; to
- * configure the behavior of the rest of the client, configure the 
+ * configure the behavior of the rest of the client, configure the
  * {@link org.apache.http.client.HttpClient} used as the "backend"
  * for the {@code CachingHttpClient}.</p>
- * 
+ *
  * <p>Cache configuration can be grouped into the following categories:</p>
- * 
+ *
  * <p><b>Cache size.</b> If the backend storage supports these limits, you
  * can specify the {@link CacheConfig#setMaxCacheEntries maximum number of
  * cache entries} as well as the {@link CacheConfig#setMaxObjectSizeBytes
  * maximum cacheable response body size}.</p>
- * 
+ *
  * <p><b>Public/private caching.</b> By default, the caching module considers
  * itself to be a shared (public) cache, and will not, for example, cache
  * responses to requests with {@code Authorization} headers or responses
@@ -53,7 +53,7 @@ package org.apache.http.impl.client.cache;
  * is only going to be used by one logical "user" (behaving similarly to a
  * browser cache), then you will want to {@link
  * CacheConfig#setSharedCache(boolean) turn off the shared cache setting}.</p>
- * 
+ *
  * <p><b>Heuristic caching</b>. Per RFC2616, a cache may cache certain cache
  * entries even if no explicit cache control headers are set by the origin.
  * This behavior is off by default, but you may want to turn this on if you
@@ -67,7 +67,7 @@ package org.apache.http.impl.client.cache;
  * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.2">
  * 13.2.2</a> and <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.4">
  * 13.2.4</a> of the HTTP/1.1 RFC for more details on heuristic caching.</p>
- * 
+ *
  * <p><b>Background validation</b>. The cache module supports the
  * {@code stale-while-revalidate} directive of
  * <a href="http://tools.ietf.org/html/rfc5861">RFC5861</a>, which allows
@@ -79,7 +79,7 @@ package org.apache.http.impl.client.cache;
  * CacheConfig#setAsynchronousWorkerIdleLifetimeSecs(int) maximum time they
  * can be idle before being reclaimed}. You can also control the {@link
  * CacheConfig#setRevalidationQueueSize(int) size of the queue} used for
- * revalidations when there aren't enough workers to keep up with demand.</b> 
+ * revalidations when there aren't enough workers to keep up with demand.</b>
  */
 public class CacheConfig {
 
@@ -115,23 +115,23 @@ public class CacheConfig {
     /** Default number of worker threads to allow for background revalidations
      * resulting from the stale-while-revalidate directive.
      */
-    public static final int DEFAULT_ASYNCHRONOUS_WORKERS_MAX = 1; 
+    public static final int DEFAULT_ASYNCHRONOUS_WORKERS_MAX = 1;
 
     /** Default minimum number of worker threads to allow for background
      * revalidations resulting from the stale-while-revalidate directive.
      */
     public static final int DEFAULT_ASYNCHRONOUS_WORKERS_CORE = 1;
-    
+
     /** Default maximum idle lifetime for a background revalidation thread
      * before it gets reclaimed.
      */
     public static final int DEFAULT_ASYNCHRONOUS_WORKER_IDLE_LIFETIME_SECS = 60;
-    
-    /** Default maximum queue length for background revalidation requests. 
+
+    /** Default maximum queue length for background revalidation requests.
      */
     public static final int DEFAULT_REVALIDATION_QUEUE_SIZE = 100;
-    
-    private int maxObjectSizeBytes = DEFAULT_MAX_OBJECT_SIZE_BYTES;
+
+    private long maxObjectSize = DEFAULT_MAX_OBJECT_SIZE_BYTES;
     private int maxCacheEntries = DEFAULT_MAX_CACHE_ENTRIES;
     private int maxUpdateRetries = DEFAULT_MAX_UPDATE_RETRIES;
     private boolean heuristicCachingEnabled = false;
@@ -146,17 +146,47 @@ public class CacheConfig {
     /**
      * Returns the current maximum response body size that will be cached.
      * @return size in bytes
+     *
+     * @deprecated (4.2)  use {@link #getMaxObjectSize()}
      */
+    @Deprecated 
     public int getMaxObjectSizeBytes() {
-        return maxObjectSizeBytes;
+        return maxObjectSize > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maxObjectSize;
     }
 
     /**
      * Specifies the maximum response body size that will be eligible for caching.
      * @param maxObjectSizeBytes size in bytes
+     *
+     * @deprecated (4.2)  use {@link #setMaxObjectSize(long)}
      */
+    @Deprecated 
     public void setMaxObjectSizeBytes(int maxObjectSizeBytes) {
-        this.maxObjectSizeBytes = maxObjectSizeBytes;
+        if (maxObjectSizeBytes > Integer.MAX_VALUE) {
+            this.maxObjectSize = Integer.MAX_VALUE;
+        } else {
+            this.maxObjectSize = maxObjectSizeBytes;
+        }
+    }
+
+    /**
+     * Returns the current maximum response body size that will be cached.
+     * @return size in bytes
+     *
+     * @since 4.2
+     */
+    public long getMaxObjectSize() {
+        return maxObjectSize;
+    }
+
+    /**
+     * Specifies the maximum response body size that will be eligible for caching.
+     * @param maxObjectSize size in bytes
+     *
+     * @since 4.2
+     */
+    public void setMaxObjectSize(long maxObjectSize) {
+        this.maxObjectSize = maxObjectSize;
     }
 
     /**
@@ -236,7 +266,7 @@ public class CacheConfig {
      * and {@code Date} headers of a cached response during which the cached
      * response will be considered heuristically fresh.
      * @param heuristicCoefficient should be between {@code 0.0} and
-     *   {@code 1.0}. 
+     *   {@code 1.0}.
      */
     public void setHeuristicCoefficient(float heuristicCoefficient) {
         this.heuristicCoefficient = heuristicCoefficient;
@@ -255,7 +285,7 @@ public class CacheConfig {
      * calculation is not possible. Explicit cache control directives on
      * either the request or origin response will override this, as will
      * the heuristic {@code Last-Modified} freshness calculation if it is
-     * available. 
+     * available.
      * @param heuristicDefaultLifetimeSecs is the number of seconds to
      *   consider a cache-eligible response fresh in the absence of other
      *   information. Set this to {@code 0} to disable this style of
@@ -276,9 +306,9 @@ public class CacheConfig {
 
     /**
      * Sets the maximum number of threads to allow for background
-     * revalidations due to the {@code stale-while-revalidate} directive. 
+     * revalidations due to the {@code stale-while-revalidate} directive.
      * @param max number of threads; a value of 0 disables background
-     * revalidations. 
+     * revalidations.
      */
     public void setAsynchronousWorkersMax(int max) {
         this.asynchronousWorkersMax = max;
@@ -286,7 +316,7 @@ public class CacheConfig {
 
     /**
      * Returns the minimum number of threads to keep alive for background
-     * revalidations due to the {@code stale-while-revalidate} directive. 
+     * revalidations due to the {@code stale-while-revalidate} directive.
      */
     public int getAsynchronousWorkersCore() {
         return asynchronousWorkersCore;
@@ -296,7 +326,7 @@ public class CacheConfig {
      * Sets the minimum number of threads to keep alive for background
      * revalidations due to the {@code stale-while-revalidate} directive.
      * @param min should be greater than zero and less than or equal
-     *   to <code>getAsynchronousWorkersMax()</code> 
+     *   to <code>getAsynchronousWorkersMax()</code>
      */
     public void setAsynchronousWorkersCore(int min) {
         this.asynchronousWorkersCore = min;
@@ -337,5 +367,5 @@ public class CacheConfig {
         this.revalidationQueueSize = size;
     }
 
-    
+
 }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntity.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntity.java
index 538689a..b63e7d8 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntity.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheEntity.java
@@ -35,6 +35,7 @@ import org.apache.http.Header;
 import org.apache.http.HttpEntity;
 import org.apache.http.annotation.Immutable;
 import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.cache.Resource;
 import org.apache.http.protocol.HTTP;
 
 @Immutable
@@ -66,7 +67,8 @@ class CacheEntity implements HttpEntity, Serializable {
     }
 
     public long getContentLength() {
-        return this.cacheEntry.getResource().length();
+        Resource resource = this.cacheEntry.getResource();
+		return (resource != null) ? resource.length() : 0L;
     }
 
     public InputStream getContent() throws IOException {
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java
index c73b5e5..ccb03e7 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheInvalidator.java
@@ -43,6 +43,7 @@ import org.apache.http.client.cache.HttpCacheEntry;
 import org.apache.http.client.cache.HttpCacheStorage;
 import org.apache.http.impl.cookie.DateParseException;
 import org.apache.http.impl.cookie.DateUtils;
+import org.apache.http.protocol.HTTP;
 
 /**
  * Given a particular HttpRequest, flush any cache entries that this request
@@ -172,7 +173,7 @@ class CacheInvalidator {
         }
         return relURL;
     }
-    
+
     protected boolean requestShouldNotBeCached(HttpRequest req) {
         String method = req.getRequestLine().getMethod();
         return notGetOrHeadRequest(method);
@@ -184,7 +185,7 @@ class CacheInvalidator {
     }
 
     /** Flushes entries that were invalidated by the given response
-     * received for the given host/request pair. 
+     * received for the given host/request pair.
      */
     public void flushInvalidatedCacheEntries(HttpHost host,
             HttpRequest request, HttpResponse response) {
@@ -200,7 +201,7 @@ class CacheInvalidator {
 
         if (!responseDateNewerThanEntryDate(response, entry)) return;
         if (!responseAndEntryEtagsDiffer(response, entry)) return;
-        
+
         flushUriIfSameHost(reqURL, canonURL);
     }
 
@@ -210,21 +211,21 @@ class CacheInvalidator {
         String contentLocation = clHeader.getValue();
         URL canonURL = getAbsoluteURL(contentLocation);
         if (canonURL != null) return canonURL;
-        return getRelativeURL(reqURL, contentLocation); 
+        return getRelativeURL(reqURL, contentLocation);
     }
 
     private boolean responseAndEntryEtagsDiffer(HttpResponse response,
             HttpCacheEntry entry) {
-        Header entryEtag = entry.getFirstHeader("ETag");
-        Header responseEtag = response.getFirstHeader("ETag");
+        Header entryEtag = entry.getFirstHeader(HeaderConstants.ETAG);
+        Header responseEtag = response.getFirstHeader(HeaderConstants.ETAG);
         if (entryEtag == null || responseEtag == null) return false;
         return (!entryEtag.getValue().equals(responseEtag.getValue()));
     }
 
     private boolean responseDateNewerThanEntryDate(HttpResponse response,
             HttpCacheEntry entry) {
-        Header entryDateHeader = entry.getFirstHeader("Date");
-        Header responseDateHeader = response.getFirstHeader("Date");
+        Header entryDateHeader = entry.getFirstHeader(HTTP.DATE_HEADER);
+        Header responseDateHeader = response.getFirstHeader(HTTP.DATE_HEADER);
         if (entryDateHeader == null || responseDateHeader == null) {
             return false;
         }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheKeyGenerator.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheKeyGenerator.java
index 647debd..50e8ce2 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheKeyGenerator.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheKeyGenerator.java
@@ -37,6 +37,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+import org.apache.http.Consts;
 import org.apache.http.Header;
 import org.apache.http.HeaderElement;
 import org.apache.http.HttpHost;
@@ -44,7 +45,6 @@ import org.apache.http.HttpRequest;
 import org.apache.http.annotation.Immutable;
 import org.apache.http.client.cache.HeaderConstants;
 import org.apache.http.client.cache.HttpCacheEntry;
-import org.apache.http.protocol.HTTP;
 
 /**
  * @since 4.1
@@ -144,7 +144,7 @@ class CacheKeyGenerator {
      * Compute a "variant key" from the headers of a given request that are
      * covered by the Vary header of a given cache entry. Any request whose
      * varying headers match those of this request should have the same
-     * variant key. 
+     * variant key.
      * @param req originating request
      * @param entry cache entry in question that has variants
      * @return a <code>String</code> variant key
@@ -166,10 +166,10 @@ class CacheKeyGenerator {
                 if (!first) {
                     buf.append("&");
                 }
-                buf.append(URLEncoder.encode(headerName, HTTP.UTF_8));
+                buf.append(URLEncoder.encode(headerName, Consts.UTF_8.name()));
                 buf.append("=");
                 buf.append(URLEncoder.encode(getFullHeaderValue(req.getHeaders(headerName)),
-                        HTTP.UTF_8));
+                        Consts.UTF_8.name()));
                 first = false;
             }
             buf.append("}");
@@ -179,4 +179,4 @@ class CacheKeyGenerator {
         return buf.toString();
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheMap.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheMap.java
index 1bb1e3a..3bf8247 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheMap.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheMap.java
@@ -47,4 +47,4 @@ final class CacheMap extends LinkedHashMap<String, HttpCacheEntry> {
         return size() > this.maxEntries;
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java
index ea5cf40..c87c7df 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheValidityPolicy.java
@@ -81,10 +81,10 @@ class CacheValidityPolicy {
      * if last-modified and date are defined, freshness lifetime is coefficient*(date-lastModified),
      * else freshness lifetime is defaultLifetime
      *
-     * @param entry
-     * @param now
-     * @param coefficient
-     * @param defaultLifetime
+     * @param entry the cache entry
+     * @param now what time is it currently (When is right NOW)
+     * @param coefficient Part of the heuristic for cache entry freshness
+     * @param defaultLifetime How long can I assume a cache entry is default TTL
      * @return {@code true} if the response is fresh
      */
     public boolean isResponseHeuristicallyFresh(final HttpCacheEntry entry,
@@ -113,17 +113,17 @@ class CacheValidityPolicy {
     }
 
     public boolean mustRevalidate(final HttpCacheEntry entry) {
-        return hasCacheControlDirective(entry, "must-revalidate");
+        return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE);
     }
 
     public boolean proxyRevalidate(final HttpCacheEntry entry) {
-        return hasCacheControlDirective(entry, "proxy-revalidate");
+        return hasCacheControlDirective(entry, HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE);
     }
-    
+
     public boolean mayReturnStaleWhileRevalidating(final HttpCacheEntry entry, Date now) {
-        for (Header h : entry.getHeaders("Cache-Control")) {
+        for (Header h : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for(HeaderElement elt : h.getElements()) {
-                if ("stale-while-revalidate".equalsIgnoreCase(elt.getName())) {
+                if (HeaderConstants.STALE_WHILE_REVALIDATE.equalsIgnoreCase(elt.getName())) {
                     try {
                         int allowedStalenessLifetime = Integer.parseInt(elt.getValue());
                         if (getStalenessSecs(entry, now) <= allowedStalenessLifetime) {
@@ -135,24 +135,24 @@ class CacheValidityPolicy {
                 }
             }
         }
-        
+
         return false;
     }
-    
+
     public boolean mayReturnStaleIfError(HttpRequest request,
             HttpCacheEntry entry, Date now) {
         long stalenessSecs = getStalenessSecs(entry, now);
-        return mayReturnStaleIfError(request.getHeaders("Cache-Control"),
+        return mayReturnStaleIfError(request.getHeaders(HeaderConstants.CACHE_CONTROL),
                                      stalenessSecs)
-                || mayReturnStaleIfError(entry.getHeaders("Cache-Control"),
+                || mayReturnStaleIfError(entry.getHeaders(HeaderConstants.CACHE_CONTROL),
                                          stalenessSecs);
     }
-    
+
     private boolean mayReturnStaleIfError(Header[] headers, long stalenessSecs) {
         boolean result = false;
         for(Header h : headers) {
             for(HeaderElement elt : h.getElements()) {
-                if ("stale-if-error".equals(elt.getName())) {
+                if (HeaderConstants.STALE_IF_ERROR.equals(elt.getName())) {
                     try {
                         int staleIfErrorSecs = Integer.parseInt(elt.getValue());
                         if (stalenessSecs <= staleIfErrorSecs) {
@@ -204,14 +204,19 @@ class CacheValidityPolicy {
         }
     }
 
+    protected boolean hasContentLengthHeader(HttpCacheEntry entry) {
+        return null != entry.getFirstHeader(HTTP.CONTENT_LEN);
+    }
+
     /**
      * This matters for deciding whether the cache entry is valid to serve as a
      * response. If these values do not match, we might have a partial response
      *
+     * @param entry The cache entry we are currently working with
      * @return boolean indicating whether actual length matches Content-Length
      */
     protected boolean contentLengthHeaderMatchesActualLength(final HttpCacheEntry entry) {
-        return getContentLengthValue(entry) == entry.getResource().length();
+        return !hasContentLengthHeader(entry) || getContentLengthValue(entry) == entry.getResource().length();
     }
 
     protected long getApparentAgeSecs(final HttpCacheEntry entry) {
@@ -256,10 +261,6 @@ class CacheValidityPolicy {
         return getCorrectedReceivedAgeSecs(entry) + getResponseDelaySecs(entry);
     }
 
-    protected Date getCurrentDate() {
-        return new Date();
-    }
-
     protected long getResidentTimeSecs(HttpCacheEntry entry, Date now) {
         long diff = now.getTime() - entry.getResponseDate().getTime();
         return (diff / 1000L);
@@ -300,7 +301,7 @@ class CacheValidityPolicy {
 
     public boolean hasCacheControlDirective(final HttpCacheEntry entry,
             final String directive) {
-        for (Header h : entry.getHeaders("Cache-Control")) {
+        for (Header h : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for(HeaderElement elt : h.getElements()) {
                 if (directive.equalsIgnoreCase(elt.getName())) {
                     return true;
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheableRequestPolicy.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheableRequestPolicy.java
index d9b1ab0..07148c3 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheableRequestPolicy.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CacheableRequestPolicy.java
@@ -58,17 +58,17 @@ class CacheableRequestPolicy {
 
         ProtocolVersion pv = request.getRequestLine().getProtocolVersion();
         if (HttpVersion.HTTP_1_1.compareToVersion(pv) != 0) {
-            log.debug("Request was not serveable from cache");
+            log.trace("non-HTTP/1.1 request was not serveable from cache");
             return false;
         }
 
         if (!method.equals(HeaderConstants.GET_METHOD)) {
-            log.debug("Request was not serveable from cache");
+            log.trace("non-GET request was not serveable from cache");
             return false;
         }
 
         if (request.getHeaders(HeaderConstants.PRAGMA).length > 0) {
-            log.debug("Request was not serveable from cache");
+            log.trace("request with Pragma header was not serveable from cache");
             return false;
         }
 
@@ -77,19 +77,19 @@ class CacheableRequestPolicy {
             for (HeaderElement cacheControlElement : cacheControl.getElements()) {
                 if (HeaderConstants.CACHE_CONTROL_NO_STORE.equalsIgnoreCase(cacheControlElement
                         .getName())) {
-                    log.debug("Request was not serveable from Cache");
+                    log.trace("Request with no-store was not serveable from cache");
                     return false;
                 }
 
                 if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equalsIgnoreCase(cacheControlElement
                         .getName())) {
-                    log.debug("Request was not serveable from cache");
+                    log.trace("Request with no-cache was not serveable from cache");
                     return false;
                 }
             }
         }
 
-        log.debug("Request was serveable from cache");
+        log.trace("Request was serveable from cache");
         return true;
     }
 
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java
index c8a3a4a..4a73da4 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedHttpResponseGenerator.java
@@ -61,10 +61,10 @@ class CachedHttpResponseGenerator {
     }
 
     /**
-     * If I was able to use a {@link CacheEntry} to response to the {@link org.apache.http.HttpRequest} then
+     * If I was able to use a {@link CacheEntity} to response to the {@link org.apache.http.HttpRequest} then
      * generate an {@link HttpResponse} based on the cache entry.
      * @param entry
-     *            {@link CacheEntry} to transform into an {@link HttpResponse}
+     *            {@link CacheEntity} to transform into an {@link HttpResponse}
      * @return {@link HttpResponse} that was constructed
      */
     HttpResponse generateResponse(HttpCacheEntry entry) {
@@ -92,8 +92,8 @@ class CachedHttpResponseGenerator {
     }
 
     /**
-     * Generate a 304 - Not Modified response from a {@link CacheEntry}.  This should be
-     * used to respond to conditional requests, when the entry exists or has been revalidated.
+     * Generate a 304 - Not Modified response from a {@link CacheEntity}.  This should be
+     * used to respond to conditional requests, when the entry exists or has been re-validated.
      *
      * @param entry
      * @return
@@ -107,15 +107,15 @@ class CachedHttpResponseGenerator {
         //  (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
 
         // - Date, unless its omission is required by section 14.8.1
-        Header dateHeader = entry.getFirstHeader("Date");
+        Header dateHeader = entry.getFirstHeader(HTTP.DATE_HEADER);
         if (dateHeader == null) {
-             dateHeader = new BasicHeader("Date", DateUtils.formatDate(new Date()));
+             dateHeader = new BasicHeader(HTTP.DATE_HEADER, DateUtils.formatDate(new Date()));
         }
         response.addHeader(dateHeader);
 
         // - ETag and/or Content-Location, if the header would have been sent
         //   in a 200 response to the same request
-        Header etagHeader = entry.getFirstHeader("ETag");
+        Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
         if (etagHeader != null) {
             response.addHeader(etagHeader);
         }
@@ -128,17 +128,17 @@ class CachedHttpResponseGenerator {
         // - Expires, Cache-Control, and/or Vary, if the field-value might
         //   differ from that sent in any previous response for the same
         //   variant
-        Header expiresHeader = entry.getFirstHeader("Expires");
+        Header expiresHeader = entry.getFirstHeader(HeaderConstants.EXPIRES);
         if (expiresHeader != null) {
             response.addHeader(expiresHeader);
         }
 
-        Header cacheControlHeader = entry.getFirstHeader("Cache-Control");
+        Header cacheControlHeader = entry.getFirstHeader(HeaderConstants.CACHE_CONTROL);
         if (cacheControlHeader != null) {
             response.addHeader(cacheControlHeader);
         }
 
-        Header varyHeader = entry.getFirstHeader("Vary");
+        Header varyHeader = entry.getFirstHeader(HeaderConstants.VARY);
         if (varyHeader != null) {
             response.addHeader(varyHeader);
         }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java
index 42f6ea8..439f3df 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachedResponseSuitabilityChecker.java
@@ -91,9 +91,9 @@ class CachedResponseSuitabilityChecker {
 
     private long getMaxStale(HttpRequest request) {
         long maxstale = -1;
-        for(Header h : request.getHeaders("Cache-Control")) {
+        for(Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for(HeaderElement elt : h.getElements()) {
-                if ("max-stale".equals(elt.getName())) {
+                if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
                     if ((elt.getValue() == null || "".equals(elt.getValue().trim()))
                             && maxstale == -1) {
                         maxstale = Long.MAX_VALUE;
@@ -125,11 +125,14 @@ class CachedResponseSuitabilityChecker {
      *            {@link HttpRequest}
      * @param entry
      *            {@link HttpCacheEntry}
+     * @param now
+     *            Right now in time
      * @return boolean yes/no answer
      */
     public boolean canCachedResponseBeUsed(HttpHost host, HttpRequest request, HttpCacheEntry entry, Date now) {
+
         if (!isFreshEnough(entry, request, now)) {
-            log.debug("Cache entry was not fresh enough");
+            log.trace("Cache entry was not fresh enough");
             return false;
         }
 
@@ -150,12 +153,12 @@ class CachedResponseSuitabilityChecker {
         for (Header ccHdr : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for (HeaderElement elt : ccHdr.getElements()) {
                 if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) {
-                    log.debug("Response contained NO CACHE directive, cache was not suitable");
+                    log.trace("Response contained NO CACHE directive, cache was not suitable");
                     return false;
                 }
 
                 if (HeaderConstants.CACHE_CONTROL_NO_STORE.equals(elt.getName())) {
-                    log.debug("Response contained NO STORE directive, cache was not suitable");
+                    log.trace("Response contained NO STORE directive, cache was not suitable");
                     return false;
                 }
 
@@ -163,12 +166,12 @@ class CachedResponseSuitabilityChecker {
                     try {
                         int maxage = Integer.parseInt(elt.getValue());
                         if (validityStrategy.getCurrentAgeSecs(entry, now) > maxage) {
-                            log.debug("Response from cache was NOT suitable due to max age");
+                            log.trace("Response from cache was NOT suitable due to max age");
                             return false;
                         }
                     } catch (NumberFormatException ex) {
                         // err conservatively
-                        log.debug("Response from cache was malformed: " + ex.getMessage());
+                        log.debug("Response from cache was malformed" + ex.getMessage());
                         return false;
                     }
                 }
@@ -177,7 +180,7 @@ class CachedResponseSuitabilityChecker {
                     try {
                         int maxstale = Integer.parseInt(elt.getValue());
                         if (validityStrategy.getFreshnessLifetimeSecs(entry) > maxstale) {
-                            log.debug("Response from cache was not suitable due to Max stale freshness");
+                            log.trace("Response from cache was not suitable due to Max stale freshness");
                             return false;
                         }
                     } catch (NumberFormatException ex) {
@@ -194,7 +197,7 @@ class CachedResponseSuitabilityChecker {
                         long age = validityStrategy.getCurrentAgeSecs(entry, now);
                         long freshness = validityStrategy.getFreshnessLifetimeSecs(entry);
                         if (freshness - age < minfresh) {
-                            log.debug("Response from cache was not suitable due to min fresh " +
+                            log.trace("Response from cache was not suitable due to min fresh " +
                                     "freshness requirement");
                             return false;
                         }
@@ -207,51 +210,53 @@ class CachedResponseSuitabilityChecker {
             }
         }
 
-        log.debug("Response from cache was suitable");
+        log.trace("Response from cache was suitable");
         return true;
     }
 
     /**
      * Is this request the type of conditional request we support?
-     * @param request
+     * @param request The current httpRequest being made
      * @return {@code true} if the request is supported
      */
     public boolean isConditional(HttpRequest request) {
-        return hasSupportedEtagVadlidator(request) || hasSupportedLastModifiedValidator(request);
+        return hasSupportedEtagValidator(request) || hasSupportedLastModifiedValidator(request);
     }
 
     /**
      * Check that conditionals that are part of this request match
-     * @param request
-     * @param entry
-     * @param now
+     * @param request The current httpRequest being made
+     * @param entry the cache entry
+     * @param now right NOW in time
      * @return {@code true} if the request matches all conditionals
      */
     public boolean allConditionalsMatch(HttpRequest request, HttpCacheEntry entry, Date now) {
-        boolean hasEtagValidator = hasSupportedEtagVadlidator(request);
+        boolean hasEtagValidator = hasSupportedEtagValidator(request);
         boolean hasLastModifiedValidator = hasSupportedLastModifiedValidator(request);
 
-        boolean etagValidatorMatches = (hasEtagValidator) ? etagValidtorMatches(request, entry) : false;
-        boolean lastModifiedValidatorMatches = (hasLastModifiedValidator) ? lastModifiedValidatorMatches(request, entry, now) : false;
+        boolean etagValidatorMatches = (hasEtagValidator) && etagValidatorMatches(request, entry);
+        boolean lastModifiedValidatorMatches = (hasLastModifiedValidator) && lastModifiedValidatorMatches(request, entry, now);
 
         if ((hasEtagValidator && hasLastModifiedValidator)
             && !(etagValidatorMatches && lastModifiedValidatorMatches)) {
             return false;
         } else if (hasEtagValidator && !etagValidatorMatches) {
             return false;
-        } if (hasLastModifiedValidator && !lastModifiedValidatorMatches) {
+        }
+
+        if (hasLastModifiedValidator && !lastModifiedValidatorMatches) {
             return false;
         }
         return true;
     }
 
     private boolean hasUnsupportedConditionalHeaders(HttpRequest request) {
-        return (request.getFirstHeader("If-Range") != null
-                || request.getFirstHeader("If-Match") != null
-                || hasValidDateField(request, "If-Unmodified-Since"));
+        return (request.getFirstHeader(HeaderConstants.IF_RANGE) != null
+                || request.getFirstHeader(HeaderConstants.IF_MATCH) != null
+                || hasValidDateField(request, HeaderConstants.IF_UNMODIFIED_SINCE));
     }
 
-    private boolean hasSupportedEtagVadlidator(HttpRequest request) {
+    private boolean hasSupportedEtagValidator(HttpRequest request) {
         return request.containsHeader(HeaderConstants.IF_NONE_MATCH);
     }
 
@@ -261,11 +266,11 @@ class CachedResponseSuitabilityChecker {
 
     /**
      * Check entry against If-None-Match
-     * @param request
-     * @param entry
-     * @return
+     * @param request The current httpRequest being made
+     * @param entry the cache entry
+     * @return boolean does the etag validator match
      */
-    private boolean etagValidtorMatches(HttpRequest request, HttpCacheEntry entry) {
+    private boolean etagValidatorMatches(HttpRequest request, HttpCacheEntry entry) {
         Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG);
         String etag = (etagHeader != null) ? etagHeader.getValue() : null;
         Header[] ifNoneMatch = request.getHeaders(HeaderConstants.IF_NONE_MATCH);
@@ -286,10 +291,10 @@ class CachedResponseSuitabilityChecker {
     /**
      * Check entry against If-Modified-Since, if If-Modified-Since is in the future it is invalid as per
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
-     * @param request
-     * @param entry
-     * @param now
-     * @return
+     * @param request The current httpRequest being made
+     * @param entry the cache entry
+     * @param now right NOW in time
+     * @return  boolean Does the last modified header match
      */
     private boolean lastModifiedValidatorMatches(HttpRequest request, HttpCacheEntry entry, Date now) {
         Header lastModifiedHeader = entry.getFirstHeader(HeaderConstants.LAST_MODIFIED);
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java
index 9e633f1..deb97a3 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java
@@ -27,8 +27,10 @@
 package org.apache.http.impl.client.cache;
 
 import java.io.IOException;
+import java.lang.reflect.UndeclaredThrowableException;
 import java.net.URI;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicLong;
@@ -37,6 +39,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.http.Header;
 import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpMessage;
 import org.apache.http.HttpRequest;
@@ -62,7 +65,10 @@ import org.apache.http.impl.cookie.DateParseException;
 import org.apache.http.impl.cookie.DateUtils;
 import org.apache.http.message.BasicHttpResponse;
 import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HTTP;
 import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
 import org.apache.http.util.VersionInfo;
 
 /**
@@ -79,7 +85,7 @@ import org.apache.http.util.VersionInfo;
  * related configuration you want to do vis-a-vis timeouts and connection
  * pools should be done on this backend client before constructing a {@code
  * CachingHttpClient} from it.</p>
- * 
+ *
  * <p>Generally speaking, the {@code CachingHttpClient} is implemented as a
  * <a href="http://en.wikipedia.org/wiki/Decorator_pattern">Decorator</a>
  * of the backend client; for any incoming request it attempts to satisfy
@@ -92,7 +98,7 @@ import org.apache.http.util.VersionInfo;
  * This notion of "semantic transparency" means you should be able to drop
  * a {@link CachingHttpClient} into an existing application without breaking
  * anything.</p>
- * 
+ *
  * <p>Folks that would like to experiment with alternative storage backends
  * should look at the {@link HttpCacheStorage} interface and the related
  * package documentation there. You may also be interested in the provided
@@ -120,6 +126,8 @@ public class CachingHttpClient implements HttpClient {
     private final AtomicLong cacheMisses = new AtomicLong();
     private final AtomicLong cacheUpdates = new AtomicLong();
 
+    private final Map<ProtocolVersion, String> viaHeaders = new HashMap<ProtocolVersion, String>(4);
+
     private final HttpClient backend;
     private final HttpCache responseCache;
     private final CacheValidityPolicy validityPolicy;
@@ -130,14 +138,14 @@ public class CachingHttpClient implements HttpClient {
 
     private final ConditionalRequestBuilder conditionalRequestBuilder;
 
-    private final int maxObjectSizeBytes;
+    private final long maxObjectSizeBytes;
     private final boolean sharedCache;
 
     private final ResponseProtocolCompliance responseCompliance;
     private final RequestProtocolCompliance requestCompliance;
 
     private final AsynchronousValidator asynchRevalidator;
-    
+
     private final Log log = LogFactory.getLog(getClass());
 
     CachingHttpClient(
@@ -154,7 +162,7 @@ public class CachingHttpClient implements HttpClient {
         if (config == null) {
             throw new IllegalArgumentException("CacheConfig may not be null");
         }
-        this.maxObjectSizeBytes = config.getMaxObjectSizeBytes();
+        this.maxObjectSizeBytes = config.getMaxObjectSize();
         this.sharedCache = config.isSharedCache();
         this.backend = client;
         this.responseCache = cache;
@@ -226,7 +234,7 @@ public class CachingHttpClient implements HttpClient {
      * response bodies are managed using the given {@link ResourceFactory}.
      * @param client used to make origin requests
      * @param resourceFactory how to manage cached response bodies
-     * @param storage where to store cache entries 
+     * @param storage where to store cache entries
      * @param config cache module options
      */
     public CachingHttpClient(
@@ -244,7 +252,7 @@ public class CachingHttpClient implements HttpClient {
      * that stores cache entries in the provided storage backend and uses
      * the given {@link HttpClient} for backend requests.
      * @param client used to make origin requests
-     * @param storage where to store cache entries 
+     * @param storage where to store cache entries
      * @param config cache module options
      */
     public CachingHttpClient(
@@ -268,7 +276,7 @@ public class CachingHttpClient implements HttpClient {
             ResponseProtocolCompliance responseCompliance,
             RequestProtocolCompliance requestCompliance) {
         CacheConfig config = new CacheConfig();
-        this.maxObjectSizeBytes = config.getMaxObjectSizeBytes();
+        this.maxObjectSizeBytes = config.getMaxObjectSize();
         this.sharedCache = config.isSharedCache();
         this.backend = backend;
         this.validityPolicy = validityPolicy;
@@ -282,7 +290,7 @@ public class CachingHttpClient implements HttpClient {
         this.requestCompliance = requestCompliance;
         this.asynchRevalidator = makeAsynchronousValidator(config);
     }
-    
+
     private AsynchronousValidator makeAsynchronousValidator(
             CacheConfig config) {
         if (config.getAsynchronousWorkersMax() > 0) {
@@ -292,7 +300,7 @@ public class CachingHttpClient implements HttpClient {
     }
 
     /**
-     * Reports the number of times that the cache successfully responded 
+     * Reports the number of times that the cache successfully responded
      * to an {@link HttpRequest} without contacting the origin server.
      * @return the number of cache hits
      */
@@ -331,7 +339,7 @@ public class CachingHttpClient implements HttpClient {
     public <T> T execute(HttpHost target, HttpRequest request,
                          ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException {
         HttpResponse resp = execute(target, request, context);
-        return responseHandler.handleResponse(resp);
+        return handleAndConsume(responseHandler,resp);
     }
 
     public HttpResponse execute(HttpUriRequest request) throws IOException {
@@ -353,7 +361,38 @@ public class CachingHttpClient implements HttpClient {
     public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler,
                          HttpContext context) throws IOException {
         HttpResponse resp = execute(request, context);
-        return responseHandler.handleResponse(resp);
+        return handleAndConsume(responseHandler, resp);
+    }
+
+    private <T> T handleAndConsume(
+            final ResponseHandler<? extends T> responseHandler,
+            HttpResponse response) throws Error, IOException {
+        T result;
+        try {
+            result = responseHandler.handleResponse(response);
+        } catch (Exception t) {
+            HttpEntity entity = response.getEntity();
+            try {
+                EntityUtils.consume(entity);
+            } catch (Exception t2) {
+                // Log this exception. The original exception is more
+                // important and will be thrown to the caller.
+                this.log.warn("Error consuming content after an exception.", t2);
+            }
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException) t;
+            }
+            if (t instanceof IOException) {
+                throw (IOException) t;
+            }
+            throw new UndeclaredThrowableException(t);
+        }
+
+        // Handling the response was successful. Ensure that the content has
+        // been fully consumed.
+        HttpEntity entity = response.getEntity();
+        EntityUtils.consume(entity);
+        return result;
     }
 
     public ClientConnectionManager getConnectionManager() {
@@ -395,43 +434,46 @@ public class CachingHttpClient implements HttpClient {
             return handleCacheMiss(target, request, context);
         }
 
-        return handleCacheHit(target, request, context, entry); 
+        return handleCacheHit(target, request, context, entry);
     }
 
     private HttpResponse handleCacheHit(HttpHost target, HttpRequest request,
             HttpContext context, HttpCacheEntry entry)
             throws ClientProtocolException, IOException {
         recordCacheHit(target, request);
-
+        HttpResponse out = null;
         Date now = getCurrentDate();
         if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) {
-            return generateCachedResponse(request, context, entry, now);
-        }
-
-        if (!mayCallBackend(request)) {
-            return generateGatewayTimeout(context);
-        }
-
-        if (validityPolicy.isRevalidatable(entry)) {
+            out = generateCachedResponse(request, context, entry, now);
+        } else if (!mayCallBackend(request)) {
+            out = generateGatewayTimeout(context);
+        } else if (validityPolicy.isRevalidatable(entry)) {
             return revalidateCacheEntry(target, request, context, entry, now);
+        } else {
+        	return callBackend(target, request, context);
         }
-        return callBackend(target, request, context);
+        if (context != null) {
+        	context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
+        	context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
+        	context.setAttribute(ExecutionContext.HTTP_RESPONSE, out);
+        	context.setAttribute(ExecutionContext.HTTP_REQ_SENT, true);
+        }
+        return out;
     }
 
     private HttpResponse revalidateCacheEntry(HttpHost target,
             HttpRequest request, HttpContext context, HttpCacheEntry entry,
             Date now) throws ClientProtocolException {
-        log.debug("Revalidating the cache entry");
+        log.trace("Revalidating the cache entry");
 
         try {
             if (asynchRevalidator != null
                 && !staleResponseNotAllowed(request, entry, now)
                 && validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
-                final HttpResponse resp = responseGenerator.generateResponse(entry);
-                resp.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
-                
+                final HttpResponse resp = generateCachedResponse(request, context, entry, now);
+
                 asynchRevalidator.revalidateCacheEntry(target, request, context, entry);
-                
+
                 return resp;
             }
             return revalidateCacheEntry(target, request, context, entry);
@@ -469,12 +511,12 @@ public class CachingHttpClient implements HttpClient {
         }
         return entry;
     }
-    
+
     private HttpResponse getFatallyNoncompliantResponse(HttpRequest request,
             HttpContext context) {
         HttpResponse fatalErrorResponse = null;
         List<RequestProtocolError> fatalError = requestCompliance.requestIsFatallyNonCompliant(request);
-        
+
         for (RequestProtocolError error : fatalError) {
             setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
             fatalErrorResponse = requestCompliance.getErrorForRequest(error);
@@ -495,20 +537,20 @@ public class CachingHttpClient implements HttpClient {
 
     private void recordCacheMiss(HttpHost target, HttpRequest request) {
         cacheMisses.getAndIncrement();
-        if (log.isDebugEnabled()) {
+        if (log.isTraceEnabled()) {
             RequestLine rl = request.getRequestLine();
-            log.debug("Cache miss [host: " + target + "; uri: " + rl.getUri() + "]");
+            log.trace("Cache miss [host: " + target + "; uri: " + rl.getUri() + "]");
         }
     }
 
     private void recordCacheHit(HttpHost target, HttpRequest request) {
         cacheHits.getAndIncrement();
-        if (log.isDebugEnabled()) {
+        if (log.isTraceEnabled()) {
             RequestLine rl = request.getRequestLine();
-            log.debug("Cache hit [host: " + target + "; uri: " + rl.getUri() + "]");
+            log.trace("Cache hit [host: " + target + "; uri: " + rl.getUri() + "]");
         }
     }
-    
+
     private void recordCacheUpdate(HttpContext context) {
         cacheUpdates.getAndIncrement();
         setResponseStatus(context, CacheResponseStatus.VALIDATED);
@@ -534,7 +576,7 @@ public class CachingHttpClient implements HttpClient {
         }
         setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
         if (validityPolicy.getStalenessSecs(entry, now) > 0L) {
-            cachedResponse.addHeader("Warning","110 localhost \"Response is stale\"");
+            cachedResponse.addHeader(HeaderConstants.WARNING,"110 localhost \"Response is stale\"");
         }
         return cachedResponse;
     }
@@ -570,7 +612,7 @@ public class CachingHttpClient implements HttpClient {
     }
 
     private boolean mayCallBackend(HttpRequest request) {
-        for (Header h: request.getHeaders("Cache-Control")) {
+        for (Header h: request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for (HeaderElement elt : h.getElements()) {
                 if ("only-if-cached".equals(elt.getName())) {
                     return false;
@@ -581,9 +623,9 @@ public class CachingHttpClient implements HttpClient {
     }
 
     private boolean explicitFreshnessRequest(HttpRequest request, HttpCacheEntry entry, Date now) {
-        for(Header h : request.getHeaders("Cache-Control")) {
+        for(Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for(HeaderElement elt : h.getElements()) {
-                if ("max-stale".equals(elt.getName())) {
+                if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
                     try {
                         int maxstale = Integer.parseInt(elt.getValue());
                         long age = validityPolicy.getCurrentAgeSecs(entry, now);
@@ -592,26 +634,35 @@ public class CachingHttpClient implements HttpClient {
                     } catch (NumberFormatException nfe) {
                         return true;
                     }
-                } else if ("min-fresh".equals(elt.getName())
-                            || "max-age".equals(elt.getName())) {
+                } else if (HeaderConstants.CACHE_CONTROL_MIN_FRESH.equals(elt.getName())
+                            || HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())) {
                     return true;
                 }
             }
         }
         return false;
     }
-    
+
     private String generateViaHeader(HttpMessage msg) {
+
+        final ProtocolVersion pv = msg.getProtocolVersion();
+        String existingEntry = viaHeaders.get(pv);
+        if (existingEntry != null) return existingEntry;
+
         final VersionInfo vi = VersionInfo.loadVersionInfo("org.apache.http.client", getClass().getClassLoader());
         final String release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE;
-        final ProtocolVersion pv = msg.getProtocolVersion();
+
+        String value;
         if ("http".equalsIgnoreCase(pv.getProtocol())) {
-            return String.format("%d.%d localhost (Apache-HttpClient/%s (cache))",
-                pv.getMajor(), pv.getMinor(), release);
+            value = String.format("%d.%d localhost (Apache-HttpClient/%s (cache))", pv.getMajor(), pv.getMinor(),
+                    release);
         } else {
-            return String.format("%s/%d.%d localhost (Apache-HttpClient/%s (cache))",
-                    pv.getProtocol(), pv.getMajor(), pv.getMinor(), release);
+            value = String.format("%s/%d.%d localhost (Apache-HttpClient/%s (cache))", pv.getProtocol(), pv.getMajor(),
+                    pv.getMinor(), release);
         }
+        viaHeaders.put(pv, value);
+
+        return value;
     }
 
     private void setResponseStatus(final HttpContext context, final CacheResponseStatus value) {
@@ -665,7 +716,7 @@ public class CachingHttpClient implements HttpClient {
 
         Date requestDate = getCurrentDate();
 
-        log.debug("Calling the backend");
+        log.trace("Calling the backend");
         HttpResponse backendResponse = backend.execute(target, request, context);
         backendResponse.addHeader("Via", generateViaHeader(backendResponse));
         return handleBackendResponse(target, request, requestDate, getCurrentDate(),
@@ -675,8 +726,8 @@ public class CachingHttpClient implements HttpClient {
 
     private boolean revalidationResponseIsTooOld(HttpResponse backendResponse,
             HttpCacheEntry cacheEntry) {
-        final Header entryDateHeader = cacheEntry.getFirstHeader("Date");
-        final Header responseDateHeader = backendResponse.getFirstHeader("Date");
+        final Header entryDateHeader = cacheEntry.getFirstHeader(HTTP.DATE_HEADER);
+        final Header responseDateHeader = backendResponse.getFirstHeader(HTTP.DATE_HEADER);
         if (entryDateHeader != null && responseDateHeader != null) {
             try {
                 Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
@@ -691,7 +742,7 @@ public class CachingHttpClient implements HttpClient {
         }
         return false;
     }
-    
+
     HttpResponse negotiateResponseFromVariants(HttpHost target,
             HttpRequest request, HttpContext context,
             Map<String, Variant> variants) throws IOException {
@@ -721,12 +772,13 @@ public class CachingHttpClient implements HttpClient {
         }
 
         HttpCacheEntry matchedEntry = matchingVariant.getEntry();
-        
+
         if (revalidationResponseIsTooOld(backendResponse, matchedEntry)) {
+            EntityUtils.consume(backendResponse.getEntity());
             return retryRequestUnconditionally(target, request, context,
                     matchedEntry);
         }
-        
+
         recordCacheUpdate(context);
 
         HttpCacheEntry responseEntry = getUpdatedVariantEntry(target,
@@ -793,6 +845,7 @@ public class CachingHttpClient implements HttpClient {
         Date responseDate = getCurrentDate();
 
         if (revalidationResponseIsTooOld(backendResponse, cacheEntry)) {
+            EntityUtils.consume(backendResponse.getEntity());
             HttpRequest unconditional = conditionalRequestBuilder
                 .buildUnconditionalRequest(request, cacheEntry);
             requestDate = getCurrentDate();
@@ -800,7 +853,7 @@ public class CachingHttpClient implements HttpClient {
             responseDate = getCurrentDate();
         }
 
-        backendResponse.addHeader("Via", generateViaHeader(backendResponse));
+        backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse));
 
         int statusCode = backendResponse.getStatusLine().getStatusCode();
         if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) {
@@ -816,13 +869,15 @@ public class CachingHttpClient implements HttpClient {
             }
             return responseGenerator.generateResponse(updatedEntry);
         }
-        
+
         if (staleIfErrorAppliesTo(statusCode)
             && !staleResponseNotAllowed(request, cacheEntry, getCurrentDate())
             && validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
             final HttpResponse cachedResponse = responseGenerator.generateResponse(cacheEntry);
             cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
-            return cachedResponse; 
+            HttpEntity errorBody = backendResponse.getEntity();
+            if (errorBody != null) EntityUtils.consume(errorBody);
+            return cachedResponse;
         }
 
         return handleBackendResponse(target, conditionalRequest, requestDate, responseDate,
@@ -830,9 +885,9 @@ public class CachingHttpClient implements HttpClient {
     }
 
     private boolean staleIfErrorAppliesTo(int statusCode) {
-        return statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR  
-                || statusCode == HttpStatus.SC_BAD_GATEWAY  
-                || statusCode == HttpStatus.SC_SERVICE_UNAVAILABLE  
+        return statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR
+                || statusCode == HttpStatus.SC_BAD_GATEWAY
+                || statusCode == HttpStatus.SC_SERVICE_UNAVAILABLE
                 || statusCode == HttpStatus.SC_GATEWAY_TIMEOUT;
     }
 
@@ -843,7 +898,7 @@ public class CachingHttpClient implements HttpClient {
             Date responseDate,
             HttpResponse backendResponse) throws IOException {
 
-        log.debug("Handling Backend response");
+        log.trace("Handling Backend response");
         responseCompliance.ensureProtocolCompliance(request, backendResponse);
 
         boolean cacheable = responseCachingPolicy.isResponseCacheable(request, backendResponse);
@@ -876,15 +931,16 @@ public class CachingHttpClient implements HttpClient {
             // nop
         }
         if (existing == null) return false;
-        Header entryDateHeader = existing.getFirstHeader("Date");
+        Header entryDateHeader = existing.getFirstHeader(HTTP.DATE_HEADER);
         if (entryDateHeader == null) return false;
-        Header responseDateHeader = backendResponse.getFirstHeader("Date");
+        Header responseDateHeader = backendResponse.getFirstHeader(HTTP.DATE_HEADER);
         if (responseDateHeader == null) return false;
         try {
             Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
             Date responseDate = DateUtils.parseDate(responseDateHeader.getValue());
             return responseDate.before(entryDate);
         } catch (DateParseException e) {
+            // Empty on Purpose
         }
         return false;
     }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ConditionalRequestBuilder.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ConditionalRequestBuilder.java
index d04e973..02e85a7 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ConditionalRequestBuilder.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ConditionalRequestBuilder.java
@@ -46,14 +46,14 @@ import org.apache.http.impl.client.RequestWrapper;
 class ConditionalRequestBuilder {
 
     private static final Log log = LogFactory.getLog(ConditionalRequestBuilder.class);
-    
+
     /**
      * When a {@link HttpCacheEntry} is stale but 'might' be used as a response
      * to an {@link HttpRequest} we will attempt to revalidate the entry with
      * the origin.  Build the origin {@link HttpRequest} here and return it.
      *
      * @param request the original request from the caller
-     * @param cacheEntry the entry that needs to be revalidated
+     * @param cacheEntry the entry that needs to be re-validated
      * @return the wrapped request
      * @throws ProtocolException when I am unable to build a new origin request.
      */
@@ -80,7 +80,7 @@ class ConditionalRequestBuilder {
             }
         }
         if (mustRevalidate) {
-            wrapperRequest.addHeader("Cache-Control","max-age=0");
+            wrapperRequest.addHeader(HeaderConstants.CACHE_CONTROL, HeaderConstants.CACHE_CONTROL_MAX_AGE + "=0");
         }
         return wrapperRequest;
 
@@ -117,7 +117,7 @@ class ConditionalRequestBuilder {
             first = false;
             etags.append(etag);
         }
-        
+
         wrapperRequest.setHeader(HeaderConstants.IF_NONE_MATCH, etags.toString());
         return wrapperRequest;
     }
@@ -133,8 +133,7 @@ class ConditionalRequestBuilder {
      * @param entry existing cache entry we are trying to validate
      * @return an unconditional validation request
      */
-    public HttpRequest buildUnconditionalRequest(HttpRequest request,
-            HttpCacheEntry entry) { 
+    public HttpRequest buildUnconditionalRequest(HttpRequest request, HttpCacheEntry entry) {
         RequestWrapper wrapped;
         try {
             wrapped = new RequestWrapper(request);
@@ -143,13 +142,13 @@ class ConditionalRequestBuilder {
             return request;
         }
         wrapped.resetHeaders();
-        wrapped.addHeader("Cache-Control","no-cache");
-        wrapped.addHeader("Pragma","no-cache");
-        wrapped.removeHeaders("If-Range");
-        wrapped.removeHeaders("If-Match");
-        wrapped.removeHeaders("If-None-Match");
-        wrapped.removeHeaders("If-Unmodified-Since");
-        wrapped.removeHeaders("If-Modified-Since");
+        wrapped.addHeader(HeaderConstants.CACHE_CONTROL,HeaderConstants.CACHE_CONTROL_NO_CACHE);
+        wrapped.addHeader(HeaderConstants.PRAGMA,HeaderConstants.CACHE_CONTROL_NO_CACHE);
+        wrapped.removeHeaders(HeaderConstants.IF_RANGE);
+        wrapped.removeHeaders(HeaderConstants.IF_MATCH);
+        wrapped.removeHeaders(HeaderConstants.IF_NONE_MATCH);
+        wrapped.removeHeaders(HeaderConstants.IF_UNMODIFIED_SINCE);
+        wrapped.removeHeaders(HeaderConstants.IF_MODIFIED_SINCE);
         return wrapped;
     }
 
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileResource.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileResource.java
index 52c3306..03e2d5f 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileResource.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/FileResource.java
@@ -54,24 +54,15 @@ public class FileResource implements Resource {
         this.disposed = false;
     }
 
-    private void ensureValid() {
-        if (this.disposed) {
-            throw new IllegalStateException("Resource has been deallocated");
-        }
-    }
-
     synchronized File getFile() {
-        ensureValid();
         return this.file;
     }
 
     public synchronized InputStream getInputStream() throws IOException {
-        ensureValid();
         return new FileInputStream(this.file);
     }
 
     public synchronized long length() {
-        ensureValid();
         return this.file.length();
     }
 
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java
index 71f4942..ad8d2e3 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/HttpCache.java
@@ -56,9 +56,9 @@ interface HttpCache {
      */
     void flushInvalidatedCacheEntriesFor(HttpHost host, HttpRequest request)
         throws IOException;
-    
+
     /** Clear any entries that may be invalidated by the given response to
-     * a particular request. 
+     * a particular request.
      * @param host
      * @param request
      * @param response
@@ -122,7 +122,7 @@ interface HttpCache {
      * Update a specific {@link HttpCacheEntry} representing a cached variant
      * using a 304 {@link HttpResponse}.
      * @param target host for client request
-     * @param request actual request from upstream client 
+     * @param request actual request from upstream client
      * @param stale current variant cache entry
      * @param originResponse 304 response received from origin
      * @param requestSent when the validating request was sent
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/IOUtils.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/IOUtils.java
index 44b66ef..a9d78a2 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/IOUtils.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/IOUtils.java
@@ -69,7 +69,7 @@ class IOUtils {
 
     static void copyFile(final File in, final File out) throws IOException {
         RandomAccessFile f1 = new RandomAccessFile(in, "r");
-        RandomAccessFile f2 = new RandomAccessFile(out, "w");
+        RandomAccessFile f2 = new RandomAccessFile(out, "rw");
         try {
             FileChannel c1 = f1.getChannel();
             FileChannel c2 = f2.getChannel();
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/RequestProtocolCompliance.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/RequestProtocolCompliance.java
index 94e2518..bef4ae4 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/RequestProtocolCompliance.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/RequestProtocolCompliance.java
@@ -43,6 +43,7 @@ import org.apache.http.annotation.Immutable;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.cache.HeaderConstants;
 import org.apache.http.entity.AbstractHttpEntity;
+import org.apache.http.entity.ContentType;
 import org.apache.http.impl.client.RequestWrapper;
 import org.apache.http.message.BasicHeader;
 import org.apache.http.message.BasicHttpResponse;
@@ -56,7 +57,7 @@ import org.apache.http.protocol.HTTP;
 class RequestProtocolCompliance {
 
     private static final List<String> disallowedWithNoCache =
-        Arrays.asList("min-fresh", "max-stale", "max-age");
+        Arrays.asList(HeaderConstants.CACHE_CONTROL_MIN_FRESH, HeaderConstants.CACHE_CONTROL_MAX_STALE, HeaderConstants.CACHE_CONTROL_MAX_AGE);
 
     /**
      * Test to see if the {@link HttpRequest} is HTTP1.1 compliant or not
@@ -96,7 +97,7 @@ class RequestProtocolCompliance {
      */
     public HttpRequest makeRequestCompliant(HttpRequest request)
         throws ClientProtocolException {
-    
+
         if (requestMustNotHaveEntity(request)) {
             ((HttpEntityEnclosingRequest) request).setEntity(null);
         }
@@ -116,23 +117,23 @@ class RequestProtocolCompliance {
 
         return request;
     }
-    
+
     private void stripOtherFreshnessDirectivesWithNoCache(HttpRequest request) {
         List<HeaderElement> outElts = new ArrayList<HeaderElement>();
         boolean shouldStrip = false;
-        for(Header h : request.getHeaders("Cache-Control")) {
+        for(Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for(HeaderElement elt : h.getElements()) {
                 if (!disallowedWithNoCache.contains(elt.getName())) {
                     outElts.add(elt);
                 }
-                if ("no-cache".equals(elt.getName())) {
+                if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elt.getName())) {
                     shouldStrip = true;
                 }
             }
         }
         if (!shouldStrip) return;
-        request.removeHeaders("Cache-Control");
-        request.setHeader("Cache-Control", buildHeaderFromElements(outElts));
+        request.removeHeaders(HeaderConstants.CACHE_CONTROL);
+        request.setHeader(HeaderConstants.CACHE_CONTROL, buildHeaderFromElements(outElts));
     }
 
     private String buildHeaderFromElements(List<HeaderElement> outElts) {
@@ -184,7 +185,8 @@ class RequestProtocolCompliance {
 
     private void addContentTypeHeaderIfMissing(HttpEntityEnclosingRequest request) {
         if (request.getEntity().getContentType() == null) {
-            ((AbstractHttpEntity) request.getEntity()).setContentType(HTTP.OCTET_STREAM_TYPE);
+            ((AbstractHttpEntity) request.getEntity()).setContentType(
+                    ContentType.APPLICATION_OCTET_STREAM.getMimeType());
         }
     }
 
@@ -376,9 +378,9 @@ class RequestProtocolCompliance {
     }
 
     private RequestProtocolError requestContainsNoCacheDirectiveWithFieldName(HttpRequest request) {
-        for(Header h : request.getHeaders("Cache-Control")) {
+        for(Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
             for(HeaderElement elt : h.getElements()) {
-                if ("no-cache".equalsIgnoreCase(elt.getName())
+                if (HeaderConstants.CACHE_CONTROL_NO_CACHE.equalsIgnoreCase(elt.getName())
                     && elt.getValue() != null) {
                     return RequestProtocolError.NO_CACHE_DIRECTIVE_WITH_FIELD_NAME;
                 }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java
index 43a3725..8258737 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseCachingPolicy.java
@@ -26,7 +26,10 @@
  */
 package org.apache.http.impl.client.cache;
 
+import java.util.Arrays;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -51,10 +54,19 @@ import org.apache.http.protocol.HTTP;
 @Immutable
 class ResponseCachingPolicy {
 
-    private final int maxObjectSizeBytes;
+    private final long maxObjectSizeBytes;
     private final boolean sharedCache;
     private final Log log = LogFactory.getLog(getClass());
-
+    private static final Set<Integer> cacheableStatuses = 
+    	new HashSet<Integer>(Arrays.asList(HttpStatus.SC_OK,
+    			HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION,
+    			HttpStatus.SC_MULTIPLE_CHOICES,
+    			HttpStatus.SC_MOVED_PERMANENTLY,
+    			HttpStatus.SC_GONE));
+    private static final Set<Integer> uncacheableStatuses =
+    		new HashSet<Integer>(Arrays.asList(HttpStatus.SC_PARTIAL_CONTENT,
+    				HttpStatus.SC_SEE_OTHER));
+    	
     /**
      * Define a cache policy that limits the size of things that should be stored
      * in the cache to a maximum of {@link HttpResponse} bytes in size.
@@ -63,7 +75,7 @@ class ResponseCachingPolicy {
      * @param sharedCache whether to behave as a shared cache (true) or a
      * non-shared/private cache (false)
      */
-    public ResponseCachingPolicy(int maxObjectSizeBytes, boolean sharedCache) {
+    public ResponseCachingPolicy(long maxObjectSizeBytes, boolean sharedCache) {
         this.maxObjectSizeBytes = maxObjectSizeBytes;
         this.sharedCache = sharedCache;
     }
@@ -82,30 +94,19 @@ class ResponseCachingPolicy {
             log.debug("Response was not cacheable.");
             return false;
         }
-
-        switch (response.getStatusLine().getStatusCode()) {
-        case HttpStatus.SC_OK:
-        case HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION:
-        case HttpStatus.SC_MULTIPLE_CHOICES:
-        case HttpStatus.SC_MOVED_PERMANENTLY:
-        case HttpStatus.SC_GONE:
-            // these response codes MAY be cached
-            cacheable = true;
-            log.debug("Response was cacheable");
-            break;
-        case HttpStatus.SC_PARTIAL_CONTENT:
-            // we don't implement Range requests and hence are not
-            // allowed to cache partial content
-            log.debug("Response was not cacheable (Partial Content)");
-            return cacheable;
-
-        default:
-            // If the status code is not one of the recognized
-            // available codes in HttpStatus Don't Cache
-            log.debug("Response was not cacheable (Unknown Status code)");
-            return cacheable;
+        
+        int status = response.getStatusLine().getStatusCode();
+        if (cacheableStatuses.contains(status)) {
+        	// these response codes MAY be cached
+        	cacheable = true;
+        } else if (uncacheableStatuses.contains(status)) {
+        	return false;
+        } else if (unknownStatusCode(status)) {
+        	// a response with an unknown status code MUST NOT be
+        	// cached
+        	return false;
         }
-
+        
         Header contentLength = response.getFirstHeader(HTTP.CONTENT_LEN);
         if (contentLength != null) {
             int contentLengthValue = Integer.parseInt(contentLength.getValue());
@@ -148,13 +149,22 @@ class ResponseCachingPolicy {
         return (cacheable || isExplicitlyCacheable(response));
     }
 
-    protected boolean isExplicitlyNonCacheable(HttpResponse response) {
+    private boolean unknownStatusCode(int status) {
+    	if (status >= 100 && status <= 101) return false;
+    	if (status >= 200 && status <= 206) return false;
+    	if (status >= 300 && status <= 307) return false;
+    	if (status >= 400 && status <= 417) return false;
+    	if (status >= 500 && status <= 505) return false;
+		return true;
+	}
+
+	protected boolean isExplicitlyNonCacheable(HttpResponse response) {
         Header[] cacheControlHeaders = response.getHeaders(HeaderConstants.CACHE_CONTROL);
         for (Header header : cacheControlHeaders) {
             for (HeaderElement elem : header.getElements()) {
                 if (HeaderConstants.CACHE_CONTROL_NO_STORE.equals(elem.getName())
                         || HeaderConstants.CACHE_CONTROL_NO_CACHE.equals(elem.getName())
-                        || (sharedCache && "private".equals(elem.getName()))) {
+                        || (sharedCache && HeaderConstants.PRIVATE.equals(elem.getName()))) {
                     return true;
                 }
             }
@@ -179,8 +189,10 @@ class ResponseCachingPolicy {
     protected boolean isExplicitlyCacheable(HttpResponse response) {
         if (response.getFirstHeader(HeaderConstants.EXPIRES) != null)
             return true;
-        String[] cacheableParams = { "max-age", "s-maxage",
-                "must-revalidate", "proxy-revalidate", "public"
+        String[] cacheableParams = { HeaderConstants.CACHE_CONTROL_MAX_AGE, "s-maxage",
+                HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE,
+                HeaderConstants.CACHE_CONTROL_PROXY_REVALIDATE,
+                HeaderConstants.PUBLIC
         };
         return hasCacheControlParameterFrom(response, cacheableParams);
     }
@@ -198,13 +210,13 @@ class ResponseCachingPolicy {
             log.debug("Response was not cacheable.");
             return false;
         }
-        
-        String[] uncacheableRequestDirectives = { "no-store" };
+
+        String[] uncacheableRequestDirectives = { HeaderConstants.CACHE_CONTROL_NO_STORE };
         if (hasCacheControlParameterFrom(request,uncacheableRequestDirectives)) {
             return false;
         }
 
-        if (request.getRequestLine().getUri().contains("?") && 
+        if (request.getRequestLine().getUri().contains("?") &&
             (!isExplicitlyCacheable(response) || from1_0Origin(response))) {
             log.debug("Response was not cacheable.");
             return false;
@@ -213,12 +225,12 @@ class ResponseCachingPolicy {
         if (expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(response)) {
             return false;
         }
-        
+
         if (sharedCache) {
-            Header[] authNHeaders = request.getHeaders("Authorization");
+            Header[] authNHeaders = request.getHeaders(HeaderConstants.AUTHORIZATION);
             if (authNHeaders != null && authNHeaders.length > 0) {
                 String[] authCacheableParams = {
-                        "s-maxage", "must-revalidate", "public"
+                        "s-maxage", HeaderConstants.CACHE_CONTROL_MUST_REVALIDATE, HeaderConstants.PUBLIC
                 };
                 return hasCacheControlParameterFrom(response, authCacheableParams);
             }
@@ -230,9 +242,9 @@ class ResponseCachingPolicy {
 
     private boolean expiresHeaderLessOrEqualToDateHeaderAndNoCacheControl(
             HttpResponse response) {
-        if (response.getFirstHeader("Cache-Control") != null) return false;
-        Header expiresHdr = response.getFirstHeader("Expires");
-        Header dateHdr = response.getFirstHeader("Date");
+        if (response.getFirstHeader(HeaderConstants.CACHE_CONTROL) != null) return false;
+        Header expiresHdr = response.getFirstHeader(HeaderConstants.EXPIRES);
+        Header dateHdr = response.getFirstHeader(HTTP.DATE_HEADER);
         if (expiresHdr == null || dateHdr == null) return false;
         try {
             Date expires = DateUtils.parseDate(expiresHdr.getValue());
@@ -244,12 +256,12 @@ class ResponseCachingPolicy {
     }
 
     private boolean from1_0Origin(HttpResponse response) {
-        Header via = response.getFirstHeader("Via");
+        Header via = response.getFirstHeader(HeaderConstants.VIA);
         if (via != null) {
             for(HeaderElement elt : via.getElements()) {
                 String proto = elt.toString().split("\\s")[0];
                 if (proto.contains("/")) {
-                    return proto.equals("HTTP/1.0"); 
+                    return proto.equals("HTTP/1.0");
                 } else {
                     return proto.equals("1.0");
                 }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProtocolCompliance.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProtocolCompliance.java
index d5de714..fd4bfc7 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProtocolCompliance.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ResponseProtocolCompliance.java
@@ -26,12 +26,14 @@
  */
 package org.apache.http.impl.client.cache;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
 import org.apache.http.Header;
 import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
 import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
@@ -46,6 +48,7 @@ import org.apache.http.impl.cookie.DateParseException;
 import org.apache.http.impl.cookie.DateUtils;
 import org.apache.http.message.BasicHeader;
 import org.apache.http.protocol.HTTP;
+import org.apache.http.util.EntityUtils;
 
 /**
  * @since 4.1
@@ -53,6 +56,10 @@ import org.apache.http.protocol.HTTP;
 @Immutable
 class ResponseProtocolCompliance {
 
+    private static final String UNEXPECTED_100_CONTINUE = "The incoming request did not contain a "
+                    + "100-continue header, but the response was a Status 100, continue.";
+    private static final String UNEXPECTED_PARTIAL_CONTENT = "partial content was returned for a request that did not ask for it";
+
     /**
      * When we get a response from a down stream server (Origin Server)
      * we attempt to see if it is HTTP 1.1 Compliant and if not, attempt to
@@ -60,20 +67,15 @@ class ResponseProtocolCompliance {
      *
      * @param request The {@link HttpRequest} that generated an origin hit and response
      * @param response The {@link HttpResponse} from the origin server
-     * @throws ClientProtocolException when we are unable to 'convert' the response to a compliant one
+     * @throws IOException Bad things happened
      */
     public void ensureProtocolCompliance(HttpRequest request, HttpResponse response)
-            throws ClientProtocolException {
+            throws IOException {
         if (backendResponseMustNotHaveBody(request, response)) {
+            consumeBody(response);
             response.setEntity(null);
         }
 
-        authenticationRequiredDidNotHaveAProxyAuthenticationHeader(request, response);
-
-        notAllowedResponseDidNotHaveAnAllowHeader(request, response);
-
-        unauthorizedResponseDidNotHaveAWWWAuthenticateHeader(request, response);
-
         requestDidNotExpect100ContinueButResponseIsOne(request, response);
 
         transferEncodingIsNotReturnedTo1_0Client(request, response);
@@ -83,7 +85,7 @@ class ResponseProtocolCompliance {
         ensure200ForOPTIONSRequestWithNoBodyHasContentLengthZero(request, response);
 
         ensure206ContainsDateHeader(response);
-        
+
         ensure304DoesNotContainExtraEntityHeaders(response);
 
         identityIsNotUsedInContentEncoding(response);
@@ -91,30 +93,40 @@ class ResponseProtocolCompliance {
         warningsWithNonMatchingWarnDatesAreRemoved(response);
     }
 
+    private void consumeBody(HttpResponse response) throws IOException {
+        HttpEntity body = response.getEntity();
+        if (body != null) EntityUtils.consume(body);
+    }
+
     private void warningsWithNonMatchingWarnDatesAreRemoved(
             HttpResponse response) {
         Date responseDate = null;
         try {
-            responseDate = DateUtils.parseDate(response.getFirstHeader("Date").getValue());
+            responseDate = DateUtils.parseDate(response.getFirstHeader(HTTP.DATE_HEADER).getValue());
         } catch (DateParseException e) {
+            //Empty On Purpose
         }
+
         if (responseDate == null) return;
-        Header[] warningHeaders = response.getHeaders("Warning");
+
+        Header[] warningHeaders = response.getHeaders(HeaderConstants.WARNING);
+
         if (warningHeaders == null || warningHeaders.length == 0) return;
+
         List<Header> newWarningHeaders = new ArrayList<Header>();
         boolean modified = false;
         for(Header h : warningHeaders) {
             for(WarningValue wv : WarningValue.getWarningValues(h)) {
                 Date warnDate = wv.getWarnDate();
                 if (warnDate == null || warnDate.equals(responseDate)) {
-                    newWarningHeaders.add(new BasicHeader("Warning",wv.toString()));
+                    newWarningHeaders.add(new BasicHeader(HeaderConstants.WARNING,wv.toString()));
                 } else {
                     modified = true;
                 }
             }
         }
         if (modified) {
-            response.removeHeaders("Warning");
+            response.removeHeaders(HeaderConstants.WARNING);
             for(Header h : newWarningHeaders) {
                 response.addHeader(h);
             }
@@ -122,7 +134,7 @@ class ResponseProtocolCompliance {
     }
 
     private void identityIsNotUsedInContentEncoding(HttpResponse response) {
-        Header[] hdrs = response.getHeaders("Content-Encoding");
+        Header[] hdrs = response.getHeaders(HTTP.CONTENT_ENCODING);
         if (hdrs == null || hdrs.length == 0) return;
         List<Header> newHeaders = new ArrayList<Header>();
         boolean modified = false;
@@ -140,46 +152,16 @@ class ResponseProtocolCompliance {
             }
             String newHeaderValue = buf.toString();
             if (!"".equals(newHeaderValue)) {
-                newHeaders.add(new BasicHeader("Content-Encoding", newHeaderValue));
+                newHeaders.add(new BasicHeader(HTTP.CONTENT_ENCODING, newHeaderValue));
             }
         }
         if (!modified) return;
-        response.removeHeaders("Content-Encoding");
+        response.removeHeaders(HTTP.CONTENT_ENCODING);
         for (Header h : newHeaders) {
             response.addHeader(h);
         }
     }
 
-    private void authenticationRequiredDidNotHaveAProxyAuthenticationHeader(HttpRequest request,
-            HttpResponse response) throws ClientProtocolException {
-        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED)
-            return;
-
-        if (response.getFirstHeader(HeaderConstants.PROXY_AUTHENTICATE) == null)
-            throw new ClientProtocolException(
-                    "407 Response did not contain a Proxy-Authentication header");
-    }
-
-    private void notAllowedResponseDidNotHaveAnAllowHeader(HttpRequest request,
-            HttpResponse response) throws ClientProtocolException {
-        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_METHOD_NOT_ALLOWED)
-            return;
-
-        if (response.getFirstHeader(HeaderConstants.ALLOW) == null)
-            throw new ClientProtocolException("405 Response did not contain an Allow header.");
-    }
-
-    private void unauthorizedResponseDidNotHaveAWWWAuthenticateHeader(HttpRequest request,
-            HttpResponse response) throws ClientProtocolException {
-        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_UNAUTHORIZED)
-            return;
-
-        if (response.getFirstHeader(HeaderConstants.WWW_AUTHENTICATE) == null) {
-            throw new ClientProtocolException(
-                    "401 Response did not contain required WWW-Authenticate challenge header");
-        }
-    }
-
     private void ensure206ContainsDateHeader(HttpResponse response) {
         if (response.getFirstHeader(HTTP.DATE_HEADER) == null) {
             response.addHeader(HTTP.DATE_HEADER, DateUtils.formatDate(new Date()));
@@ -188,15 +170,13 @@ class ResponseProtocolCompliance {
     }
 
     private void ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(HttpRequest request,
-            HttpResponse response) throws ClientProtocolException {
-        if (request.getFirstHeader(HeaderConstants.RANGE) != null)
+            HttpResponse response) throws IOException {
+        if (request.getFirstHeader(HeaderConstants.RANGE) != null
+                || response.getStatusLine().getStatusCode() != HttpStatus.SC_PARTIAL_CONTENT)
             return;
 
-        if (response.getFirstHeader(HeaderConstants.CONTENT_RANGE) != null) {
-            throw new ClientProtocolException(
-                    "Content-Range was returned for a request that did not ask for a Content-Range.");
-        }
-
+        consumeBody(response);
+        throw new ClientProtocolException(UNEXPECTED_PARTIAL_CONTENT);
     }
 
     private void ensure200ForOPTIONSRequestWithNoBodyHasContentLengthZero(HttpRequest request,
@@ -215,9 +195,9 @@ class ResponseProtocolCompliance {
     }
 
     private void ensure304DoesNotContainExtraEntityHeaders(HttpResponse response) {
-        String[] disallowedEntityHeaders = { "Allow", "Content-Encoding",
-                "Content-Language", "Content-Length", "Content-MD5",
-                "Content-Range", "Content-Type", "Last-Modified"
+        String[] disallowedEntityHeaders = { HeaderConstants.ALLOW, HTTP.CONTENT_ENCODING,
+                "Content-Language", HTTP.CONTENT_LEN, "Content-MD5",
+                "Content-Range", HTTP.CONTENT_TYPE, HeaderConstants.LAST_MODIFIED
         };
         if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
             for(String hdr : disallowedEntityHeaders) {
@@ -234,26 +214,18 @@ class ResponseProtocolCompliance {
     }
 
     private void requestDidNotExpect100ContinueButResponseIsOne(HttpRequest request,
-            HttpResponse response) throws ClientProtocolException {
+            HttpResponse response) throws IOException {
         if (response.getStatusLine().getStatusCode() != HttpStatus.SC_CONTINUE) {
             return;
         }
 
-        if (!requestWasWrapped(request)) {
-            return;
-        }
-
-        ProtocolVersion originalProtocol = getOriginalRequestProtocol((RequestWrapper) request);
-
-        if (originalProtocol.compareToVersion(HttpVersion.HTTP_1_1) >= 0) {
-            return;
-        }
-
-        if (originalRequestDidNotExpectContinue((RequestWrapper) request)) {
-            throw new ClientProtocolException("The incoming request did not contain a "
-                    + "100-continue header, but the response was a Status 100, continue.");
-
+        HttpRequest originalRequest = requestWasWrapped(request) ?
+                ((RequestWrapper)request).getOriginal() : request;
+        if (originalRequest instanceof HttpEntityEnclosingRequest) {
+            if (((HttpEntityEnclosingRequest)originalRequest).expectContinue()) return;
         }
+        consumeBody(response);
+        throw new ClientProtocolException(UNEXPECTED_100_CONTINUE);
     }
 
     private void transferEncodingIsNotReturnedTo1_0Client(HttpRequest request, HttpResponse response) {
@@ -275,18 +247,6 @@ class ResponseProtocolCompliance {
         response.removeHeaders(HTTP.TRANSFER_ENCODING);
     }
 
-    private boolean originalRequestDidNotExpectContinue(RequestWrapper request) {
-
-        try {
-            HttpEntityEnclosingRequest original = (HttpEntityEnclosingRequest) request
-                    .getOriginal();
-
-            return !original.expectContinue();
-        } catch (ClassCastException ex) {
-            return false;
-        }
-    }
-
     private ProtocolVersion getOriginalRequestProtocol(RequestWrapper request) {
         return request.getOriginal().getProtocolVersion();
     }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Variant.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Variant.java
index 0eabe68..77bcc04 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Variant.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/Variant.java
@@ -44,11 +44,11 @@ class Variant {
     public String getVariantKey() {
         return variantKey;
     }
-    
+
     public String getCacheKey() {
         return cacheKey;
     }
-    
+
     public HttpCacheEntry getEntry() {
         return entry;
     }
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ehcache/EhcacheHttpCacheStorage.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ehcache/EhcacheHttpCacheStorage.java
index 118b7bc..bf188c9 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ehcache/EhcacheHttpCacheStorage.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ehcache/EhcacheHttpCacheStorage.java
@@ -47,12 +47,12 @@ import org.apache.http.impl.client.cache.DefaultHttpCacheEntrySerializer;
  * In particular, this backend allows for spillover to disk, where the
  * cache can be effectively larger than memory, and cached responses are
  * paged into and out of memory from disk as needed.</p>
- * 
+ *
  * <p><b>N.B.</b> Since the Ehcache is configured ahead of time with a
  * maximum number of cache entries, this effectively ignores the
  * {@link CacheConfig#setMaxCacheEntries(int) maximum cache entries}
  * specified by a provided {@link CacheConfig}.</p>
- * 
+ *
  * <p>Please refer to the <a href="http://ehcache.org/documentation/index.html">
  * Ehcache documentation</a> for details on how to configure the Ehcache
  * itself.</p>
@@ -154,4 +154,4 @@ public class EhcacheHttpCacheStorage implements HttpCacheStorage {
         }while(numRetries <= maxUpdateRetries);
         throw new HttpCacheUpdateException("Failed to update");
     }
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ehcache/package.html b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ehcache/package.html
index 2cdcbca..9a27360 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ehcache/package.html
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/ehcache/package.html
@@ -12,7 +12,7 @@ to you under the Apache License, Version 2.0 (the
 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
@@ -31,7 +31,7 @@ information on the Apache Software Foundation, please see
 
 <p>
 This package contains a storage backend based on
-<a href="http://ehcache.org/">Ehcache</a> 
+<a href="http://ehcache.org/">Ehcache</a>
 that can be plugged into
 a {@link org.apache.http.impl.client.cache.CachingHttpClient} and
 used for storing cache entries.
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/KeyHashingScheme.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/KeyHashingScheme.java
new file mode 100644
index 0000000..06d5212
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/KeyHashingScheme.java
@@ -0,0 +1,47 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */package org.apache.http.impl.client.cache.memcached;
+
+import org.apache.http.client.cache.HttpCacheStorage;
+
+/**
+ * Since the {@link HttpCacheStorage} interface expects to use variant-annotated
+ * URLs for its storage keys, but Memcached has a maximum key size, we need to
+ * support mapping storage keys to cache keys. Clients can implement this
+ * interface to change the way the mapping is done (for example, to add a prefix
+ * to all cache keys to provide a form of memcached namespacing).
+ */
+public interface KeyHashingScheme {
+
+    /** Maps a storage key to a cache key. The storage key is what
+     * the higher-level HTTP cache uses as a key; the cache key is what
+     * we use as a key for talking to memcached.
+     * @param storageKey what the higher-level HTTP cache wants to use
+     *   as its key for looking up cache entries
+     * @return a cache key suitable for use with memcached
+     */
+    public String hash(String storageKey);
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntry.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntry.java
new file mode 100644
index 0000000..bf2217a
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntry.java
@@ -0,0 +1,75 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import org.apache.http.client.cache.HttpCacheEntry;
+
+/**
+ * Provides for serialization and deserialization of higher-level
+ * {@link HttpCacheEntry} objects into byte arrays suitable for
+ * storage in memcached. Clients wishing to change the serialization
+ * mechanism from the provided defaults should implement this
+ * interface as well as {@link MemcachedCacheEntryFactory}.
+ */
+public interface MemcachedCacheEntry {
+
+    /**
+     * Returns a serialized representation of the current cache entry.
+     */
+    byte[] toByteArray();
+
+    /**
+     * Returns the storage key associated with this entry. May return
+     * <code>null</code> if this is an "unset" instance waiting to be
+     * {@link #set(byte[])} with a serialized representation.
+     */
+    String getStorageKey();
+
+    /**
+     * Returns the {@link HttpCacheEntry} associated with this entry.
+     * May return <code>null</code> if this is an "unset" instance
+     * waiting to be {@link #set(byte[])} with a serialized
+     * representation.
+     */
+    HttpCacheEntry getHttpCacheEntry();
+
+    /**
+     * Given a serialized representation of a {@link MemcachedCacheEntry},
+     * attempt to reconstitute the storage key and {@link HttpCacheEntry}
+     * represented therein. After a successful call to this method, this
+     * object should return updated (as appropriate) values for
+     * {@link #getStorageKey()} and {@link #getHttpCacheEntry()}. This
+     * should be viewed as an atomic operation on the
+     * <code>MemcachedCacheEntry</code>.
+     * @param bytes serialized representation
+     * @throws {@link MemcachedSerializationException} if deserialization
+     *   fails. In this case, the prior values for {{@link #getStorageKey()}
+     *   and {@link #getHttpCacheEntry()} should remain unchanged.
+     */
+    void set(byte[] bytes);
+
+}
\ No newline at end of file
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryFactory.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryFactory.java
new file mode 100644
index 0000000..9cc4d52
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryFactory.java
@@ -0,0 +1,62 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import org.apache.http.client.cache.HttpCacheEntry;
+
+/**
+ * Creates {@link MemcachedCacheEntry} instances that can be used for
+ * serializing and deserializing {@link HttpCacheEntry} instances for
+ * storage in memcached.
+ */
+public interface MemcachedCacheEntryFactory {
+
+    /**
+     * Creates a new {@link MemcachedCacheEntry} for storing the
+     * given {@link HttpCacheEntry} under the given storage key. Since
+     * we are hashing storage keys into cache keys to accommodate
+     * limitations in memcached's key space, it is possible to have
+     * cache collisions. Therefore, we store the storage key along
+     * with the <code>HttpCacheEntry</code> so it can be compared
+     * on retrieval and thus detect collisions.
+     * @param storageKey storage key under which the entry will 
+     *   be logically stored
+     * @param entry the cache entry to store
+     * @return a {@link MemcachedCacheEntry} ready to provide
+     *   a serialized representation
+     */
+    MemcachedCacheEntry getMemcachedCacheEntry(String storageKey, HttpCacheEntry entry);
+    
+    /**
+     * Creates an "unset" {@link MemcachedCacheEntry} ready to accept
+     * a serialized representation via {@link MemcachedCacheEntry#set(byte[])}
+     * and deserialize it into a storage key and a {@link HttpCacheEntry}.
+     * @return <code>MemcachedCacheEntry</code>
+     */
+    MemcachedCacheEntry getUnsetCacheEntry();
+
+}
\ No newline at end of file
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryFactoryImpl.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryFactoryImpl.java
new file mode 100644
index 0000000..03c939c
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryFactoryImpl.java
@@ -0,0 +1,43 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import org.apache.http.client.cache.HttpCacheEntry;
+
+/**
+ * Default implementation of {@link MemcachedCacheEntryFactory}.
+ */
+public class MemcachedCacheEntryFactoryImpl implements MemcachedCacheEntryFactory {
+
+    public MemcachedCacheEntry getMemcachedCacheEntry(String key, HttpCacheEntry entry) {
+        return new MemcachedCacheEntryImpl(key, entry);
+    }
+
+    public MemcachedCacheEntry getUnsetCacheEntry() {
+        return new MemcachedCacheEntryImpl(null, null);
+    }
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryImpl.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryImpl.java
new file mode 100644
index 0000000..4ddb471
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedCacheEntryImpl.java
@@ -0,0 +1,108 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import org.apache.http.client.cache.HttpCacheEntry;
+
+/**
+ * Default implementation of {@link MemcachedCacheEntry}. This implementation
+ * simply uses Java serialization to serialize the storage key followed by
+ * the {@link HttpCacheEntry} into a byte array.
+ */
+public class MemcachedCacheEntryImpl implements MemcachedCacheEntry {
+
+    private String key;
+    private HttpCacheEntry httpCacheEntry;
+
+    public MemcachedCacheEntryImpl(String key, HttpCacheEntry httpCacheEntry) {
+        this.key = key;
+        this.httpCacheEntry = httpCacheEntry;
+    }
+
+    public MemcachedCacheEntryImpl() {
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#toByteArray()
+     */
+    synchronized public byte[] toByteArray() {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        ObjectOutputStream oos;
+        try {
+            oos = new ObjectOutputStream(bos);
+            oos.writeObject(this.key);
+            oos.writeObject(this.httpCacheEntry);
+            oos.close();
+        } catch (IOException ioe) {
+            throw new MemcachedSerializationException(ioe);
+        }
+        return bos.toByteArray();
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#getKey()
+     */
+    public synchronized String getStorageKey() {
+        return key;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#getHttpCacheEntry()
+     */
+    public synchronized HttpCacheEntry getHttpCacheEntry() {
+        return httpCacheEntry;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.http.impl.client.cache.memcached.MemcachedCacheEntry#set(byte[])
+     */
+    synchronized public void set(byte[] bytes) {
+        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+        ObjectInputStream ois;
+        String s;
+        HttpCacheEntry entry;
+        try {
+            ois = new ObjectInputStream(bis);
+            s = (String)ois.readObject();
+            entry = (HttpCacheEntry)ois.readObject();
+            ois.close();
+            bis.close();
+        } catch (IOException ioe) {
+            throw new MemcachedSerializationException(ioe);
+        } catch (ClassNotFoundException cnfe) {
+            throw new MemcachedSerializationException(cnfe);
+        }
+        this.key = s;
+        this.httpCacheEntry = entry;
+    }
+
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
index 573c796..e8a7cf0 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedHttpCacheStorage.java
@@ -26,24 +26,24 @@
  */
 package org.apache.http.impl.client.cache.memcached;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.net.InetSocketAddress;
 
 import net.spy.memcached.CASResponse;
 import net.spy.memcached.CASValue;
 import net.spy.memcached.MemcachedClient;
 import net.spy.memcached.MemcachedClientIF;
+import net.spy.memcached.OperationTimeoutException;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.http.client.cache.HttpCacheEntry;
 import org.apache.http.client.cache.HttpCacheEntrySerializer;
 import org.apache.http.client.cache.HttpCacheUpdateException;
 import org.apache.http.client.cache.HttpCacheStorage;
 import org.apache.http.client.cache.HttpCacheUpdateCallback;
 import org.apache.http.impl.client.cache.CacheConfig;
-import org.apache.http.impl.client.cache.DefaultHttpCacheEntrySerializer;
+import org.apache.http.impl.client.cache.CachingHttpClient;
 
 /**
  * <p>This class is a storage backend that uses an external <i>memcached</i>
@@ -54,28 +54,43 @@ import org.apache.http.impl.client.cache.DefaultHttpCacheEntrySerializer;
  * <li>in-memory cached objects can survive an application restart since
  * they are held in a separate process</li>
  * <li>it becomes possible for several cooperating applications to share
- * a large <i>memcached</i> farm together, effectively providing cache
- * peering of a sort</li>
+ * a large <i>memcached</i> farm together</li>
  * </ol>
  * Note that in a shared memcached pool setting you may wish to make use
- * of the Ketama consistent hashing algorithm to reduce the number of 
+ * of the Ketama consistent hashing algorithm to reduce the number of
  * cache misses that might result if one of the memcached cluster members
  * fails (see the <a href="http://dustin.github.com/java-memcached-client/apidocs/net/spy/memcached/KetamaConnectionFactory.html">
  * KetamaConnectionFactory</a>).
  * </p>
  * 
+ * <p>Because memcached places limits on the size of its keys, we need to
+ * introduce a key hashing scheme to map the annotated URLs the higher-level
+ * {@link CachingHttpClient} wants to use as keys onto ones that are suitable
+ * for use with memcached. Please see {@link KeyHashingScheme} if you would
+ * like to use something other than the provided {@link SHA256KeyHashingScheme}.</p>
+ * 
+ * <p>Because this hashing scheme can potentially result in key collisions (though
+ * highly unlikely), we need to store the higher-level logical storage key along
+ * with the {@link HttpCacheEntry} so that we can re-check it on retrieval. There
+ * is a default serialization scheme provided for this, although you can provide
+ * your own implementations of {@link MemcachedCacheEntry} and
+ * {@link MemcachedCacheEntryFactory} to customize this serialization.</p>
+ *
  * <p>Please refer to the <a href="http://code.google.com/p/memcached/wiki/NewStart">
  * memcached documentation</a> and in particular to the documentation for
  * the <a href="http://code.google.com/p/spymemcached/">spymemcached
  * documentation</a> for details about how to set up and configure memcached
  * and the Java client used here, respectively.</p>
- * 
+ *
  * @since 4.1
  */
 public class MemcachedHttpCacheStorage implements HttpCacheStorage {
 
+    private static final Log log = LogFactory.getLog(MemcachedHttpCacheStorage.class);
+    
     private final MemcachedClientIF client;
-    private final HttpCacheEntrySerializer serializer;
+    private final KeyHashingScheme keyHashingScheme;
+    private final MemcachedCacheEntryFactory memcachedCacheEntryFactory;
     private final int maxUpdateRetries;
 
     /**
@@ -84,7 +99,7 @@ public class MemcachedHttpCacheStorage implements HttpCacheStorage {
      * just have a single local memcached instance running on the same
      * machine as your application, for example.
      * @param address where the <i>memcached</i> daemon is running
-     * @throws IOException
+     * @throws IOException in case of an error
      */
     public MemcachedHttpCacheStorage(InetSocketAddress address) throws IOException {
         this(new MemcachedClient(address));
@@ -96,74 +111,163 @@ public class MemcachedHttpCacheStorage implements HttpCacheStorage {
      * @param cache client to use for communicating with <i>memcached</i>
      */
     public MemcachedHttpCacheStorage(MemcachedClientIF cache) {
-        this(cache, new CacheConfig(), new DefaultHttpCacheEntrySerializer());
+        this(cache, new CacheConfig(), new MemcachedCacheEntryFactoryImpl(),
+                new SHA256KeyHashingScheme());
     }
 
     /**
      * Create a storage backend using the given <i>memcached</i> client and
      * applying the given cache configuration and cache entry serialization
-     * mechanism.
+     * mechanism. <b>Deprecation note:</b> In the process of fixing a bug
+     * based on the need to hash logical storage keys onto memcached cache
+     * keys, the serialization process was revamped. This constructor still
+     * works, but the serializer argument will be ignored and default
+     * implementations of the new framework will be used. You can still
+     * provide custom serialization by using the
+     * {@link #MemcachedHttpCacheStorage(MemcachedClientIF, CacheConfig,
+     * MemcachedCacheEntryFactory, KeyHashingScheme)} constructor.
      * @param client how to talk to <i>memcached</i>
      * @param config apply HTTP cache-related options
-     * @param serializer how to serialize the cache entries before writing
-     *   them out to <i>memcached</i>. The provided {@link
-     *   DefaultHttpCacheEntrySerializer} is a fine serialization mechanism
-     *   to use here.
+     * @param serializer <b>ignored</b>
+     *
+     * @deprecated (4.2) do not use
      */
+    @Deprecated
     public MemcachedHttpCacheStorage(MemcachedClientIF client, CacheConfig config,
             HttpCacheEntrySerializer serializer) {
+        this(client, config, new MemcachedCacheEntryFactoryImpl(),
+                new SHA256KeyHashingScheme());
+    }
+
+    /**
+     * Create a storage backend using the given <i>memcached</i> client and
+     * applying the given cache configuration, serialization, and hashing
+     * mechanisms.
+     * @param client how to talk to <i>memcached</i>
+     * @param config apply HTTP cache-related options
+     * @param memcachedCacheEntryFactory Factory pattern used for obtaining
+     *   instances of alternative cache entry serialization mechanisms
+     * @param keyHashingScheme how to map higher-level logical "storage keys"
+     *   onto "cache keys" suitable for use with memcached
+     */
+    public MemcachedHttpCacheStorage(MemcachedClientIF client, CacheConfig config,
+            MemcachedCacheEntryFactory memcachedCacheEntryFactory,
+            KeyHashingScheme keyHashingScheme) {
         this.client = client;
         this.maxUpdateRetries = config.getMaxUpdateRetries();
-        this.serializer = serializer;
+        this.memcachedCacheEntryFactory = memcachedCacheEntryFactory;
+        this.keyHashingScheme = keyHashingScheme;
     }
-
+    
     public void putEntry(String url, HttpCacheEntry entry) throws IOException  {
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        serializer.writeTo(entry, bos);
-        client.set(url, 0, bos.toByteArray());
+        byte[] bytes = serializeEntry(url, entry);
+        String key = getCacheKey(url);
+        if (key == null) return;
+        try {
+            client.set(key, 0, bytes);
+        } catch (OperationTimeoutException ex) {
+            throw new MemcachedOperationTimeoutException(ex);
+        }
     }
 
-    public HttpCacheEntry getEntry(String url) throws IOException {
-        byte[] data = (byte[]) client.get(url);
-        if (null == data)
+    private String getCacheKey(String url) {
+        try {
+            return keyHashingScheme.hash(url);
+        } catch (MemcachedKeyHashingException mkhe) {
+            return null;
+        }
+    }
+
+    private byte[] serializeEntry(String url, HttpCacheEntry hce) throws IOException {
+        MemcachedCacheEntry mce = memcachedCacheEntryFactory.getMemcachedCacheEntry(url, hce);
+        try {
+            return mce.toByteArray();
+        } catch (MemcachedSerializationException mse) {
+            IOException ioe = new IOException();
+            ioe.initCause(mse);
+            throw ioe;
+        }
+    }
+
+    private byte[] convertToByteArray(Object o) {
+        if (o == null) return null;
+        if (!(o instanceof byte[])) {
+            log.warn("got a non-bytearray back from memcached: " + o);
             return null;
-        InputStream bis = new ByteArrayInputStream(data);
-        return serializer.readFrom(bis);
+        }
+        return (byte[])o;
+    }
+
+    private MemcachedCacheEntry reconstituteEntry(Object o) throws IOException {
+        byte[] bytes = convertToByteArray(o);
+        if (bytes == null) return null;
+        MemcachedCacheEntry mce = memcachedCacheEntryFactory.getUnsetCacheEntry();
+        try {
+            mce.set(bytes);
+        } catch (MemcachedSerializationException mse) {
+            return null;
+        }
+        return mce;
+    }
+    
+    public HttpCacheEntry getEntry(String url) throws IOException {
+        String key = getCacheKey(url);
+        if (key == null) return null;
+        try {
+            MemcachedCacheEntry mce = reconstituteEntry(client.get(key));
+            if (mce == null || !url.equals(mce.getStorageKey())) return null;
+            return mce.getHttpCacheEntry();
+        } catch (OperationTimeoutException ex) {
+            throw new MemcachedOperationTimeoutException(ex);
+        }
     }
 
     public void removeEntry(String url) throws IOException {
-        client.delete(url);
+        String key = getCacheKey(url);
+        if (key == null) return;
+        try {
+            client.delete(key);
+        } catch (OperationTimeoutException ex) {
+            throw new MemcachedOperationTimeoutException(ex);
+        }
     }
 
     public void updateEntry(String url, HttpCacheUpdateCallback callback)
             throws HttpCacheUpdateException, IOException {
         int numRetries = 0;
-        do{
-
-        CASValue<Object> v = client.gets(url);
-        byte[] oldBytes = (v != null) ? (byte[]) v.getValue() : null;
-        HttpCacheEntry existingEntry = null;
-        if (oldBytes != null) {
-            ByteArrayInputStream bis = new ByteArrayInputStream(oldBytes);
-            existingEntry = serializer.readFrom(bis);
+        String key = getCacheKey(url);
+        if (key == null) {
+            throw new HttpCacheUpdateException("couldn't generate cache key");
         }
-        HttpCacheEntry updatedEntry = callback.update(existingEntry);
-
-        if (v == null) {
-            putEntry(url, updatedEntry);
-            return;
-
-        } else {
-            ByteArrayOutputStream bos = new ByteArrayOutputStream();
-            serializer.writeTo(updatedEntry, bos);
-            CASResponse casResult = client.cas(url, v.getCas(), bos.toByteArray());
-            if (casResult != CASResponse.OK) {
-                 numRetries++;
+        do {
+            try {
+                CASValue<Object> v = client.gets(key);
+                MemcachedCacheEntry mce = (v == null) ? null
+                        : reconstituteEntry(v.getValue());
+                if (mce != null && (!url.equals(mce.getStorageKey()))) {
+                    mce = null;
+                }
+                HttpCacheEntry existingEntry = (mce == null) ? null
+                        : mce.getHttpCacheEntry();
+                HttpCacheEntry updatedEntry = callback.update(existingEntry);
+
+                if (existingEntry == null) {
+                    putEntry(url, updatedEntry);
+                    return;
+
+                } else {
+                    byte[] updatedBytes = serializeEntry(url, updatedEntry);
+                    CASResponse casResult = client.cas(key, v.getCas(),
+                            updatedBytes);
+                    if (casResult != CASResponse.OK) {
+                        numRetries++;
+                    } else return;
+                }
+            } catch (OperationTimeoutException ex) {
+                throw new MemcachedOperationTimeoutException(ex);
             }
-            else return;
-        }
+        } while (numRetries <= maxUpdateRetries);
 
-    } while(numRetries <= maxUpdateRetries);
-    throw new HttpCacheUpdateException("Failed to update");
+        throw new HttpCacheUpdateException("Failed to update");
     }
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedKeyHashingException.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedKeyHashingException.java
new file mode 100644
index 0000000..fb84425
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedKeyHashingException.java
@@ -0,0 +1,41 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+/**
+ * Indicates a problem encountered when trying to map a
+ * logical "storage key" to a "cache key" suitable for use with
+ * memcached.
+ */
+public class MemcachedKeyHashingException extends RuntimeException {
+
+    private static final long serialVersionUID = -7553380015989141114L;
+
+    public MemcachedKeyHashingException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedOperationTimeoutException.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedOperationTimeoutException.java
new file mode 100644
index 0000000..7a5d147
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedOperationTimeoutException.java
@@ -0,0 +1,43 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import java.io.IOException;
+
+/**
+ * Raised when memcached times out on us.
+ */
+class MemcachedOperationTimeoutException extends IOException {
+
+    private static final long serialVersionUID = 1608334789051537010L;
+
+    public MemcachedOperationTimeoutException(final Throwable cause) {
+        super(cause.getMessage());
+        initCause(cause);
+    }
+
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedSerializationException.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedSerializationException.java
new file mode 100644
index 0000000..dd13580
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/MemcachedSerializationException.java
@@ -0,0 +1,41 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+/**
+ * Raised when there is a problem serializing or deserializing cache
+ * entries into a byte representation suitable for memcached storage.
+ */
+public class MemcachedSerializationException extends RuntimeException {
+
+    private static final long serialVersionUID = 2201652990656412236L;
+
+    public MemcachedSerializationException(Throwable cause) {
+        super(cause);
+    }
+
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/PrefixKeyHashingScheme.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/PrefixKeyHashingScheme.java
new file mode 100644
index 0000000..bc6ade3
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/PrefixKeyHashingScheme.java
@@ -0,0 +1,59 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+
+/**
+ * This is a {@link KeyHashingScheme} decorator that simply adds
+ * a known prefix to the results of another <code>KeyHashingScheme</code>.
+ * Primarily useful for namespacing a shared memcached cluster, for
+ * example.
+ */
+public class PrefixKeyHashingScheme implements KeyHashingScheme {
+
+    private String prefix;
+    private KeyHashingScheme backingScheme;
+
+    /**
+     * Creates a new {@link KeyHashingScheme} that prepends the given
+     * prefix to the results of hashes from the given backing scheme.
+     * Users should be aware that memcached has a fixed maximum key
+     * length, so the combination of this prefix plus the results of
+     * the backing hashing scheme must still fit within these limits.
+     * @param prefix
+     * @param backingScheme
+     */
+    public PrefixKeyHashingScheme(String prefix, KeyHashingScheme backingScheme) {
+        this.prefix = prefix;
+        this.backingScheme = backingScheme;
+    }
+
+    public String hash(String storageKey) {
+        return prefix + backingScheme.hash(storageKey);
+    }
+
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/SHA256KeyHashingScheme.java b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/SHA256KeyHashingScheme.java
new file mode 100644
index 0000000..0de84ab
--- /dev/null
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/SHA256KeyHashingScheme.java
@@ -0,0 +1,61 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * This is a {@link KeyHashingScheme} based on the
+ * <a href="http://en.wikipedia.org/wiki/SHA-2">SHA-256</a>
+ * algorithm. The hashes produced are hex-encoded SHA-256
+ * digests and hence are always 64-character hexadecimal
+ * strings.
+ */
+public class SHA256KeyHashingScheme implements KeyHashingScheme {
+
+    private static final Log log = LogFactory.getLog(SHA256KeyHashingScheme.class);
+    
+    public String hash(String key) {
+        MessageDigest md = getDigest();
+        md.update(key.getBytes());
+        return Hex.encodeHexString(md.digest());
+    }
+
+    private MessageDigest getDigest() {
+        try {
+            return MessageDigest.getInstance("SHA-256");
+        } catch (NoSuchAlgorithmException nsae) {
+            log.error("can't find SHA-256 implementation for cache key hashing");
+            throw new MemcachedKeyHashingException(nsae);
+        }
+    }
+}
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/package.html b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/package.html
index a5fd731..72c3285 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/package.html
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/memcached/package.html
@@ -12,7 +12,7 @@ to you under the Apache License, Version 2.0 (the
 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
@@ -31,7 +31,7 @@ information on the Apache Software Foundation, please see
 
 <p>
 This package contains a storage backend based on
-<a href="http://memcached.org/">memcached</a> 
+<a href="http://memcached.org/">memcached</a>
 that can be plugged into
 a {@link org.apache.http.impl.client.cache.CachingHttpClient} and
 used for storing cache entries.
diff --git a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/package.html b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/package.html
index 5847fa7..4c208e7 100644
--- a/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/package.html
+++ b/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/package.html
@@ -12,7 +12,7 @@ to you under the Apache License, Version 2.0 (the
 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
diff --git a/httpclient-cache/src/site/site.xml b/httpclient-cache/src/site/site.xml
index 463453f..cc8f293 100644
--- a/httpclient-cache/src/site/site.xml
+++ b/httpclient-cache/src/site/site.xml
@@ -25,12 +25,15 @@
    <http://www.apache.org/>.
  -->
 
-<project name="HttpClient">
+<project xmlns="http://maven.apache.org/DECORATION/1.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 http://maven.apache.org/xsd/decoration-1.0.0.xsd"
+	name="HttpClient">
 
   <body>
     <menu name="HttpClient Overview">
       <item name="Description" href="../index.html"/>
-      <item name="Examples" href="../examples.html"/>
+      <item name="Quick Start" href="../quickstart.html"/>
     </menu>
     <menu ref="modules" />
     <menu ref="reports"/>      
diff --git a/httpclient-cache/src/test/java/org/apache/http/client/cache/TestHttpCacheEntry.java b/httpclient-cache/src/test/java/org/apache/http/client/cache/TestHttpCacheEntry.java
index 928067c..edf359d 100644
--- a/httpclient-cache/src/test/java/org/apache/http/client/cache/TestHttpCacheEntry.java
+++ b/httpclient-cache/src/test/java/org/apache/http/client/cache/TestHttpCacheEntry.java
@@ -178,16 +178,6 @@ public class TestHttpCacheEntry {
     }
 
     @Test
-    public void mustProvideResource() {
-        try {
-            new HttpCacheEntry(new Date(), new Date(), statusLine,
-                    new Header[]{}, null);
-            fail("Should have thrown exception");
-        } catch (IllegalArgumentException expected) {
-        }
-    }
-
-    @Test
     public void canRetrieveOriginalStatusLine() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
                 new Header[]{}, mockResource);
@@ -199,21 +189,21 @@ public class TestHttpCacheEntry {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
                 new Header[]{}, mockResource);
         assertSame(statusLine.getProtocolVersion(),
-                entry.getProtocolVersion());        
+                entry.getProtocolVersion());
     }
 
     @Test
     public void reasonPhraseComesFromOriginalStatusLine() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
                 new Header[]{}, mockResource);
-        assertSame(statusLine.getReasonPhrase(), entry.getReasonPhrase());        
+        assertSame(statusLine.getReasonPhrase(), entry.getReasonPhrase());
     }
 
     @Test
     public void statusCodeComesFromOriginalStatusLine() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
                 new Header[]{}, mockResource);
-        assertEquals(statusLine.getStatusCode(), entry.getStatusCode());        
+        assertEquals(statusLine.getStatusCode(), entry.getStatusCode());
     }
 
     @Test
@@ -221,7 +211,7 @@ public class TestHttpCacheEntry {
         final Date requestDate = new Date();
         entry = new HttpCacheEntry(requestDate, new Date(), statusLine,
                 new Header[]{}, mockResource);
-        assertSame(requestDate, entry.getRequestDate());        
+        assertSame(requestDate, entry.getRequestDate());
     }
 
     @Test
@@ -229,14 +219,14 @@ public class TestHttpCacheEntry {
         final Date responseDate = new Date();
         entry = new HttpCacheEntry(new Date(), responseDate, statusLine,
                 new Header[]{}, mockResource);
-        assertSame(responseDate, entry.getResponseDate());        
+        assertSame(responseDate, entry.getResponseDate());
     }
 
     @Test
     public void canGetOriginalResource() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
                 new Header[]{}, mockResource);
-        assertSame(mockResource, entry.getResource());        
+        assertSame(mockResource, entry.getResource());
     }
 
     @Test
@@ -253,7 +243,7 @@ public class TestHttpCacheEntry {
             assertEquals(headers[i], result[i]);
         }
     }
-        
+
     @Test
     public void canConstructWithoutVariants() {
         new HttpCacheEntry(new Date(), new Date(), statusLine,
@@ -266,7 +256,7 @@ public class TestHttpCacheEntry {
                 new Header[]{}, mockResource,
                 new HashMap<String,String>());
     }
-    
+
     @Test
     public void canRetrieveOriginalVariantMap() {
         Map<String,String> variantMap = new HashMap<String,String>();
@@ -280,7 +270,7 @@ public class TestHttpCacheEntry {
         assertEquals("B", result.get("A"));
         assertEquals("D", result.get("C"));
     }
-    
+
     @Test
     public void retrievedVariantMapIsNotModifiable() {
         Map<String,String> variantMap = new HashMap<String,String>();
@@ -301,7 +291,7 @@ public class TestHttpCacheEntry {
         } catch (UnsupportedOperationException expected) {
         }
     }
-    
+
     @Test
     public void canConvertToString() {
         entry = new HttpCacheEntry(new Date(), new Date(), statusLine,
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java
index cab7158..b0c3ba5 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/AbstractProtocolTest.java
@@ -71,7 +71,7 @@ public abstract class AbstractProtocolTest {
 
         params = new CacheConfig();
         params.setMaxCacheEntries(MAX_ENTRIES);
-        params.setMaxObjectSizeBytes(MAX_BYTES);
+        params.setMaxObjectSize(MAX_BYTES);
         cache = new BasicHttpCache(params);
         mockBackend = EasyMock.createMock(HttpClient.class);
         mockCache = EasyMock.createMock(HttpCache.class);
@@ -113,7 +113,7 @@ public abstract class AbstractProtocolTest {
 
         mockCache.flushInvalidatedCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class));
         EasyMock.expectLastCall().anyTimes();
-        
+
         mockCache.flushInvalidatedCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpResponse.class));
         EasyMock.expectLastCall().anyTimes();
     }
@@ -127,4 +127,4 @@ public abstract class AbstractProtocolTest {
         super();
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/ConsumableInputStream.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/ConsumableInputStream.java
new file mode 100644
index 0000000..a6dd3f4
--- /dev/null
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/ConsumableInputStream.java
@@ -0,0 +1,58 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ConsumableInputStream extends InputStream {
+
+    private ByteArrayInputStream buf;
+    private boolean closed = false;
+
+    public ConsumableInputStream(ByteArrayInputStream buf) {
+        this.buf = buf;
+    }
+
+    public int read() throws IOException {
+        return buf.read();
+    }
+
+    @Override
+    public void close() {
+        closed = true;
+        try {
+            buf.close();
+        } catch (IOException e) {
+        }
+    }
+
+    public boolean wasClosed() {
+        return closed;
+    }
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/Counter.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/Counter.java
index ad9b22b..78e403e 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/Counter.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/Counter.java
@@ -38,4 +38,4 @@ public class Counter {
         return count;
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DoNotTestProtocolRequirements.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DoNotTestProtocolRequirements.java
index e3a5037..4ae512b 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DoNotTestProtocolRequirements.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DoNotTestProtocolRequirements.java
@@ -75,7 +75,7 @@ public class DoNotTestProtocolRequirements {
 
         originResponse = make200Response();
         CacheConfig params = new CacheConfig();
-        params.setMaxObjectSizeBytes(MAX_BYTES);
+        params.setMaxObjectSize(MAX_BYTES);
         params.setMaxCacheEntries(MAX_ENTRIES);
 
         HttpCache cache = new BasicHttpCache(params);
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyHttpClient.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyHttpClient.java
new file mode 100644
index 0000000..b829c6b
--- /dev/null
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/DummyHttpClient.java
@@ -0,0 +1,142 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache;
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.impl.conn.SingleClientConnManager;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+ at SuppressWarnings("deprecation")
+public class DummyHttpClient implements HttpClient {
+
+    private HttpParams params = new BasicHttpParams();
+    private ClientConnectionManager connManager = new SingleClientConnManager();
+    private HttpRequest request;
+    private HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP",1,1), HttpStatus.SC_OK, "OK");
+    private int executions = 0;
+    
+    public void setParams(HttpParams params) {
+        this.params = params;
+    }
+    
+    public HttpParams getParams() {
+        return params;
+    }
+
+    public ClientConnectionManager getConnectionManager() {
+        return connManager;
+    }
+    
+    public void setConnectionManager(ClientConnectionManager ccm) {
+        connManager = ccm;
+    }
+    
+    public void setResponse(HttpResponse resp) {
+        response = resp;
+    }
+    
+    public HttpRequest getCapturedRequest() {
+        return request;
+    }
+
+    public HttpResponse execute(HttpUriRequest request) throws IOException,
+            ClientProtocolException {
+        this.request = request;
+        executions++;
+        return response;
+    }
+
+    public HttpResponse execute(HttpUriRequest request, HttpContext context)
+            throws IOException, ClientProtocolException {
+        this.request = request;
+        executions++;
+        return response;
+    }
+
+    public HttpResponse execute(HttpHost target, HttpRequest request)
+            throws IOException, ClientProtocolException {
+        this.request = request;
+        executions++;
+        return response;
+    }
+
+    public HttpResponse execute(HttpHost target, HttpRequest request,
+            HttpContext context) throws IOException, ClientProtocolException {
+        this.request = request;
+        executions++;
+        return response;
+    }
+
+    public <T> T execute(HttpUriRequest request,
+            ResponseHandler<? extends T> responseHandler) throws IOException,
+            ClientProtocolException {
+        this.request = request;
+        executions++;
+        return responseHandler.handleResponse(response);
+    }
+
+    public <T> T execute(HttpUriRequest request,
+            ResponseHandler<? extends T> responseHandler, HttpContext context)
+            throws IOException, ClientProtocolException {
+        this.request = request;
+        executions++;
+        return responseHandler.handleResponse(response);
+    }
+
+    public <T> T execute(HttpHost target, HttpRequest request,
+            ResponseHandler<? extends T> responseHandler) throws IOException,
+            ClientProtocolException {
+        this.request = request;
+        executions++;
+        return responseHandler.handleResponse(response);
+    }
+
+    public <T> T execute(HttpHost target, HttpRequest request,
+            ResponseHandler<? extends T> responseHandler, HttpContext context)
+            throws IOException, ClientProtocolException {
+        this.request = request;
+        executions++;
+        return responseHandler.handleResponse(response);
+    }
+
+    public int getExecutions() {
+    	return executions;
+    }
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/HttpTestUtils.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/HttpTestUtils.java
index 34d4ce6..8e15465 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/HttpTestUtils.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/HttpTestUtils.java
@@ -76,11 +76,11 @@ public class HttpTestUtils {
         "If-None-Match", "If-Range", "If-Unmodified-Since", "Last-Modified", "Location",
         "Max-Forwards", "Proxy-Authorization", "Range", "Referer", "Retry-After", "Server",
         "User-Agent", "Vary" };
-    
+
     /*
      * "Entity-header fields define metainformation about the entity-body or,
      * if no body is present, about the resource identified by the request."
-     * 
+     *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.1
      */
     public static final String[] ENTITY_HEADERS = { "Allow", "Content-Encoding",
@@ -341,4 +341,4 @@ public class HttpTestUtils {
         return new BasicHttpResponse(HttpVersion.HTTP_1_1,
                 HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
     }
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/OKStatus.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/OKStatus.java
index c62e8da..8390450 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/OKStatus.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/OKStatus.java
@@ -38,4 +38,4 @@ public class OKStatus extends BasicStatusLine {
         super(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/RequestEquivalent.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/RequestEquivalent.java
index 4e21102..4bfb65f 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/RequestEquivalent.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/RequestEquivalent.java
@@ -50,4 +50,4 @@ public class RequestEquivalent implements IArgumentMatcher {
         buf.append(")");
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidationRequest.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidationRequest.java
index 12ffa6d..4821f2f 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidationRequest.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidationRequest.java
@@ -46,7 +46,7 @@ public class TestAsynchronousValidationRequest {
     private HttpRequest request;
     private HttpContext mockContext;
     private HttpCacheEntry mockCacheEntry;
-    
+
     @Before
     public void setUp() {
         mockParent = EasyMock.createMock(AsynchronousValidator.class);
@@ -56,68 +56,68 @@ public class TestAsynchronousValidationRequest {
         mockContext = EasyMock.createMock(HttpContext.class);
         mockCacheEntry = EasyMock.createMock(HttpCacheEntry.class);
     }
-    
+
     @Test
     public void testRunCallsCachingClientAndRemovesIdentifier() throws ProtocolException, IOException {
         String identifier = "foo";
-        
+
         AsynchronousValidationRequest asynchRequest = new AsynchronousValidationRequest(
                 mockParent, mockClient, target, request, mockContext, mockCacheEntry,
                 identifier);
-     
+
         // response not used
         EasyMock.expect(mockClient.revalidateCacheEntry(target, request, mockContext, mockCacheEntry)).andReturn(null);
         mockParent.markComplete(identifier);
-        
+
         replayMocks();
         asynchRequest.run();
         verifyMocks();
     }
-    
+
     @Test
     public void testRunGracefullyHandlesProtocolException() throws IOException, ProtocolException {
         String identifier = "foo";
-        
+
         AsynchronousValidationRequest impl = new AsynchronousValidationRequest(
                 mockParent, mockClient, target, request, mockContext, mockCacheEntry,
                 identifier);
-     
+
         // response not used
         EasyMock.expect(
                 mockClient.revalidateCacheEntry(target, request, mockContext,
                         mockCacheEntry)).andThrow(new ProtocolException());
         mockParent.markComplete(identifier);
-        
+
         replayMocks();
         impl.run();
         verifyMocks();
     }
-    
+
     @Test
     public void testRunGracefullyHandlesIOException() throws IOException, ProtocolException {
         String identifier = "foo";
-        
+
         AsynchronousValidationRequest impl = new AsynchronousValidationRequest(
                 mockParent, mockClient, target, request, mockContext, mockCacheEntry,
                 identifier);
-     
+
         // response not used
         EasyMock.expect(
                 mockClient.revalidateCacheEntry(target, request, mockContext,
                         mockCacheEntry)).andThrow(new IOException());
         mockParent.markComplete(identifier);
-        
+
         replayMocks();
         impl.run();
         verifyMocks();
     }
-    
+
     public void replayMocks() {
         EasyMock.replay(mockClient);
         EasyMock.replay(mockContext);
         EasyMock.replay(mockCacheEntry);
     }
-    
+
     public void verifyMocks() {
         EasyMock.verify(mockClient);
         EasyMock.verify(mockContext);
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidator.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidator.java
index db2364c..37cb75b 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidator.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsynchronousValidator.java
@@ -48,17 +48,17 @@ import org.junit.Before;
 import org.junit.Test;
 
 public class TestAsynchronousValidator {
-    
+
     private AsynchronousValidator impl;
-    
+
     private CachingHttpClient mockClient;
     private HttpHost target;
     private HttpRequest request;
     private HttpContext mockContext;
     private HttpCacheEntry mockCacheEntry;
-    
+
     private ExecutorService mockExecutor;
-    
+
     @Before
     public void setUp() {
         mockClient = EasyMock.createMock(CachingHttpClient.class);
@@ -66,138 +66,138 @@ public class TestAsynchronousValidator {
         request = new HttpGet("/");
         mockContext = EasyMock.createMock(HttpContext.class);
         mockCacheEntry = EasyMock.createMock(HttpCacheEntry.class);
-        
+
         mockExecutor = EasyMock.createMock(ExecutorService.class);
-        
+
     }
-    
+
     @Test
     public void testRevalidateCacheEntrySchedulesExecutionAndPopulatesIdentifier() {
         impl = new AsynchronousValidator(mockClient, mockExecutor);
-        
+
         EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
         mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
-        
+
         replayMocks();
         impl.revalidateCacheEntry(target, request, mockContext, mockCacheEntry);
         verifyMocks();
-        
+
         Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
     }
-    
+
     @Test
     public void testMarkCompleteRemovesIdentifier() {
         impl = new AsynchronousValidator(mockClient, mockExecutor);
-        
+
         EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
         Capture<AsynchronousValidationRequest> cap = new Capture<AsynchronousValidationRequest>();
         mockExecutor.execute(EasyMock.capture(cap));
-        
+
         replayMocks();
         impl.revalidateCacheEntry(target, request, mockContext, mockCacheEntry);
         verifyMocks();
-        
+
         Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
-        
+
         impl.markComplete(cap.getValue().getIdentifier());
-        
+
         Assert.assertEquals(0, impl.getScheduledIdentifiers().size());
     }
-    
+
     @Test
     public void testRevalidateCacheEntryDoesNotPopulateIdentifierOnRejectedExecutionException() {
         impl = new AsynchronousValidator(mockClient, mockExecutor);
-        
+
         EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
         mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
         EasyMock.expectLastCall().andThrow(new RejectedExecutionException());
-        
+
         replayMocks();
         impl.revalidateCacheEntry(target, request, mockContext, mockCacheEntry);
         verifyMocks();
-        
+
         Assert.assertEquals(0, impl.getScheduledIdentifiers().size());
     }
-    
+
     @Test
     public void testRevalidateCacheEntryProperlyCollapsesRequest() {
         impl = new AsynchronousValidator(mockClient, mockExecutor);
-        
+
         EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
         mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
-        
+
         EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
-        
+
         replayMocks();
         impl.revalidateCacheEntry(target, request, mockContext, mockCacheEntry);
         impl.revalidateCacheEntry(target, request, mockContext, mockCacheEntry);
         verifyMocks();
-        
+
         Assert.assertEquals(1, impl.getScheduledIdentifiers().size());
     }
-    
+
     @Test
     public void testVariantsBothRevalidated() {
         impl = new AsynchronousValidator(mockClient, mockExecutor);
-        
+
         HttpRequest req1 = new HttpGet("/");
         req1.addHeader(new BasicHeader("Accept-Encoding", "identity"));
-        
+
         HttpRequest req2 = new HttpGet("/");
         req2.addHeader(new BasicHeader("Accept-Encoding", "gzip"));
-        
+
         Header[] variantHeaders = new Header[] {
                 new BasicHeader(HeaderConstants.VARY, "Accept-Encoding")
         };
-        
+
         EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(true).times(2);
         EasyMock.expect(mockCacheEntry.getHeaders(HeaderConstants.VARY)).andReturn(variantHeaders).times(2);
         mockExecutor.execute(EasyMock.isA(AsynchronousValidationRequest.class));
         EasyMock.expectLastCall().times(2);
-        
+
         replayMocks();
         impl.revalidateCacheEntry(target, req1, mockContext, mockCacheEntry);
         impl.revalidateCacheEntry(target, req2, mockContext, mockCacheEntry);
         verifyMocks();
-        
+
         Assert.assertEquals(2, impl.getScheduledIdentifiers().size());
-        
+
     }
-    
+
     @Test
     public void testRevalidateCacheEntryEndToEnd() throws ProtocolException, IOException {
         CacheConfig config = new CacheConfig();
         config.setAsynchronousWorkersMax(1);
         config.setAsynchronousWorkersCore(1);
         impl = new AsynchronousValidator(mockClient, config);
-        
+
         EasyMock.expect(mockCacheEntry.hasVariants()).andReturn(false);
         EasyMock.expect(mockClient.revalidateCacheEntry(target, request, mockContext, mockCacheEntry)).andReturn(null);
-        
+
         replayMocks();
         impl.revalidateCacheEntry(target, request, mockContext, mockCacheEntry);
-        
+
         try {
             // shut down backend executor and make sure all finishes properly, 1 second should be sufficient
             ExecutorService implExecutor = impl.getExecutor();
             implExecutor.shutdown();
             implExecutor.awaitTermination(1, TimeUnit.SECONDS);
         } catch (InterruptedException ie) {
-            
+
         } finally {
             verifyMocks();
-            
+
             Assert.assertEquals(0, impl.getScheduledIdentifiers().size());
         }
     }
-    
+
     public void replayMocks() {
         EasyMock.replay(mockExecutor);
         EasyMock.replay(mockClient);
         EasyMock.replay(mockContext);
         EasyMock.replay(mockCacheEntry);
     }
-    
+
     public void verifyMocks() {
         EasyMock.verify(mockExecutor);
         EasyMock.verify(mockClient);
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestBasicHttpCache.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestBasicHttpCache.java
index 846907b..6a61d8b 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestBasicHttpCache.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestBasicHttpCache.java
@@ -234,7 +234,7 @@ public class TestBasicHttpCache {
         Date responseReceived = new Date(now.getTime() - 1 * 1000L);
 
         HttpResponse originResponse = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
-        originResponse.setEntity(HttpTestUtils.makeBody(CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES + 1));
+        originResponse.setEntity(HttpTestUtils.makeBody((int) CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES + 1));
         originResponse.setHeader("Cache-Control","public, max-age=3600");
         originResponse.setHeader("Date", DateUtils.formatDate(responseGenerated));
         originResponse.setHeader("ETag", "\"etag\"");
@@ -256,7 +256,7 @@ public class TestBasicHttpCache {
         Date responseReceived = new Date(now.getTime() - 1 * 1000L);
 
         HttpResponse originResponse = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
-        originResponse.setEntity(HttpTestUtils.makeBody(CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES - 1));
+        originResponse.setEntity(HttpTestUtils.makeBody((int) CacheConfig.DEFAULT_MAX_OBJECT_SIZE_BYTES - 1));
         originResponse.setHeader("Cache-Control","public, max-age=3600");
         originResponse.setHeader("Date", DateUtils.formatDate(responseGenerated));
         originResponse.setHeader("ETag", "\"etag\"");
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheEntryUpdater.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheEntryUpdater.java
index 3743f08..5e5b7fa 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheEntryUpdater.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheEntryUpdater.java
@@ -94,7 +94,7 @@ public class TestCacheEntryUpdater {
         HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry,
                 new Date(), new Date(), response);
 
-         
+
         Header[] updatedHeaders = updatedEntry.getAllHeaders();
         assertEquals(2, updatedHeaders.length);
         headersContain(updatedHeaders, "Date", formatDate(responseDate));
@@ -159,7 +159,7 @@ public class TestCacheEntryUpdater {
                 new BasicHeader("Date", formatDate(oneSecondAgo)),
                 new BasicHeader("ETag", "\"new-etag\"")
         };
-        entry = HttpTestUtils.makeCacheEntry(twoSecondsAgo, now, headers); 
+        entry = HttpTestUtils.makeCacheEntry(twoSecondsAgo, now, headers);
         response.setHeader("Date", formatDate(tenSecondsAgo));
         response.setHeader("ETag", "\"old-etag\"");
         HttpCacheEntry result = impl.updateCacheEntry("A", entry, new Date(),
@@ -179,7 +179,7 @@ public class TestCacheEntryUpdater {
         assertEquals(twoSecondsAgo, updated.getRequestDate());
         assertEquals(oneSecondAgo, updated.getResponseDate());
     }
-    
+
     @Test
     public void entry1xxWarningsAreRemovedOnUpdate() throws Exception {
         Header[] headers = {
@@ -195,7 +195,7 @@ public class TestCacheEntryUpdater {
 
         assertEquals(0, updated.getHeaders("Warning").length);
     }
-    
+
     @Test
     public void entryWithMalformedDateIsStillUpdated() throws Exception {
         Header[] headers = {
@@ -225,7 +225,7 @@ public class TestCacheEntryUpdater {
 
         assertEquals("\"new\"", updated.getFirstHeader("ETag").getValue());
     }
-    
+
     @Test
     public void cannotUpdateFromANon304OriginResponse() throws Exception {
         entry = HttpTestUtils.makeCacheEntry();
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java
index 9d2cc21..a86f13f 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheInvalidator.java
@@ -71,7 +71,7 @@ public class TestCacheInvalidator {
     public void setUp() {
         now = new Date();
         tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
-        
+
         host = new HttpHost("foo.example.com");
         mockStorage = createMock(HttpCacheStorage.class);
         cacheKeyGenerator = new CacheKeyGenerator();
@@ -265,7 +265,7 @@ public class TestCacheInvalidator {
         impl.flushInvalidatedCacheEntries(host, request);
         verifyMocks();
     }
-    
+
     @Test
     public void doesNotFlushForResponsesWithoutContentLocation()
             throws Exception {
@@ -273,7 +273,7 @@ public class TestCacheInvalidator {
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
     }
-    
+
     @Test
     public void flushesEntryIfFresherAndSpecifiedByContentLocation()
             throws Exception {
@@ -281,20 +281,20 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
            new BasicHeader("Date", formatDate(tenSecondsAgo)),
            new BasicHeader("ETag", "\"old-etag\"")
         });
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
         mockStorage.removeEntry(theURI);
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
     }
-    
+
     @Test
     public void doesNotFlushEntryForUnsuccessfulResponse()
             throws Exception {
@@ -303,14 +303,14 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
            new BasicHeader("Date", formatDate(tenSecondsAgo)),
            new BasicHeader("ETag", "\"old-etag\"")
         });
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
@@ -323,15 +323,15 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String cacheKey = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", "http://foo.example.com/bar");
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
            new BasicHeader("Date", formatDate(tenSecondsAgo)),
            new BasicHeader("ETag", "\"old-etag\"")
         });
-        
+
         expect(mockStorage.getEntry(cacheKey)).andReturn(entry).anyTimes();
         mockStorage.removeEntry(cacheKey);
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
@@ -344,15 +344,15 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String cacheKey = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", "/bar");
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
            new BasicHeader("Date", formatDate(tenSecondsAgo)),
            new BasicHeader("ETag", "\"old-etag\"")
         });
-        
+
         expect(mockStorage.getEntry(cacheKey)).andReturn(entry).anyTimes();
         mockStorage.removeEntry(cacheKey);
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
@@ -365,21 +365,21 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String cacheKey = "http://baz.example.com:80/bar";
         response.setHeader("Content-Location", cacheKey);
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
            new BasicHeader("Date", formatDate(tenSecondsAgo)),
            new BasicHeader("ETag", "\"old-etag\"")
         });
-        
+
         expect(mockStorage.getEntry(cacheKey)).andReturn(entry).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
     }
 
-    
-    
+
+
     @Test
     public void doesNotFlushEntrySpecifiedByContentLocationIfEtagsMatch()
             throws Exception {
@@ -387,14 +387,14 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
            new BasicHeader("Date", formatDate(tenSecondsAgo)),
            new BasicHeader("ETag", "\"same-etag\"")
         });
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
@@ -407,19 +407,19 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
            new BasicHeader("Date", formatDate(now)),
            new BasicHeader("ETag", "\"old-etag\"")
         });
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
     }
-    
+
     @Test
     public void doesNotFlushEntryIfNotInCache()
             throws Exception {
@@ -427,14 +427,14 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(null).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
     }
-    
+
     @Test
     public void doesNotFlushEntrySpecifiedByContentLocationIfResponseHasNoEtag()
             throws Exception {
@@ -442,19 +442,19 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
            new BasicHeader("Date", formatDate(tenSecondsAgo)),
            new BasicHeader("ETag", "\"old-etag\"")
         });
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
     }
-    
+
     @Test
     public void doesNotFlushEntrySpecifiedByContentLocationIfEntryHasNoEtag()
             throws Exception {
@@ -462,13 +462,13 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
            new BasicHeader("Date", formatDate(tenSecondsAgo)),
         });
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
@@ -481,19 +481,19 @@ public class TestCacheInvalidator {
         response.removeHeaders("Date");
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
                 new BasicHeader("ETag", "\"old-etag\""),
                 new BasicHeader("Date", formatDate(tenSecondsAgo)),
         });
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
     }
-    
+
     @Test
     public void doesNotFlushEntrySpecifiedByContentLocationIfEntryHasNoDate()
             throws Exception {
@@ -501,13 +501,13 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
+
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
            new BasicHeader("ETag", "\"old-etag\"")
         });
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
@@ -520,19 +520,19 @@ public class TestCacheInvalidator {
         response.setHeader("Date", "blarg");
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
-        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {                
+
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
                 new BasicHeader("ETag", "\"old-etag\""),
                 new BasicHeader("Date", formatDate(tenSecondsAgo))
         });
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
     }
-    
+
     @Test
     public void doesNotFlushEntrySpecifiedByContentLocationIfEntryHasMalformedDate()
             throws Exception {
@@ -540,20 +540,20 @@ public class TestCacheInvalidator {
         response.setHeader("Date", formatDate(now));
         String theURI = "http://foo.example.com:80/bar";
         response.setHeader("Content-Location", theURI);
-        
-        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {                
+
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
                 new BasicHeader("ETag", "\"old-etag\""),
                 new BasicHeader("Date", "foo")
         });
-        
+
         expect(mockStorage.getEntry(theURI)).andReturn(entry).anyTimes();
-        
+
         replayMocks();
         impl.flushInvalidatedCacheEntries(host, request, response);
         verifyMocks();
     }
 
-    
+
     // Expectations
     private void cacheEntryHasVariantMap(Map<String,String> variantMap) {
         expect(mockEntry.getVariantMap()).andReturn(variantMap);
@@ -572,4 +572,4 @@ public class TestCacheInvalidator {
         mockStorage.removeEntry(theUri);
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java
index 412e328..3085643 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCacheValidityPolicy.java
@@ -37,6 +37,7 @@ import org.apache.http.client.cache.HttpCacheEntry;
 import org.apache.http.impl.cookie.DateUtils;
 import org.apache.http.message.BasicHeader;
 import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.protocol.HTTP;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -137,12 +138,7 @@ public class TestCacheValidityPolicy {
     @Test
     public void testResidentTimeSecondsIsTimeSinceResponseTime() {
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo);
-        impl = new CacheValidityPolicy() {
-            @Override
-            protected Date getCurrentDate() {
-                return now;
-            }
-        };
+        impl = new CacheValidityPolicy();
         assertEquals(6, impl.getResidentTimeSecs(entry, now));
     }
 
@@ -332,6 +328,30 @@ public class TestCacheValidityPolicy {
     }
 
     @Test
+    public void testMissingContentLengthDoesntInvalidateEntry() {
+        final int contentLength = 128;
+        Header[] headers = {}; // no Content-Length header
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers, HttpTestUtils.getRandomBytes(contentLength));
+        assertTrue(impl.contentLengthHeaderMatchesActualLength(entry));
+    }
+
+    @Test
+    public void testCorrectContentLengthDoesntInvalidateEntry() {
+        final int contentLength = 128;
+        Header[] headers = { new BasicHeader(HTTP.CONTENT_LEN, Integer.toString(contentLength)) };
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers, HttpTestUtils.getRandomBytes(contentLength));
+        assertTrue(impl.contentLengthHeaderMatchesActualLength(entry));
+    }
+
+    @Test
+    public void testWrongContentLengthInvalidatesEntry() {
+        final int contentLength = 128;
+        Header[] headers = {new BasicHeader(HTTP.CONTENT_LEN, Integer.toString(contentLength+1))};
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers, HttpTestUtils.getRandomBytes(contentLength));
+        assertFalse(impl.contentLengthHeaderMatchesActualLength(entry));
+    }
+
+    @Test
     public void testMalformedDateHeaderIsIgnored() {
         Header[] headers = new Header[] { new BasicHeader("Date", "asdf") };
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
@@ -411,7 +431,7 @@ public class TestCacheValidityPolicy {
         HttpRequest req = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
         assertTrue(impl.mayReturnStaleIfError(req, entry, now));
     }
-    
+
     @Test
     public void testMayReturnStaleIfErrorInRequestIsTrueWithinStaleness(){
         Header[] headers = new Header[] {
@@ -450,15 +470,15 @@ public class TestCacheValidityPolicy {
     @Test
     public void testMayReturnStaleWhileRevalidatingIsFalseWhenDirectiveIsAbsent() {
         Date now = new Date();
-        
+
         Header[] headers = new Header[] { new BasicHeader("Cache-control", "public") };
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
-        
+
         CacheValidityPolicy impl = new CacheValidityPolicy();
-        
+
         assertFalse(impl.mayReturnStaleWhileRevalidating(entry, now));
     }
-    
+
     @Test
     public void testMayReturnStaleWhileRevalidatingIsTrueWhenWithinStaleness() {
         Date now = new Date();
@@ -468,12 +488,12 @@ public class TestCacheValidityPolicy {
                 new BasicHeader("Cache-Control", "max-age=5, stale-while-revalidate=15")
         };
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
-        
+
         CacheValidityPolicy impl = new CacheValidityPolicy();
-        
+
         assertTrue(impl.mayReturnStaleWhileRevalidating(entry, now));
     }
-    
+
     @Test
     public void testMayReturnStaleWhileRevalidatingIsFalseWhenPastStaleness() {
         Date now = new Date();
@@ -483,12 +503,12 @@ public class TestCacheValidityPolicy {
                 new BasicHeader("Cache-Control", "max-age=5, stale-while-revalidate=15")
         };
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
-        
+
         CacheValidityPolicy impl = new CacheValidityPolicy();
-        
+
         assertFalse(impl.mayReturnStaleWhileRevalidating(entry, now));
     }
-    
+
     @Test
     public void testMayReturnStaleWhileRevalidatingIsFalseWhenDirectiveEmpty() {
         Date now = new Date();
@@ -498,9 +518,9 @@ public class TestCacheValidityPolicy {
                 new BasicHeader("Cache-Control", "max-age=5, stale-while-revalidate=")
         };
         HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers);
-        
+
         CacheValidityPolicy impl = new CacheValidityPolicy();
-        
+
         assertFalse(impl.mayReturnStaleWhileRevalidating(entry, now));
     }
 }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java
index 782990b..b2c7bb0 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachedResponseSuitabilityChecker.java
@@ -260,4 +260,4 @@ public class TestCachedResponseSuitabilityChecker {
 
         Assert.assertTrue(impl.canCachedResponseBeUsed(host, request, entry, now));
     }
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java
index 787a9fa..91b42f8 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java
@@ -26,6 +26,7 @@
  */
 package org.apache.http.impl.client.cache;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.net.URI;
 import java.util.ArrayList;
@@ -34,6 +35,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import org.apache.http.Header;
+import org.apache.http.HttpEntity;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
@@ -50,6 +52,7 @@ import org.apache.http.client.cache.HttpCacheStorage;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.entity.InputStreamEntity;
 import org.apache.http.impl.cookie.DateUtils;
 import org.apache.http.message.BasicHeader;
 import org.apache.http.message.BasicHttpRequest;
@@ -57,6 +60,7 @@ import org.apache.http.message.BasicHttpResponse;
 import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.ExecutionContext;
 import org.apache.http.protocol.HttpContext;
 import org.easymock.Capture;
 import static org.easymock.classextension.EasyMock.*;
@@ -106,6 +110,7 @@ public class TestCachingHttpClient {
     private HttpContext context;
     private HttpParams params;
     private HttpCacheEntry entry;
+    private ConsumableInputStream cis;
 
     @SuppressWarnings("unchecked")
     @Before
@@ -203,16 +208,16 @@ public class TestCachingHttpClient {
     @Test
     public void testCacheableResponsesGoIntoCache() throws Exception {
         impl = new CachingHttpClient(mockBackend);
-        
+
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control","max-age=3600");
-        
+
         expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class),
                 (HttpContext)isNull())).andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
-        
+
         replayMocks();
         impl.execute(host, req1);
         impl.execute(host, req2);
@@ -224,7 +229,7 @@ public class TestCachingHttpClient {
         impl = new CachingHttpClient(mockBackend);
         Date now = new Date();
         Date fiveSecondsAgo = new Date(now.getTime() - 5 * 1000L);
-        
+
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Date", DateUtils.formatDate(now));
@@ -233,7 +238,7 @@ public class TestCachingHttpClient {
 
         expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class),
                 (HttpContext)isNull())).andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control","no-cache");
         HttpResponse resp2 = HttpTestUtils.make200Response();
@@ -260,7 +265,7 @@ public class TestCachingHttpClient {
         impl = new CachingHttpClient(mockBackend);
         Date now = new Date();
         Date fiveSecondsAgo = new Date(now.getTime() - 5 * 1000L);
-        
+
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Date", DateUtils.formatDate(fiveSecondsAgo));
@@ -269,7 +274,7 @@ public class TestCachingHttpClient {
 
         expect(mockBackend.execute(isA(HttpHost.class), isA(HttpRequest.class),
                 (HttpContext)isNull())).andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control","max-age=0");
         HttpResponse resp2 = HttpTestUtils.make200Response();
@@ -486,7 +491,7 @@ public class TestCachingHttpClient {
         impl = new CachingHttpClient(mockBackend,
                 new BasicHttpCache(new HeapResourceFactory(), mockStorage, config),
                 config);
-        
+
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control","no-cache");
@@ -620,12 +625,61 @@ public class TestCachingHttpClient {
 
         expect(mockHandler.handleResponse(mockBackendResponse)).andReturn(
                 theObject);
-
+        HttpEntity streamingEntity = makeStreamingEntity();
+        expect(theResponse.getEntity()).andReturn(streamingEntity);
         replayMocks();
         Object result = impl.execute(host, request, mockHandler, context);
         verifyMocks();
         Assert.assertEquals(1, c.getCount());
         Assert.assertSame(theObject, result);
+        Assert.assertTrue(cis.wasClosed());
+    }
+
+    @Test
+    public void testConsumesEntityOnExecuteWithException() throws Exception {
+
+        final Counter c = new Counter();
+        final HttpHost theHost = host;
+        final HttpRequest theRequest = request;
+        final HttpResponse theResponse = mockBackendResponse;
+        final HttpContext theContext = context;
+        impl = new CachingHttpClient(
+                mockBackend,
+                mockValidityPolicy,
+                mockResponsePolicy,
+                mockCache,
+                mockResponseGenerator,
+                mockRequestPolicy,
+                mockSuitabilityChecker,
+                mockConditionalRequestBuilder,
+                mockResponseProtocolCompliance,
+                mockRequestProtocolCompliance) {
+            @Override
+            public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) {
+                Assert.assertSame(theHost, target);
+                Assert.assertSame(theRequest, request);
+                Assert.assertSame(theContext, context);
+                c.incr();
+                return theResponse;
+            }
+        };
+        IOException ioException = new IOException("test exception");
+
+        expect(mockHandler.handleResponse(mockBackendResponse)).andThrow(ioException);
+        HttpEntity streamingEntity = makeStreamingEntity();
+        expect(theResponse.getEntity()).andReturn(streamingEntity).anyTimes();
+
+        replayMocks();
+        Object result = null;
+        try {
+            result = impl.execute(host, request, mockHandler, context);
+        } catch (Exception e) {
+            assertEquals(ioException, e);
+        }
+        verifyMocks();
+        Assert.assertEquals(1, c.getCount());
+        Assert.assertNull(result);
+        assertTrue(cis.wasClosed());
     }
 
     @Test
@@ -769,15 +823,16 @@ public class TestCachingHttpClient {
                 return theResponse;
             }
         };
-
         expect(mockHandler.handleResponse(mockBackendResponse)).andReturn(
                 theValue);
-
+        HttpEntity streamingEntity = makeStreamingEntity();
+        expect(theResponse.getEntity()).andReturn(streamingEntity);
         replayMocks();
         Object result = impl.execute(mockUriRequest, mockHandler, context);
         verifyMocks();
         Assert.assertEquals(1, c.getCount());
         Assert.assertSame(theValue, result);
+        Assert.assertTrue(cis.wasClosed());
     }
 
     @Test
@@ -1012,8 +1067,8 @@ public class TestCachingHttpClient {
         resp1.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo));
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
 
         replayMocks();
@@ -1048,8 +1103,8 @@ public class TestCachingHttpClient {
         HttpResponse resp2 = HttpTestUtils.make200Response();
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
         expect(
                 mockBackend.execute(isA(HttpHost.class),
@@ -1088,8 +1143,8 @@ public class TestCachingHttpClient {
                 .formatDate(tenSecondsAfter));
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1).times(2);
 
         replayMocks();
@@ -1116,8 +1171,8 @@ public class TestCachingHttpClient {
         resp1.setHeader("Cache-Control", "public, max-age=3600");
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
 
         replayMocks();
@@ -1147,12 +1202,12 @@ public class TestCachingHttpClient {
         HttpResponse resp2 = HttpTestUtils.make200Response();
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp2);
 
         replayMocks();
@@ -1185,8 +1240,8 @@ public class TestCachingHttpClient {
         req2.addHeader("If-Modified-Since", DateUtils.formatDate(now));
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
 
         replayMocks();
@@ -1217,12 +1272,12 @@ public class TestCachingHttpClient {
         resp1.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo));
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
 
         replayMocks();
@@ -1432,13 +1487,13 @@ public class TestCachingHttpClient {
         resp2.setHeader("Date", DateUtils.formatDate(now));
         resp2.setHeader("Cache-Control", "public, max-age=5");
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp2);
         replayMocks();
         impl.execute(host, req1);
@@ -1476,13 +1531,13 @@ public class TestCachingHttpClient {
         resp2.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
         resp2.setHeader("Cache-Control", "public, max-age=5");
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp2);
         replayMocks();
         impl.execute(host, req1);
@@ -1520,13 +1575,13 @@ public class TestCachingHttpClient {
         resp1.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo));
         resp2.setHeader("Cache-Control", "public, max-age=5");
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp2);
         replayMocks();
         impl.execute(host, req1);
@@ -1565,13 +1620,13 @@ public class TestCachingHttpClient {
         resp1.setHeader("Last-Modified", DateUtils.formatDate(now));
         resp2.setHeader("Cache-Control", "public, max-age=5");
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp2);
         replayMocks();
         impl.execute(host, req1);
@@ -1628,18 +1683,18 @@ public class TestCachingHttpClient {
         resp3.setHeader("Cache-Control", "public, max-age=3600");
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp2);
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp3);
 
 
@@ -1702,18 +1757,18 @@ public class TestCachingHttpClient {
         resp4.setHeader("Cache-Control", "public, max-age=3600");
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp1);
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp2);
 
         expect(
-                mockBackend.execute(isA(HttpHost.class), 
-                        isA(HttpRequest.class), (HttpContext) 
+                mockBackend.execute(isA(HttpHost.class),
+                        isA(HttpRequest.class), (HttpContext)
                         isNull())).andReturn(resp4);
 
         replayMocks();
@@ -1744,7 +1799,7 @@ public class TestCachingHttpClient {
         mockCache.flushInvalidatedCacheEntriesFor(host, request);
         expectLastCall().andThrow(new IOException()).anyTimes();
         mockCache.flushInvalidatedCacheEntriesFor(isA(HttpHost.class), isA(HttpRequest.class), isA(HttpResponse.class));
-        expectLastCall().anyTimes();    
+        expectLastCall().anyTimes();
         expect(mockCache.getCacheEntry(same(host),
                 isA(HttpRequest.class)))
             .andThrow(new IOException()).anyTimes();
@@ -1817,7 +1872,150 @@ public class TestCachingHttpClient {
 
         Assert.assertSame(mockCachedResponse, resp);
     }
-
+    
+    private DummyHttpClient getContextSettingBackend(final Object value, final HttpResponse response) {
+    	return new DummyHttpClient() {
+    		@Override
+    		public HttpResponse execute(HttpHost target, HttpRequest request,
+    	            HttpContext context) throws IOException, ClientProtocolException {
+    			if (context != null) {
+    				context.setAttribute(ExecutionContext.HTTP_CONNECTION, value);
+    				context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, value);
+    				context.setAttribute(ExecutionContext.HTTP_REQ_SENT, value);
+    				context.setAttribute(ExecutionContext.HTTP_REQUEST, value);
+    				context.setAttribute(ExecutionContext.HTTP_RESPONSE, value);
+    				context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, value);
+    			}
+    			return response;
+    	    }
+    	};
+    }
+    
+	private void assertAllContextVariablesAreEqualTo(HttpContext ctx,
+			final Object value) {
+		assertSame(value, ctx.getAttribute(ExecutionContext.HTTP_CONNECTION));
+    	assertSame(value, ctx.getAttribute(ExecutionContext.HTTP_PROXY_HOST));
+    	assertSame(value, ctx.getAttribute(ExecutionContext.HTTP_REQ_SENT));
+    	assertSame(value, ctx.getAttribute(ExecutionContext.HTTP_REQUEST));
+    	assertSame(value, ctx.getAttribute(ExecutionContext.HTTP_RESPONSE));
+    	assertSame(value, ctx.getAttribute(ExecutionContext.HTTP_TARGET_HOST));
+	}
+
+	@Test
+	public void testAllowsBackendToSetHttpContextVariablesOnCacheMiss() throws Exception {
+		final Object value = new Object(); 
+		impl = new CachingHttpClient(getContextSettingBackend(value, HttpTestUtils.make200Response()));
+		HttpContext ctx = new BasicHttpContext();
+		impl.execute(host, request, ctx);
+		assertAllContextVariablesAreEqualTo(ctx, value);
+	}
+	
+	@Test
+	public void testDoesNotSetConnectionInContextOnCacheHit() throws Exception {
+		DummyHttpClient backend = new DummyHttpClient();
+		HttpResponse response = HttpTestUtils.make200Response();
+		response.setHeader("Cache-Control", "max-age=3600");
+		backend.setResponse(response);
+		impl = new CachingHttpClient(backend);
+		HttpContext ctx = new BasicHttpContext();
+		impl.execute(host, request);
+		impl.execute(host, request, ctx);
+		assertNull(ctx.getAttribute(ExecutionContext.HTTP_CONNECTION));
+	}
+	
+	@Test
+	public void testDoesNotSetProxyHostInContextOnCacheHit() throws Exception {
+		DummyHttpClient backend = new DummyHttpClient();
+		HttpResponse response = HttpTestUtils.make200Response();
+		response.setHeader("Cache-Control", "max-age=3600");
+		backend.setResponse(response);
+		impl = new CachingHttpClient(backend);
+		HttpContext ctx = new BasicHttpContext();
+		impl.execute(host, request);
+		impl.execute(host, request, ctx);
+		assertNull(ctx.getAttribute(ExecutionContext.HTTP_PROXY_HOST));
+	}
+	
+	@Test
+	public void testSetsTargetHostInContextOnCacheHit() throws Exception {
+		DummyHttpClient backend = new DummyHttpClient();
+		HttpResponse response = HttpTestUtils.make200Response();
+		response.setHeader("Cache-Control", "max-age=3600");
+		backend.setResponse(response);
+		impl = new CachingHttpClient(backend);
+		HttpContext ctx = new BasicHttpContext();
+		impl.execute(host, request);
+		impl.execute(host, request, ctx);
+		assertSame(host, ctx.getAttribute(ExecutionContext.HTTP_TARGET_HOST));
+	}
+
+	@Test
+	public void testSetsRequestInContextOnCacheHit() throws Exception {
+		DummyHttpClient backend = new DummyHttpClient();
+		HttpResponse response = HttpTestUtils.make200Response();
+		response.setHeader("Cache-Control", "max-age=3600");
+		backend.setResponse(response);
+		impl = new CachingHttpClient(backend);
+		HttpContext ctx = new BasicHttpContext();
+		impl.execute(host, request);
+		impl.execute(host, request, ctx);
+		assertSame(request, ctx.getAttribute(ExecutionContext.HTTP_REQUEST));
+	}
+
+	@Test
+	public void testSetsResponseInContextOnCacheHit() throws Exception {
+		DummyHttpClient backend = new DummyHttpClient();
+		HttpResponse response = HttpTestUtils.make200Response();
+		response.setHeader("Cache-Control", "max-age=3600");
+		backend.setResponse(response);
+		impl = new CachingHttpClient(backend);
+		HttpContext ctx = new BasicHttpContext();
+		impl.execute(host, request);
+		HttpResponse result = impl.execute(host, request, ctx);
+		assertSame(result, ctx.getAttribute(ExecutionContext.HTTP_RESPONSE));
+	}
+	
+	@Test
+	public void testSetsRequestSentInContextOnCacheHit() throws Exception {
+		DummyHttpClient backend = new DummyHttpClient();
+		HttpResponse response = HttpTestUtils.make200Response();
+		response.setHeader("Cache-Control", "max-age=3600");
+		backend.setResponse(response);
+		impl = new CachingHttpClient(backend);
+		HttpContext ctx = new BasicHttpContext();
+		impl.execute(host, request);
+		impl.execute(host, request, ctx);
+		assertTrue((Boolean)ctx.getAttribute(ExecutionContext.HTTP_REQ_SENT));
+	}
+	
+	@Test
+	public void testAllowsBackendToSetContextVariablesOnRevalidation() throws Exception {
+		final Object value = new Object(); 
+		HttpResponse response = HttpTestUtils.make200Response();
+		response.setHeader("Cache-Control","public");
+		response.setHeader("Etag", "\"v1\"");
+		DummyHttpClient backend = getContextSettingBackend(value, response);
+		backend.setResponse(response);
+		impl = new CachingHttpClient(backend);
+		HttpContext ctx = new BasicHttpContext();
+		impl.execute(host, request);
+		impl.execute(host, request, ctx);
+		assertAllContextVariablesAreEqualTo(ctx, value);
+	}
+	
+	@Test
+	public void testCanCacheAResponseWithoutABody() throws Exception {
+		HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_NO_CONTENT, "No Content");
+		response.setHeader("Date", DateUtils.formatDate(new Date()));
+		response.setHeader("Cache-Control","max-age=300");
+		DummyHttpClient backend = new DummyHttpClient();
+		backend.setResponse(response);
+		impl = new CachingHttpClient(backend);
+		impl.execute(host, request);
+		impl.execute(host, request);
+		assertEquals(1, backend.getExecutions());
+	}
+	
     private void getCacheEntryReturns(HttpCacheEntry result) throws IOException {
         expect(mockCache.getCacheEntry(host, request)).andReturn(result);
     }
@@ -1853,7 +2051,7 @@ public class TestCachingHttpClient {
         expect(mockValidityPolicy.isRevalidatable(
                 (HttpCacheEntry)anyObject())).andReturn(b);
     }
-    
+
     private void cacheEntryMustRevalidate(boolean b) {
         expect(mockValidityPolicy.mustRevalidate(mockCacheEntry))
             .andReturn(b);
@@ -1863,7 +2061,7 @@ public class TestCachingHttpClient {
         expect(mockValidityPolicy.proxyRevalidate(mockCacheEntry))
             .andReturn(b);
     }
-    
+
     private void mayReturnStaleWhileRevalidating(boolean b) {
         expect(mockValidityPolicy.mayReturnStaleWhileRevalidating(
                 (HttpCacheEntry)anyObject(), (Date)anyObject())).andReturn(b);
@@ -1942,6 +2140,13 @@ public class TestCachingHttpClient {
                         (HttpRequest)anyObject())).andThrow(exception);
     }
 
+    private HttpEntity makeStreamingEntity() {
+        byte[] body = HttpTestUtils.getRandomBytes(101);
+        ByteArrayInputStream buf = new ByteArrayInputStream(body);
+        cis = new ConsumableInputStream(buf);
+        return new InputStreamEntity(cis, 101);
+    }
+
     private void mockImplMethods(String... methods) {
         mockedImpl = true;
         impl = createMockBuilder(CachingHttpClient.class).withConstructor(
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java
index 6536d1d..9d0ca15 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheEntrySerializers.java
@@ -54,12 +54,12 @@ public class TestHttpCacheEntrySerializers {
     private static final Charset UTF8 = Charset.forName("UTF-8");
 
     private HttpCacheEntrySerializer impl;
-    
+
     @Before
     public void setUp() {
         impl = new DefaultHttpCacheEntrySerializer();
     }
-    
+
     @Test
     public void canSerializeEntriesWithVariantMaps() throws Exception {
         readWriteVerify(makeCacheEntryWithVariantMap());
@@ -96,7 +96,7 @@ public class TestHttpCacheEntrySerializers {
 
         return cacheEntry;
     }
-    
+
     private boolean areEqual(HttpCacheEntry one, HttpCacheEntry two) throws IOException {
         // dates are only stored with second precision, so scrub milliseconds
         if (!((one.getRequestDate().getTime() / 1000) == (two.getRequestDate()
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheJiraNumber1147.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheJiraNumber1147.java
new file mode 100644
index 0000000..78f80c7
--- /dev/null
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpCacheJiraNumber1147.java
@@ -0,0 +1,139 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache;
+
+import java.io.File;
+import java.util.Date;
+
+import junit.framework.Assert;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.cache.HttpCacheStorage;
+import org.apache.http.client.cache.ResourceFactory;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.cache.CacheConfig;
+import org.apache.http.impl.client.cache.CachingHttpClient;
+import org.apache.http.impl.client.cache.FileResourceFactory;
+import org.apache.http.impl.client.cache.ManagedHttpCacheStorage;
+import org.apache.http.impl.cookie.DateUtils;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestHttpCacheJiraNumber1147 {
+
+    private File cacheDir;
+
+    private void removeCache() {
+        if (this.cacheDir != null) {
+            File[] files = this.cacheDir.listFiles();
+            for (File cacheFile : files) {
+                cacheFile.delete();
+            }
+            this.cacheDir.delete();
+            this.cacheDir = null;
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        cacheDir = File.createTempFile("cachedir", "");
+        if (cacheDir.exists()) {
+            cacheDir.delete();
+        }
+        cacheDir.mkdir();
+    }
+    
+    @After
+    public void cleanUp() {
+        removeCache();
+    }
+    
+    @Test
+    public void testIssue1147() throws Exception {
+        CacheConfig cacheConfig = new CacheConfig();
+        cacheConfig.setSharedCache(true);
+        cacheConfig.setMaxObjectSize(262144); //256kb
+
+        ResourceFactory resourceFactory = new FileResourceFactory(cacheDir);
+        HttpCacheStorage httpCacheStorage = new ManagedHttpCacheStorage(cacheConfig);
+
+        HttpClient client = EasyMock.createMock(HttpClient.class);
+        HttpGet get = new HttpGet("http://somehost/");
+        HttpContext context = new BasicHttpContext();
+        HttpHost target = new HttpHost("somehost");
+
+        Date now = new Date();
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+        
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+        response.setEntity(HttpTestUtils.makeBody(128));
+        response.setHeader("Content-Length", "128");
+        response.setHeader("ETag", "\"etag\"");
+        response.setHeader("Cache-Control", "public, max-age=3600");
+        response.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo));
+        
+        EasyMock.expect(client.execute(
+                EasyMock.eq(target),
+                EasyMock.isA(HttpRequest.class),
+                EasyMock.same(context))).andReturn(response);
+        EasyMock.replay(client);
+        
+        CachingHttpClient t = new CachingHttpClient(client, resourceFactory, httpCacheStorage, cacheConfig);
+
+        HttpResponse response1 = t.execute(get, context);
+        Assert.assertEquals(200, response1.getStatusLine().getStatusCode());
+        EntityUtils.consume(response1.getEntity());
+        
+        EasyMock.verify(client);
+        
+        removeCache();
+
+        EasyMock.reset(client);
+        EasyMock.expect(client.execute(
+                EasyMock.eq(target),
+                EasyMock.isA(HttpRequest.class),
+                EasyMock.same(context))).andReturn(response);
+        EasyMock.replay(client);
+        
+        HttpResponse response2 = t.execute(get, context);
+        Assert.assertEquals(200, response2.getStatusLine().getStatusCode());
+        EntityUtils.consume(response2.getEntity());
+
+        EasyMock.verify(client);
+    }
+
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java
index 9a3842c..0b79836 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolDeviations.java
@@ -94,7 +94,7 @@ public class TestProtocolDeviations {
         originResponse = make200Response();
 
         CacheConfig params = new CacheConfig();
-        params.setMaxObjectSizeBytes(MAX_BYTES);
+        params.setMaxObjectSize(MAX_BYTES);
         params.setMaxCacheEntries(MAX_ENTRIES);
 
         HttpCache cache = new BasicHttpCache(params);
@@ -307,8 +307,8 @@ public class TestProtocolDeviations {
      *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
      */
-    @Test(expected = ClientProtocolException.class)
-    public void testCantReturnOrigin401ResponseWithoutWWWAuthenticateHeader() throws Exception {
+    @Test
+    public void testPassesOnOrigin401ResponseWithoutWWWAuthenticateHeader() throws Exception {
 
         originResponse = new BasicHttpResponse(HTTP_1_1, 401, "Unauthorized");
 
@@ -317,15 +317,9 @@ public class TestProtocolDeviations {
                         org.easymock.EasyMock.isA(HttpRequest.class),
                         (HttpContext) org.easymock.EasyMock.isNull())).andReturn(originResponse);
         replayMocks();
-
-        // this is another case where we are caught in a sticky
-        // situation, where the origin was not 1.1-compliant.
-        try {
-            impl.execute(host, request);
-        } catch (ClientProtocolException possiblyAcceptableBehavior) {
-            verifyMocks();
-            throw possiblyAcceptableBehavior;
-        }
+        HttpResponse result = impl.execute(host, request);
+        verifyMocks();
+        Assert.assertSame(originResponse, result);
     }
 
     /*
@@ -334,8 +328,8 @@ public class TestProtocolDeviations {
      *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2
      */
-    @Test(expected = ClientProtocolException.class)
-    public void testCantReturnAnOrigin405WithoutAllowHeader() throws Exception {
+    @Test
+    public void testPassesOnOrigin405WithoutAllowHeader() throws Exception {
         originResponse = new BasicHttpResponse(HTTP_1_1, 405, "Method Not Allowed");
 
         org.easymock.EasyMock.expect(
@@ -343,15 +337,9 @@ public class TestProtocolDeviations {
                         org.easymock.EasyMock.isA(HttpRequest.class),
                         (HttpContext) org.easymock.EasyMock.isNull())).andReturn(originResponse);
         replayMocks();
-
-        // this is another case where we are caught in a sticky
-        // situation, where the origin was not 1.1-compliant.
-        try {
-            impl.execute(host, request);
-        } catch (ClientProtocolException possiblyAcceptableBehavior) {
-            verifyMocks();
-            throw possiblyAcceptableBehavior;
-        }
+        HttpResponse result = impl.execute(host, request);
+        verifyMocks();
+        Assert.assertSame(originResponse, result);
     }
 
     /*
@@ -362,7 +350,7 @@ public class TestProtocolDeviations {
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.8
      */
     @Test
-    public void testCantReturnA407WithoutAProxyAuthenticateHeader() throws Exception {
+    public void testPassesOnOrigin407WithoutAProxyAuthenticateHeader() throws Exception {
         originResponse = new BasicHttpResponse(HTTP_1_1, 407, "Proxy Authentication Required");
 
         org.easymock.EasyMock.expect(
@@ -370,23 +358,9 @@ public class TestProtocolDeviations {
                         org.easymock.EasyMock.isA(HttpRequest.class),
                         (HttpContext) org.easymock.EasyMock.isNull())).andReturn(originResponse);
         replayMocks();
-
-        boolean gotException = false;
-        // this is another case where we are caught in a sticky
-        // situation, where the origin was not 1.1-compliant.
-        try {
-            HttpResponse result = impl.execute(host, request);
-            Assert.fail("should have gotten ClientProtocolException");
-
-            if (result.getStatusLine().getStatusCode() == 407) {
-                Assert.assertNotNull(result.getFirstHeader("Proxy-Authentication"));
-            }
-        } catch (ClientProtocolException possiblyAcceptableBehavior) {
-            gotException = true;
-        }
-
+        HttpResponse result = impl.execute(host, request);
         verifyMocks();
-        Assert.assertTrue(gotException);
+        Assert.assertSame(originResponse, result);
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java
index 62212f4..1700380 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRecommendations.java
@@ -66,7 +66,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
     private Date now;
     private Date tenSecondsAgo;
     private Date twoMinutesAgo;
-    
+
     @Override
     @Before
     public void setUp() {
@@ -76,7 +76,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         twoMinutesAgo = new Date(now.getTime() - 2 * 60 * 1000L);
         tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
     }
-    
+
     /* "identity: The default (identity) encoding; the use of no
      * transformation whatsoever. This content-coding is used only in the
      * Accept-Encoding header, and SHOULD NOT be used in the
@@ -107,7 +107,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
      * "304 Not Modified. ... If the conditional GET used a strong cache
      * validator (see section 13.3.3), the response SHOULD NOT include
      * other entity-headers."
-     * 
+     *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
      */
     private void cacheGenerated304ForValidatorShouldNotContainEntityHeader(
@@ -119,29 +119,29 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control","max-age=3600");
         resp1.setHeader(validatorHeader, validator);
         resp1.setHeader(headerName, headerValue);
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader(conditionalHeader, validator);
-        
+
         replayMocks();
         impl.execute(host, req1);
         HttpResponse result = impl.execute(host, req2);
         verifyMocks();
-        
+
         if (HttpStatus.SC_NOT_MODIFIED == result.getStatusLine().getStatusCode()) {
             assertNull(result.getFirstHeader(headerName));
         }
     }
-    
+
     private void cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader(
             String headerName, String headerValue) throws Exception,
             IOException {
         cacheGenerated304ForValidatorShouldNotContainEntityHeader(headerName,
                 headerValue, "ETag", "\"etag\"", "If-None-Match");
     }
-    
+
     private void cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader(
             String headerName, String headerValue) throws Exception,
             IOException {
@@ -149,28 +149,28 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
                 headerValue, "Last-Modified", formatDate(twoMinutesAgo),
                 "If-Modified-Since");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongEtagValidatorShouldNotContainAllow()
             throws Exception {
         cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader(
                 "Allow", "GET,HEAD");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongDateValidatorShouldNotContainAllow()
             throws Exception {
         cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader(
                 "Allow", "GET,HEAD");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongEtagValidatorShouldNotContainContentEncoding()
             throws Exception {
         cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader(
                 "Content-Encoding", "gzip");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentEncoding()
             throws Exception {
@@ -184,7 +184,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader(
                 "Content-Language", "en");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentLanguage()
             throws Exception {
@@ -198,21 +198,21 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader(
                 "Content-Length", "128");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentLength()
             throws Exception {
         cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader(
                 "Content-Length", "128");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongValidatorShouldNotContainContentMD5()
             throws Exception {
         cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader(
                 "Content-MD5", "Q2hlY2sgSW50ZWdyaXR5IQ==");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentMD5()
             throws Exception {
@@ -229,9 +229,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control","max-age=3600");
         resp1.setHeader(validatorHeader, validator);
         resp1.setHeader("Content-Range", "bytes 0-127/256");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("If-Range", validator);
         req2.setHeader("Range","bytes=0-127");
@@ -240,67 +240,67 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_NOT_MODIFIED, "Not Modified");
         resp2.setHeader("Date", formatDate(now));
         resp2.setHeader(validatorHeader, validator);
-        
+
         // cache module does not currently deal with byte ranges, but we want
         // this test to work even if it does some day
         Capture<HttpRequest> cap = new Capture<HttpRequest>();
         expect(mockBackend.execute(same(host), capture(cap), (HttpContext)isNull()))
             .andReturn(resp2).times(0,1);
-        
+
         replayMocks();
         impl.execute(host, req1);
         HttpResponse result = impl.execute(host, req2);
         verifyMocks();
-        
+
         if (!cap.hasCaptured()
             && HttpStatus.SC_NOT_MODIFIED == result.getStatusLine().getStatusCode()) {
             // cache generated a 304
             assertNull(result.getFirstHeader("Content-Range"));
         }
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongEtagValidatorShouldNotContainContentRange()
     throws Exception {
         cacheGenerated304ForStrongValidatorShouldNotContainContentRange(
                 "ETag", "\"etag\"", "If-None-Match");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentRange()
     throws Exception {
         cacheGenerated304ForStrongValidatorShouldNotContainContentRange(
                 "Last-Modified", formatDate(twoMinutesAgo), "If-Modified-Since");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongEtagValidatorShouldNotContainContentType()
             throws Exception {
         cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader(
                 "Content-Type", "text/html");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongDateValidatorShouldNotContainContentType()
             throws Exception {
         cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader(
                 "Content-Type", "text/html");
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongEtagValidatorShouldNotContainLastModified()
             throws Exception {
         cacheGenerated304ForStrongETagValidatorShouldNotContainEntityHeader(
                 "Last-Modified", formatDate(tenSecondsAgo));
     }
-    
+
     @Test
     public void cacheGenerated304ForStrongDateValidatorShouldNotContainLastModified()
             throws Exception {
         cacheGenerated304ForStrongDateValidatorShouldNotContainEntityHeader(
                 "Last-Modified", formatDate(twoMinutesAgo));
     }
-    
+
     private void shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation(
             String entityHeader, String entityHeaderValue) throws Exception,
             IOException {
@@ -311,23 +311,23 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp.setHeader("Date", formatDate(now));
         resp.setHeader("Etag", "\"etag\"");
         resp.setHeader(entityHeader, entityHeaderValue);
-        
+
         backendExpectsAnyRequest().andReturn(resp);
-        
+
         replayMocks();
         HttpResponse result = impl.execute(host, req);
         verifyMocks();
-        
+
         assertNull(result.getFirstHeader(entityHeader));
     }
-    
+
     @Test
     public void shouldStripAllowFromOrigin304ResponseToStrongValidation()
     throws Exception {
         shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation(
                 "Allow", "GET,HEAD");
     }
-    
+
     @Test
     public void shouldStripContentEncodingFromOrigin304ResponseToStrongValidation()
     throws Exception {
@@ -341,14 +341,14 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation(
                 "Content-Language", "en");
     }
-    
+
     @Test
     public void shouldStripContentLengthFromOrigin304ResponseToStrongValidation()
     throws Exception {
         shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation(
                 "Content-Length", "128");
     }
-    
+
     @Test
     public void shouldStripContentMD5FromOrigin304ResponseToStrongValidation()
     throws Exception {
@@ -362,35 +362,35 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation(
                 "Content-Type", "text/html;charset=utf-8");
     }
-    
+
     @Test
     public void shouldStripContentRangeFromOrigin304ResponseToStringValidation()
             throws Exception {
         HttpRequest req = HttpTestUtils.makeDefaultRequest();
         req.setHeader("If-Range","\"etag\"");
         req.setHeader("Range","bytes=0-127");
-        
+
         HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_NOT_MODIFIED, "Not Modified");
         resp.setHeader("Date", formatDate(now));
         resp.setHeader("ETag", "\"etag\"");
         resp.setHeader("Content-Range", "bytes 0-127/256");
-        
+
         backendExpectsAnyRequest().andReturn(resp);
-        
+
         replayMocks();
         HttpResponse result = impl.execute(host, req);
         verifyMocks();
-        
+
         assertNull(result.getFirstHeader("Content-Range"));
     }
-    
+
     @Test
     public void shouldStripLastModifiedFromOrigin304ResponseToStrongValidation()
     throws Exception {
         shouldStripEntityHeaderFromOrigin304ResponseToStrongValidation(
                 "Last-Modified", formatDate(twoMinutesAgo));
     }
-    
+
     /*
      * "For this reason, a cache SHOULD NOT return a stale response if the
      * client explicitly requests a first-hand or fresh one, unless it is
@@ -616,7 +616,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
 
         assertEquals(warning, result.getFirstHeader("Warning").getValue());
     }
-    
+
     /*
      * "A transparent proxy SHOULD NOT modify an end-to-end header unless
      * the definition of that header requires or specifically allows that."
@@ -1175,7 +1175,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         }
         assertTrue(foundEtag1 && foundEtag2);
     }
-    
+
     /* "If the entity-tag of the new response matches that of an existing
      * entry, the new response SHOULD be used to update the header fields
      * of the existing entry, and the result MUST be returned to the
@@ -1237,7 +1237,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         assertEquals(formatDate(now), result1.getFirstHeader("Date").getValue());
         assertEquals(formatDate(now), result2.getFirstHeader("Date").getValue());
     }
-    
+
     @Test
     public void testResponseToExistingVariantsIsCachedForFutureResponses()
         throws Exception {
@@ -1279,7 +1279,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
      * for the associated entity, its entity-tag SHOULD NOT be included in
      * the If-None-Match header field unless the request is for a range
      * that would be fully satisfied by that entry."
-     * 
+     *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6
      */
     @Test
@@ -1291,9 +1291,9 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control", "max-age=3600");
         resp1.setHeader("Vary", "User-Agent");
         resp1.setHeader("ETag", "\"etag1\"");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("User-Agent", "agent2");
         req2.setHeader("Range", "bytes=0-49");
@@ -1306,27 +1306,27 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp2.setHeader("ETag", "\"etag2\"");
         resp2.setHeader("Cache-Control","max-age=3600");
         resp2.setHeader("Date", formatDate(new Date()));
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         HttpRequest req3 = HttpTestUtils.makeDefaultRequest();
         req3.setHeader("User-Agent", "agent3");
-        
+
         HttpResponse resp3 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control", "max-age=3600");
         resp1.setHeader("Vary", "User-Agent");
         resp1.setHeader("ETag", "\"etag3\"");
-        
+
         Capture<HttpRequest> cap = new Capture<HttpRequest>();
         expect(mockBackend.execute(isA(HttpHost.class), capture(cap),
                 (HttpContext)isNull())).andReturn(resp3);
-        
+
         replayMocks();
         impl.execute(host, req1);
         impl.execute(host, req2);
         impl.execute(host, req3);
         verifyMocks();
-        
+
         HttpRequest captured = cap.getValue();
         for(Header h : captured.getHeaders("If-None-Match")) {
             for(HeaderElement elt : h.getElements()) {
@@ -1334,15 +1334,15 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
             }
         }
     }
-    
+
     /* "If a cache receives a successful response whose Content-Location
      * field matches that of an existing cache entry for the same Request-
      * URI, whose entity-tag differs from that of the existing entry, and
      * whose Date is more recent than that of the existing entry, the
      * existing entry SHOULD NOT be returned in response to future requests
      * and SHOULD be deleted from the cache.
-     * 
-     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6 
+     *
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.6
      */
     @Test
     public void cachedEntryShouldNotBeUsedIfMoreRecentMentionInContentLocation()
@@ -1352,34 +1352,34 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Cache-Control","max-age=3600");
         resp1.setHeader("ETag", "\"old-etag\"");
         resp1.setHeader("Date", formatDate(tenSecondsAgo));
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = new HttpGet("http://foo.example.com/bar");
         HttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("ETag", "\"new-etag\"");
         resp2.setHeader("Date", formatDate(now));
         resp2.setHeader("Content-Location", "http://foo.example.com/");
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         HttpRequest req3 = new HttpGet("http://foo.example.com");
         HttpResponse resp3 = HttpTestUtils.make200Response();
-        
+
         backendExpectsAnyRequest().andReturn(resp3);
-        
+
         replayMocks();
         impl.execute(host, req1);
         impl.execute(host, req2);
         impl.execute(host, req3);
         verifyMocks();
     }
-    
+
     /*
      * "This specifically means that responses from HTTP/1.0 servers for such
      * URIs [those containing a '?' in the rel_path part] SHOULD NOT be taken
      * from a cache."
-     * 
+     *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.9
      */
     @Test
@@ -1391,23 +1391,23 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Content-Length","200");
         resp1.setHeader("Date", formatDate(now));
         resp1.setHeader("Expires", formatDate(tenSecondsFromNow));
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = new HttpGet("http://foo.example.com/bar?baz=quux");
         HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
         resp2.setEntity(HttpTestUtils.makeBody(200));
         resp2.setHeader("Content-Length","200");
         resp2.setHeader("Date", formatDate(now));
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host, req1);
         impl.execute(host, req2);
         verifyMocks();
     }
-    
+
     @Test
     public void responseToGetWithQueryFrom1_0OriginVia1_1ProxyIsNotCached()
         throws Exception {
@@ -1418,18 +1418,18 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Date", formatDate(now));
         resp1.setHeader("Expires", formatDate(tenSecondsFromNow));
         resp1.setHeader("Via","1.0 someproxy");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = new HttpGet("http://foo.example.com/bar?baz=quux");
         HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
         resp2.setEntity(HttpTestUtils.makeBody(200));
         resp2.setHeader("Content-Length","200");
         resp2.setHeader("Date", formatDate(now));
         resp2.setHeader("Via","1.0 someproxy");
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host, req1);
         impl.execute(host, req2);
@@ -1440,7 +1440,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
      * "A cache that passes through requests for methods it does not
      * understand SHOULD invalidate any entities referred to by the
      * Request-URI."
-     * 
+     *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10
      */
     @Test
@@ -1449,30 +1449,30 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control","max-age=3600");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = new BasicHttpRequest("FROB", "/", HttpVersion.HTTP_1_1);
         HttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("Cache-Control","max-age=3600");
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         HttpRequest req3 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
         HttpResponse resp3 = HttpTestUtils.make200Response();
         resp3.setHeader("ETag", "\"etag\"");
-        
+
         backendExpectsAnyRequest().andReturn(resp3);
-        
+
         replayMocks();
         impl.execute(host, req1);
         impl.execute(host, req2);
         HttpResponse result = impl.execute(host, req3);
         verifyMocks();
-        
+
         assertTrue(HttpTestUtils.semanticallyTransparent(resp3, result));
     }
-    
+
     @Test
     public void shouldInvalidateAllVariantsForUnknownMethod()
         throws Exception {
@@ -1481,7 +1481,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control","max-age=3600");
         resp1.setHeader("Vary", "User-Agent");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
 
         HttpRequest req2 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
@@ -1489,30 +1489,30 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         HttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("Cache-Control","max-age=3600");
         resp2.setHeader("Vary", "User-Agent");
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         HttpRequest req3 = new BasicHttpRequest("FROB", "/", HttpVersion.HTTP_1_1);
         req3.setHeader("User-Agent", "agent3");
         HttpResponse resp3 = HttpTestUtils.make200Response();
         resp3.setHeader("Cache-Control","max-age=3600");
-        
+
         backendExpectsAnyRequest().andReturn(resp3);
-        
+
         HttpRequest req4 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
         req4.setHeader("User-Agent", "agent1");
         HttpResponse resp4 = HttpTestUtils.make200Response();
         resp4.setHeader("ETag", "\"etag1\"");
-        
+
         backendExpectsAnyRequest().andReturn(resp4);
 
         HttpRequest req5 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
         req5.setHeader("User-Agent", "agent2");
         HttpResponse resp5 = HttpTestUtils.make200Response();
         resp5.setHeader("ETag", "\"etag2\"");
-        
+
         backendExpectsAnyRequest().andReturn(resp5);
-        
+
         replayMocks();
         impl.execute(host, req1);
         impl.execute(host, req2);
@@ -1520,7 +1520,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         HttpResponse result4 = impl.execute(host, req4);
         HttpResponse result5 = impl.execute(host, req5);
         verifyMocks();
-        
+
         assertTrue(HttpTestUtils.semanticallyTransparent(resp4, result4));
         assertTrue(HttpTestUtils.semanticallyTransparent(resp5, result5));
     }
@@ -1529,7 +1529,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
      * "If a new cacheable response is received from a resource while any
      * existing responses for the same resource are cached, the cache
      * SHOULD use the new response to reply to the current request."
-     * 
+     *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.12
      */
     @Test
@@ -1540,29 +1540,29 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Date", formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control", "max-age=3600");
         resp1.setHeader("ETag", "\"etag1\"");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "max-age=0");
         HttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("Date", formatDate(now));
         resp2.setHeader("Cache-Control", "max-age=3600");
         resp2.setHeader("ETag", "\"etag2\"");
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         HttpRequest req3 = HttpTestUtils.makeDefaultRequest();
-        
+
         replayMocks();
         impl.execute(host, req1);
         impl.execute(host, req2);
         HttpResponse result = impl.execute(host, req3);
         verifyMocks();
-        
+
         assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
     }
-    
+
     /*
      * "Many HTTP/1.0 cache implementations will treat an Expires value
      * that is less than or equal to the response Date value as being
@@ -1571,7 +1571,7 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
      * does not include a Cache-Control header field, it SHOULD consider
      * the response to be non-cacheable in order to retain compatibility
      * with HTTP/1.0 servers."
-     * 
+     *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.3
      */
     @Test
@@ -1582,24 +1582,24 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Date", formatDate(now));
         resp1.setHeader("Expires", formatDate(now));
         resp1.removeHeaders("Cache-Control");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "max-stale=1000");
         HttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("ETag", "\"etag2\"");
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host, req1);
         HttpResponse result = impl.execute(host, req2);
         verifyMocks();
-        
+
         assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
     }
-    
+
     @Test
     public void expiresPriorToDateWithNoCacheControlIsNotCacheable()
         throws Exception {
@@ -1608,28 +1608,28 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         resp1.setHeader("Date", formatDate(now));
         resp1.setHeader("Expires", formatDate(tenSecondsAgo));
         resp1.removeHeaders("Cache-Control");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "max-stale=1000");
         HttpResponse resp2 = HttpTestUtils.make200Response();
         resp2.setHeader("ETag", "\"etag2\"");
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host, req1);
         HttpResponse result = impl.execute(host, req2);
         verifyMocks();
-        
+
         assertTrue(HttpTestUtils.semanticallyTransparent(resp2, result));
     }
 
     /*
      * "If a request includes the no-cache directive, it SHOULD NOT
      * include min-fresh, max-stale, or max-age."
-     * 
+     *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4
      */
     @Test
@@ -1638,15 +1638,15 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         req1.setHeader("Cache-Control", "min-fresh=10, no-cache");
         req1.addHeader("Cache-Control", "max-stale=0, max-age=0");
-        
+
         Capture<HttpRequest> cap = new Capture<HttpRequest>();
         expect(mockBackend.execute(same(host), capture(cap), (HttpContext)isNull()))
             .andReturn(HttpTestUtils.make200Response());
-        
+
         replayMocks();
         impl.execute(host, req1);
         verifyMocks();
-        
+
         HttpRequest captured = cap.getValue();
         boolean foundNoCache = false;
         boolean foundDisallowedDirective = false;
@@ -1665,14 +1665,14 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         assertTrue(foundNoCache);
         assertFalse(foundDisallowedDirective);
     }
-    
+
     /*
      * "To do this, the client may include the only-if-cached directive in
      * a request. If it receives this directive, a cache SHOULD either
      * respond using a cached entry that is consistent with the other
      * constraints of the request, or respond with a 504 (Gateway Timeout)
      * status."
-     * 
+     *
      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4
      */
     @Test
@@ -1680,35 +1680,35 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         throws Exception {
         HttpRequest req = HttpTestUtils.makeDefaultRequest();
         req.setHeader("Cache-Control", "only-if-cached");
-        
+
         replayMocks();
         HttpResponse result = impl.execute(host, req);
         verifyMocks();
-        
+
         assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT,
                 result.getStatusLine().getStatusCode());
     }
-    
+
     @Test
     public void cacheHitOkWithOnlyIfCached()
         throws Exception {
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control","max-age=3600");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "only-if-cached");
-        
+
         replayMocks();
         impl.execute(host, req1);
         HttpResponse result = impl.execute(host, req2);
         verifyMocks();
-        
+
         assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result));
     }
-    
+
     @Test
     public void returns504ForStaleEntryWithOnlyIfCached()
         throws Exception {
@@ -1716,21 +1716,21 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Date", formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control","max-age=5");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "only-if-cached");
-        
+
         replayMocks();
         impl.execute(host, req1);
         HttpResponse result = impl.execute(host, req2);
         verifyMocks();
-        
+
         assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT,
                 result.getStatusLine().getStatusCode());
     }
-    
+
     @Test
     public void returnsStaleCacheEntryWithOnlyIfCachedAndMaxStale()
         throws Exception {
@@ -1739,18 +1739,18 @@ public class TestProtocolRecommendations extends AbstractProtocolTest {
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Date", formatDate(tenSecondsAgo));
         resp1.setHeader("Cache-Control","max-age=5");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
-        
+
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control", "max-stale=20, only-if-cached");
-        
+
         replayMocks();
         impl.execute(host, req1);
         HttpResponse result = impl.execute(host, req2);
         verifyMocks();
-        
+
         assertTrue(HttpTestUtils.semanticallyTransparent(resp1, result));
     }
-    
+
 }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java
index 83308a4..98c181e 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestProtocolRequirements.java
@@ -5836,4 +5836,4 @@ public class TestProtocolRequirements extends AbstractProtocolTest {
         Assert.assertTrue(warningHeaders == null || warningHeaders.length == 0);
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRFC5861Compliance.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRFC5861Compliance.java
index 58de0a6..7c1b9b5 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRFC5861Compliance.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRFC5861Compliance.java
@@ -27,37 +27,41 @@
 package org.apache.http.impl.client.cache;
 
 import static org.junit.Assert.*;
+
+import java.io.ByteArrayInputStream;
 import java.util.Date;
 
 import org.apache.http.Header;
+import org.apache.http.HttpEntity;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpStatus;
 import org.apache.http.HttpVersion;
+import org.apache.http.entity.InputStreamEntity;
 import org.apache.http.impl.cookie.DateUtils;
 import org.apache.http.message.BasicHttpRequest;
 import org.junit.Test;
 
-/** 
+/**
  * A suite of acceptance tests for compliance with RFC5861, which
  * describes the stale-if-error and stale-while-revalidate
  * Cache-Control extensions.
  */
 public class TestRFC5861Compliance extends AbstractProtocolTest {
 
-    /*    
+    /*
      * "The stale-if-error Cache-Control extension indicates that when an
      * error is encountered, a cached stale response MAY be used to satisfy
-     * the request, regardless of other freshness information.When used as a 
+     * the request, regardless of other freshness information.When used as a
      * request Cache-Control extension, its scope of application is the request
-     * it appears in; when used as a response Cache-Control extension, its 
-     * scope is any request applicable to the cached response in which it 
+     * it appears in; when used as a response Cache-Control extension, its
+     * scope is any request applicable to the cached response in which it
      * occurs.Its value indicates the upper limit to staleness; when the cached
      * response is more stale than the indicated amount, the cached response
      * SHOULD NOT be used to satisfy the request, absent other information.
      * In this context, an error is any situation that would result in a
      * 500, 502, 503, or 504 HTTP response status code being returned."
-     *  
+     *
      * http://tools.ietf.org/html/rfc5861
      */
     @Test
@@ -67,22 +71,50 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
 
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp2 = HttpTestUtils.make500Response();
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host,req1);
         HttpResponse result = impl.execute(host,req2);
         verifyMocks();
-        
+
         HttpTestUtils.assert110WarningFound(result);
     }
-    
+
+    @Test
+    public void testConsumesErrorResponseWhenServingStale()
+            throws Exception{
+        Date tenSecondsAgo = new Date(new Date().getTime() - 10 * 1000L);
+        HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
+        HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
+                "public, max-age=5, stale-if-error=60");
+
+        backendExpectsAnyRequest().andReturn(resp1);
+
+        HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
+        HttpResponse resp2 = HttpTestUtils.make500Response();
+        byte[] body = HttpTestUtils.getRandomBytes(101);
+        ByteArrayInputStream buf = new ByteArrayInputStream(body);
+        ConsumableInputStream cis = new ConsumableInputStream(buf);
+        HttpEntity entity = new InputStreamEntity(cis, 101);
+        resp2.setEntity(entity);
+
+        backendExpectsAnyRequest().andReturn(resp2);
+
+        replayMocks();
+        impl.execute(host,req1);
+        impl.execute(host,req2);
+        verifyMocks();
+
+        assertTrue(cis.wasClosed());
+    }
+
     @Test
     public void testStaleIfErrorInResponseYieldsToMustRevalidate()
             throws Exception{
@@ -90,22 +122,22 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60, must-revalidate");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
 
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp2 = HttpTestUtils.make500Response();
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host,req1);
         HttpResponse result = impl.execute(host,req2);
         verifyMocks();
-        
+
         assertTrue(HttpStatus.SC_OK != result.getStatusLine().getStatusCode());
     }
-    
+
     @Test
     public void testStaleIfErrorInResponseYieldsToProxyRevalidateForSharedCache()
             throws Exception{
@@ -114,46 +146,46 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60, proxy-revalidate");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
 
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp2 = HttpTestUtils.make500Response();
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host,req1);
         HttpResponse result = impl.execute(host,req2);
         verifyMocks();
-        
+
         assertTrue(HttpStatus.SC_OK != result.getStatusLine().getStatusCode());
     }
-    
+
     @Test
     public void testStaleIfErrorInResponseNeedNotYieldToProxyRevalidateForPrivateCache()
             throws Exception{
         CacheConfig config = new CacheConfig();
         config.setSharedCache(false);
         impl = new CachingHttpClient(mockBackend, config);
-        
+
         Date tenSecondsAgo = new Date(new Date().getTime() - 10 * 1000L);
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60, proxy-revalidate");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
 
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp2 = HttpTestUtils.make500Response();
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host,req1);
         HttpResponse result = impl.execute(host,req2);
         verifyMocks();
-        
+
         HttpTestUtils.assert110WarningFound(result);
     }
 
@@ -164,23 +196,23 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=60");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
 
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control","min-fresh=2");
         HttpResponse resp2 = HttpTestUtils.make500Response();
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host,req1);
         HttpResponse result = impl.execute(host,req2);
         verifyMocks();
-        
+
         assertTrue(HttpStatus.SC_OK != result.getStatusLine().getStatusCode());
     }
-    
+
     @Test
     public void testStaleIfErrorInRequestIsTrueReturnsStaleEntryWithWarning()
             throws Exception{
@@ -188,23 +220,23 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
 
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control","public, stale-if-error=60");
         HttpResponse resp2 = HttpTestUtils.make500Response();
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host,req1);
         HttpResponse result = impl.execute(host,req2);
         verifyMocks();
-        
+
         HttpTestUtils.assert110WarningFound(result);
     }
-    
+
     @Test
     public void testStaleIfErrorInResponseIsFalseReturnsError()
             throws Exception{
@@ -213,14 +245,14 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5, stale-if-error=2");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
 
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp2 = HttpTestUtils.make500Response();
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host,req1);
         HttpResponse result = impl.execute(host,req2);
@@ -229,7 +261,7 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR,
                 result.getStatusLine().getStatusCode());
     }
-    
+
     @Test
     public void testStaleIfErrorInRequestIsFalseReturnsError()
             throws Exception{
@@ -238,15 +270,15 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         HttpRequest req1 = HttpTestUtils.makeDefaultRequest();
         HttpResponse resp1 = HttpTestUtils.make200Response(tenSecondsAgo,
                 "public, max-age=5");
-        
+
         backendExpectsAnyRequest().andReturn(resp1);
 
         HttpRequest req2 = HttpTestUtils.makeDefaultRequest();
         req2.setHeader("Cache-Control","stale-if-error=2");
         HttpResponse resp2 = HttpTestUtils.make500Response();
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
-        
+
         replayMocks();
         impl.execute(host,req1);
         HttpResponse result = impl.execute(host,req2);
@@ -255,22 +287,22 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR,
                 result.getStatusLine().getStatusCode());
     }
-    
+
     /*
      * When present in an HTTP response, the stale-while-revalidate Cache-
      * Control extension indicates that caches MAY serve the response in
      * which it appears after it becomes stale, up to the indicated number
      * of seconds.
-     * 
+     *
      * http://tools.ietf.org/html/rfc5861
      */
     @Test
     public void testStaleWhileRevalidateReturnsStaleEntryWithWarning()
         throws Exception {
-        
+
         params.setAsynchronousWorkersMax(1);
         impl = new CachingHttpClient(mockBackend, cache, params);
-        
+
         HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
         HttpResponse resp1 = HttpTestUtils.make200Response();
         Date now = new Date();
@@ -300,17 +332,57 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         }
         assertTrue(warning110Found);
     }
-    
+
+    @Test
+    public void testCanAlsoServeStale304sWhileRevalidating()
+        throws Exception {
+
+        params.setAsynchronousWorkersMax(1);
+        params.setSharedCache(false);
+        impl = new CachingHttpClient(mockBackend, cache, params);
+
+        HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
+        HttpResponse resp1 = HttpTestUtils.make200Response();
+        Date now = new Date();
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+        resp1.setHeader("Cache-Control", "private, stale-while-revalidate=15");
+        resp1.setHeader("ETag","\"etag\"");
+        resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
+
+        backendExpectsAnyRequest().andReturn(resp1).times(1,2);
+
+        HttpRequest req2 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
+        req2.setHeader("If-None-Match","\"etag\"");
+
+        replayMocks();
+        impl.execute(host, req1);
+        HttpResponse result = impl.execute(host, req2);
+        verifyMocks();
+
+        assertEquals(HttpStatus.SC_NOT_MODIFIED, result.getStatusLine().getStatusCode());
+        boolean warning110Found = false;
+        for(Header h : result.getHeaders("Warning")) {
+            for(WarningValue wv : WarningValue.getWarningValues(h)) {
+                if (wv.getWarnCode() == 110) {
+                    warning110Found = true;
+                    break;
+                }
+            }
+        }
+        assertTrue(warning110Found);
+    }
+
+
     @Test
     public void testStaleWhileRevalidateYieldsToMustRevalidate()
         throws Exception {
-        
+
         Date now = new Date();
         Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
-        
+
         params.setAsynchronousWorkersMax(1);
         impl = new CachingHttpClient(mockBackend, cache, params);
-        
+
         HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, must-revalidate");
@@ -324,7 +396,7 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, must-revalidate");
         resp2.setHeader("ETag","\"etag\"");
         resp2.setHeader("Date", DateUtils.formatDate(now));
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
 
         replayMocks();
@@ -348,14 +420,14 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
     @Test
     public void testStaleWhileRevalidateYieldsToProxyRevalidateForSharedCache()
         throws Exception {
-        
+
         Date now = new Date();
         Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
-        
+
         params.setAsynchronousWorkersMax(1);
         params.setSharedCache(true);
         impl = new CachingHttpClient(mockBackend, cache, params);
-        
+
         HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, proxy-revalidate");
@@ -369,7 +441,7 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15, proxy-revalidate");
         resp2.setHeader("ETag","\"etag\"");
         resp2.setHeader("Date", DateUtils.formatDate(now));
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
 
         replayMocks();
@@ -393,14 +465,14 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
     @Test
     public void testStaleWhileRevalidateYieldsToExplicitFreshnessRequest()
         throws Exception {
-        
+
         Date now = new Date();
         Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
-        
+
         params.setAsynchronousWorkersMax(1);
         params.setSharedCache(true);
         impl = new CachingHttpClient(mockBackend, cache, params);
-        
+
         HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
         HttpResponse resp1 = HttpTestUtils.make200Response();
         resp1.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15");
@@ -415,7 +487,7 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         resp2.setHeader("Cache-Control", "public, max-age=5, stale-while-revalidate=15");
         resp2.setHeader("ETag","\"etag\"");
         resp2.setHeader("Date", DateUtils.formatDate(now));
-        
+
         backendExpectsAnyRequest().andReturn(resp2);
 
         replayMocks();
@@ -435,5 +507,5 @@ public class TestRFC5861Compliance extends AbstractProtocolTest {
         }
         assertFalse(warning110Found);
     }
-    
+
 }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRequestProtocolCompliance.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRequestProtocolCompliance.java
index 39f52d7..11e64b2 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRequestProtocolCompliance.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestRequestProtocolCompliance.java
@@ -42,22 +42,22 @@ public class TestRequestProtocolCompliance {
     private RequestProtocolCompliance impl;
     private HttpRequest req;
     private HttpRequest result;
-    
+
     @Before
     public void setUp() {
         req = HttpTestUtils.makeDefaultRequest();
         impl = new RequestProtocolCompliance();
     }
-    
+
     @Test
     public void doesNotModifyACompliantRequest() throws Exception {
-       result = impl.makeRequestCompliant(req); 
+       result = impl.makeRequestCompliant(req);
        assertTrue(HttpTestUtils.equivalent(req, result));
     }
-    
+
     @Test
     public void removesEntityFromTRACERequest() throws Exception {
-        HttpEntityEnclosingRequest req = 
+        HttpEntityEnclosingRequest req =
             new BasicHttpEntityEnclosingRequest("TRACE", "/", HttpVersion.HTTP_1_1);
         req.setEntity(HttpTestUtils.makeBody(50));
         result = impl.makeRequestCompliant(req);
@@ -65,7 +65,7 @@ public class TestRequestProtocolCompliance {
             assertNull(((HttpEntityEnclosingRequest)result).getEntity());
         }
     }
-    
+
     @Test
     public void upgrades1_0RequestTo1_1() throws Exception {
         req = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_0);
@@ -80,7 +80,7 @@ public class TestRequestProtocolCompliance {
         result = impl.makeRequestCompliant(req);
         assertEquals(HttpVersion.HTTP_1_1, result.getProtocolVersion());
     }
-    
+
     @Test
     public void stripsMinFreshFromRequestIfNoCachePresent()
         throws Exception {
@@ -107,7 +107,7 @@ public class TestRequestProtocolCompliance {
         assertEquals("no-cache",
                 result.getFirstHeader("Cache-Control").getValue());
     }
-    
+
     @Test
     public void doesNotStripMinFreshFromRequestWithoutNoCache()
         throws Exception {
@@ -116,7 +116,7 @@ public class TestRequestProtocolCompliance {
         assertEquals("min-fresh=10",
                 result.getFirstHeader("Cache-Control").getValue());
     }
-    
+
     @Test
     public void correctlyStripsMinFreshFromMiddleIfNoCache()
         throws Exception {
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java
index 7336c40..e1f28da 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseCachingPolicy.java
@@ -60,7 +60,7 @@ public class TestResponseCachingPolicy {
         now = new Date();
         sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
         tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
-        
+
         policy = new ResponseCachingPolicy(0, true);
         request = new BasicHttpRequest("GET","/",HTTP_1_1);
         response = new BasicHttpResponse(
@@ -397,7 +397,7 @@ public class TestResponseCachingPolicy {
         response = new BasicHttpResponse(HttpVersion.HTTP_1_0, HttpStatus.SC_OK, "OK");
         Assert.assertFalse(policy.isResponseCacheable(request, response));
     }
-    
+
     @Test
     public void getsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheableEvenWithExpires() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
@@ -406,7 +406,7 @@ public class TestResponseCachingPolicy {
         response.setHeader("Expires", formatDate(tenSecondsFromNow));
         Assert.assertFalse(policy.isResponseCacheable(request, response));
     }
-    
+
     @Test
     public void getsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
@@ -424,7 +424,7 @@ public class TestResponseCachingPolicy {
         response.setHeader("Via", "1.0 someproxy");
         Assert.assertFalse(policy.isResponseCacheable(request, response));
     }
-    
+
     @Test
     public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreNotCacheableEvenWithExpires() {
         request = new BasicHttpRequest("GET", "/foo?s=bar");
@@ -443,7 +443,7 @@ public class TestResponseCachingPolicy {
         response.setHeader("Via", "1.1 someproxy");
         Assert.assertTrue(policy.isResponseCacheable(request, response));
     }
-    
+
     @Test
     public void notCacheableIfExpiresEqualsDateAndNoCacheControl() {
         response.setHeader("Date", formatDate(now));
@@ -459,6 +459,14 @@ public class TestResponseCachingPolicy {
         response.removeHeaders("Cache-Control");
         Assert.assertFalse(policy.isResponseCacheable(request, response));
     }
+    
+    @Test
+    public void otherStatusCodesAreCacheableWithExplicitCachingHeaders() {
+    	response.setStatusCode(HttpStatus.SC_NOT_FOUND);
+    	response.setHeader("Date", formatDate(now));
+    	response.setHeader("Cache-Control","max-age=300");
+    	Assert.assertTrue(policy.isResponseCacheable(request, response));
+    }
 
     private int getRandomStatus() {
         int rnd = (new Random()).nextInt(acceptableCodes.length);
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseProtocolCompliance.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseProtocolCompliance.java
new file mode 100644
index 0000000..1f4e4c0
--- /dev/null
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestResponseProtocolCompliance.java
@@ -0,0 +1,152 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayInputStream;
+import java.util.Date;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.impl.cookie.DateUtils;
+import org.apache.http.message.BasicHttpEntityEnclosingRequest;
+import org.apache.http.message.BasicHttpResponse;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestResponseProtocolCompliance {
+
+    private ResponseProtocolCompliance impl;
+
+    @Before
+    public void setUp() {
+        impl = new ResponseProtocolCompliance();
+    }
+
+    private static class Flag {
+        public boolean set;
+    }
+
+    private void setMinimalResponseHeaders(HttpResponse resp) {
+        resp.setHeader("Date", DateUtils.formatDate(new Date()));
+        resp.setHeader("Server", "MyServer/1.0");
+    }
+
+    private ByteArrayInputStream makeTrackableBody(int nbytes, final Flag closed) {
+        byte[] buf = HttpTestUtils.getRandomBytes(nbytes);
+        ByteArrayInputStream bais = new ByteArrayInputStream(buf) {
+            @Override
+            public void close() {
+                closed.set = true;
+            }
+        };
+        return bais;
+    }
+
+    private HttpResponse makePartialResponse(int nbytes) {
+        HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_PARTIAL_CONTENT, "Partial Content");
+        setMinimalResponseHeaders(resp);
+        resp.setHeader("Content-Length","" + nbytes);
+        resp.setHeader("Content-Range","0-127/256");
+        return resp;
+    }
+
+    @Test
+    public void consumesBodyIfOriginSendsOneInResponseToHEAD() throws Exception {
+        HttpRequest req = new HttpHead("http://foo.example.com/");
+        int nbytes = 128;
+        HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+        setMinimalResponseHeaders(resp);
+        resp.setHeader("Content-Length","" + nbytes);
+
+        final Flag closed = new Flag();
+        ByteArrayInputStream bais = makeTrackableBody(nbytes, closed);
+        resp.setEntity(new InputStreamEntity(bais, -1));
+
+        impl.ensureProtocolCompliance(req, resp);
+        assertNull(resp.getEntity());
+        assertTrue(closed.set || bais.read() == -1);
+    }
+
+    @Test(expected=ClientProtocolException.class)
+    public void throwsExceptionIfOriginReturnsPartialResponseWhenNotRequested() throws Exception {
+        HttpRequest req = new HttpGet("http://foo.example.com/");
+        int nbytes = 128;
+        HttpResponse resp = makePartialResponse(nbytes);
+        resp.setEntity(HttpTestUtils.makeBody(nbytes));
+
+        impl.ensureProtocolCompliance(req, resp);
+    }
+
+    @Test
+    public void consumesPartialContentFromOriginEvenIfNotRequested() throws Exception {
+        HttpRequest req = new HttpGet("http://foo.example.com/");
+        int nbytes = 128;
+        HttpResponse resp = makePartialResponse(nbytes);
+
+        final Flag closed = new Flag();
+        ByteArrayInputStream bais = makeTrackableBody(nbytes, closed);
+        resp.setEntity(new InputStreamEntity(bais, -1));
+
+        try {
+            impl.ensureProtocolCompliance(req, resp);
+        } catch (ClientProtocolException expected) {
+        }
+        assertTrue(closed.set || bais.read() == -1);
+    }
+
+    @Test
+    public void consumesBodyOf100ContinueResponseIfItArrives() throws Exception {
+        HttpEntityEnclosingRequest req = new BasicHttpEntityEnclosingRequest("POST", "/", HttpVersion.HTTP_1_1);
+        int nbytes = 128;
+        req.setHeader("Content-Length","" + nbytes);
+        req.setHeader("Content-Type", "application/octet-stream");
+        HttpEntity postBody = new ByteArrayEntity(HttpTestUtils.getRandomBytes(nbytes));
+        req.setEntity(postBody);
+
+        HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_CONTINUE, "Continue");
+        final Flag closed = new Flag();
+        ByteArrayInputStream bais = makeTrackableBody(nbytes, closed);
+        resp.setEntity(new InputStreamEntity(bais, -1));
+
+        try {
+            impl.ensureProtocolCompliance(req, resp);
+        } catch (ClientProtocolException expected) {
+        }
+        assertTrue(closed.set || bais.read() == -1);
+    }
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestSizeLimitedResponseReader.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestSizeLimitedResponseReader.java
index 53ba64e..90bf669 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestSizeLimitedResponseReader.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestSizeLimitedResponseReader.java
@@ -118,7 +118,7 @@ public class TestSizeLimitedResponseReader {
     @Test
     public void testTooLargeEntityHasOriginalContentTypes() throws Exception {
         HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
-        StringEntity entity = new StringEntity("large entity content", "text/plain", "utf-8");
+        StringEntity entity = new StringEntity("large entity content");
         response.setEntity(entity);
 
         impl = new SizeLimitedResponseReader(new HeapResourceFactory(), MAX_SIZE, request, response);
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/ehcache/TestEhcacheProtocolRequirements.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/ehcache/TestEhcacheProtocolRequirements.java
index e669f08..21906b3 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/ehcache/TestEhcacheProtocolRequirements.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/ehcache/TestEhcacheProtocolRequirements.java
@@ -64,7 +64,7 @@ public class TestEhcacheProtocolRequirements extends TestProtocolRequirements{
     public void setUp() {
         super.setUp();
         params = new CacheConfig();
-        params.setMaxObjectSizeBytes(MAX_BYTES);
+        params.setMaxObjectSize(MAX_BYTES);
 
         if (CACHE_MANAGER.cacheExists(TEST_EHCACHE_NAME)){
             CACHE_MANAGER.removeCache(TEST_EHCACHE_NAME);
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedCacheEntryFactoryImpl.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedCacheEntryFactoryImpl.java
new file mode 100644
index 0000000..7653e0d
--- /dev/null
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedCacheEntryFactoryImpl.java
@@ -0,0 +1,48 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import static org.junit.Assert.*;
+
+import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.impl.client.cache.HttpTestUtils;
+import org.junit.Test;
+
+
+public class TestMemcachedCacheEntryFactoryImpl {
+
+    @Test
+    public void createsMemcachedCacheEntryImpls() {
+        String key = "key";
+        HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
+        MemcachedCacheEntryFactoryImpl impl = new MemcachedCacheEntryFactoryImpl();
+        MemcachedCacheEntry result = impl.getMemcachedCacheEntry(key, entry);
+        assertNotNull(result);
+        assertSame(key, result.getStorageKey());
+        assertSame(entry, result.getHttpCacheEntry());
+    }
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedCacheEntryImpl.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedCacheEntryImpl.java
new file mode 100644
index 0000000..3aea454
--- /dev/null
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedCacheEntryImpl.java
@@ -0,0 +1,117 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import static org.junit.Assert.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.impl.client.cache.DefaultHttpCacheEntrySerializer;
+import org.apache.http.impl.client.cache.HttpTestUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class TestMemcachedCacheEntryImpl {
+
+    private MemcachedCacheEntryImpl impl;
+    private HttpCacheEntry entry;
+
+    @Before
+    public void setUp() {
+        entry = HttpTestUtils.makeCacheEntry();
+        impl = new MemcachedCacheEntryImpl("foo", entry);
+    }
+    
+    @Test
+    public void canBeCreatedEmpty() {
+        impl = new MemcachedCacheEntryImpl();
+        assertNull(impl.getStorageKey());
+        assertNull(impl.getHttpCacheEntry());
+    }
+    
+    @Test
+    public void canBeSerialized() {
+        byte[] bytes = impl.toByteArray();
+        assertNotNull(bytes);
+        assertTrue(bytes.length > 0);
+    }
+    
+    @Test
+    public void knowsItsCacheKey() {
+        assertEquals("foo", impl.getStorageKey());
+    }
+    
+    @Test
+    public void knowsItsCacheEntry() {
+        assertEquals(entry, impl.getHttpCacheEntry());
+    }
+    
+    @Test
+    public void canBeReconstitutedFromByteArray() throws Exception {
+        String key = impl.getStorageKey();
+        HttpCacheEntry entry = impl.getHttpCacheEntry();
+        byte[] bytes = impl.toByteArray();
+        impl = new MemcachedCacheEntryImpl();
+        impl.set(bytes);
+        
+        assertEquals(key, impl.getStorageKey());
+        assertEquivalent(entry, impl.getHttpCacheEntry());
+    }
+    
+    @Test(expected=MemcachedSerializationException.class)
+    public void cannotReconstituteFromGarbage() {
+        impl = new MemcachedCacheEntryImpl();
+        byte[] bytes = HttpTestUtils.getRandomBytes(128);
+        impl.set(bytes);
+    }
+
+    private void assertEquivalent(HttpCacheEntry entry,
+            HttpCacheEntry resultEntry) throws IOException {
+        /* Ugh. Implementing HttpCacheEntry#equals is problematic
+         * due to the Resource response body (may cause unexpected
+         * I/O to users). Checking that two entries
+         * serialize to the same bytes seems simpler, on the whole,
+         * (while still making for a somewhat yucky test). At
+         * least we encapsulate it off here in its own method so
+         * the test that uses it remains clear.
+         */
+        DefaultHttpCacheEntrySerializer ser = new DefaultHttpCacheEntrySerializer();
+        ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
+        ser.writeTo(entry, bos1);
+        byte[] bytes1 = bos1.toByteArray();
+        ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
+        ser.writeTo(resultEntry, bos2);
+        byte[] bytes2 = bos2.toByteArray();
+        assertEquals(bytes1.length, bytes2.length);
+        for(int i = 0; i < bytes1.length; i++) {
+            assertEquals(bytes1[i], bytes2[i]);
+        }
+    }
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
index e2422eb..e6821ab 100644
--- a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestMemcachedHttpCacheStorage.java
@@ -27,17 +27,15 @@
 package org.apache.http.impl.client.cache.memcached;
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 
 import junit.framework.TestCase;
 import net.spy.memcached.CASResponse;
 import net.spy.memcached.CASValue;
 import net.spy.memcached.MemcachedClientIF;
+import net.spy.memcached.OperationTimeoutException;
 
 import org.apache.http.client.cache.HttpCacheEntry;
-import org.apache.http.client.cache.HttpCacheEntrySerializer;
 import org.apache.http.client.cache.HttpCacheUpdateCallback;
 import org.apache.http.client.cache.HttpCacheUpdateException;
 import org.apache.http.impl.client.cache.CacheConfig;
@@ -49,85 +47,280 @@ import org.junit.Test;
 public class TestMemcachedHttpCacheStorage extends TestCase {
     private MemcachedHttpCacheStorage impl;
     private MemcachedClientIF mockMemcachedClient;
-    private HttpCacheEntrySerializer mockSerializer;
+    private KeyHashingScheme mockKeyHashingScheme;
+    private MemcachedCacheEntryFactory mockMemcachedCacheEntryFactory;
+    private MemcachedCacheEntry mockMemcachedCacheEntry;
+    private MemcachedCacheEntry mockMemcachedCacheEntry2;
+    private MemcachedCacheEntry mockMemcachedCacheEntry3;
+    private MemcachedCacheEntry mockMemcachedCacheEntry4;
 
     @Override
     @Before
     public void setUp() throws Exception {
         mockMemcachedClient = EasyMock.createMock(MemcachedClientIF.class);
-        mockSerializer = EasyMock.createMock(HttpCacheEntrySerializer.class);
+        mockKeyHashingScheme = EasyMock.createMock(KeyHashingScheme.class);
+        mockMemcachedCacheEntryFactory = EasyMock.createMock(MemcachedCacheEntryFactory.class);
+        mockMemcachedCacheEntry = EasyMock.createMock(MemcachedCacheEntry.class);
+        mockMemcachedCacheEntry2 = EasyMock.createMock(MemcachedCacheEntry.class);
+        mockMemcachedCacheEntry3 = EasyMock.createMock(MemcachedCacheEntry.class);
+        mockMemcachedCacheEntry4 = EasyMock.createMock(MemcachedCacheEntry.class);
         CacheConfig config = new CacheConfig();
         config.setMaxUpdateRetries(1);
         impl = new MemcachedHttpCacheStorage(mockMemcachedClient, config,
-                mockSerializer);
+                mockMemcachedCacheEntryFactory, mockKeyHashingScheme);
     }
 
     private void replayMocks() {
         EasyMock.replay(mockMemcachedClient);
-        EasyMock.replay(mockSerializer);
+        EasyMock.replay(mockKeyHashingScheme);
+        EasyMock.replay(mockMemcachedCacheEntry);
+        EasyMock.replay(mockMemcachedCacheEntry2);
+        EasyMock.replay(mockMemcachedCacheEntry3);
+        EasyMock.replay(mockMemcachedCacheEntry4);
+        EasyMock.replay(mockMemcachedCacheEntryFactory);
     }
 
     private void verifyMocks() {
         EasyMock.verify(mockMemcachedClient);
-        EasyMock.verify(mockSerializer);
+        EasyMock.verify(mockKeyHashingScheme);
+        EasyMock.verify(mockMemcachedCacheEntry);
+        EasyMock.verify(mockMemcachedCacheEntry2);
+        EasyMock.verify(mockMemcachedCacheEntry3);
+        EasyMock.verify(mockMemcachedCacheEntry4);
+        EasyMock.verify(mockMemcachedCacheEntryFactory);
     }
 
     @Test
-    public void testCachePut() throws IOException {
+    public void testSuccessfulCachePut() throws IOException {
         final String url = "foo";
+        final String key = "key";
         final HttpCacheEntry value = HttpTestUtils.makeCacheEntry();
-        mockSerializer.writeTo(EasyMock.isA(HttpCacheEntry.class), EasyMock
-                .isA(OutputStream.class));
-        EasyMock.expect(
-                mockMemcachedClient.set(EasyMock.eq(url), EasyMock.eq(0),
-                        EasyMock.aryEq(new byte[0]))).andReturn(null);
+        byte[] serialized = HttpTestUtils.getRandomBytes(128);
+
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, value))
+            .andReturn(mockMemcachedCacheEntry);
+        EasyMock.expect(mockMemcachedCacheEntry.toByteArray())
+            .andReturn(serialized);
+        EasyMock.expect(mockKeyHashingScheme.hash(url))
+            .andReturn(key);
+        EasyMock.expect(mockMemcachedClient.set(key, 0, serialized))
+            .andReturn(null);
+
+        replayMocks();
+        impl.putEntry(url, value);
+        verifyMocks();
+    }
+    
+    @Test
+    public void testCachePutFailsSilentlyWhenWeCannotHashAKey() throws IOException {
+        final String url = "foo";
+        final HttpCacheEntry value = HttpTestUtils.makeCacheEntry();
+        byte[] serialized = HttpTestUtils.getRandomBytes(128);
+
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, value))
+            .andReturn(mockMemcachedCacheEntry).times(0,1);
+        EasyMock.expect(mockMemcachedCacheEntry.toByteArray())
+            .andReturn(serialized).times(0,1);
+        EasyMock.expect(mockKeyHashingScheme.hash(url))
+            .andThrow(new MemcachedKeyHashingException(new Exception()));
+
         replayMocks();
         impl.putEntry(url, value);
         verifyMocks();
     }
+    
+    public void testThrowsIOExceptionWhenMemcachedPutTimesOut() throws IOException {
+        final String url = "foo";
+        final String key = "key";
+        final HttpCacheEntry value = HttpTestUtils.makeCacheEntry();
+        byte[] serialized = HttpTestUtils.getRandomBytes(128);
+
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, value))
+            .andReturn(mockMemcachedCacheEntry);
+        EasyMock.expect(mockMemcachedCacheEntry.toByteArray())
+            .andReturn(serialized);
+        EasyMock.expect(mockKeyHashingScheme.hash(url))
+            .andReturn(key);
+        EasyMock.expect(mockMemcachedClient.set(key, 0, serialized))
+            .andThrow(new OperationTimeoutException("timed out"));
+
+        replayMocks();
+        try {
+            impl.putEntry(url, value);
+            fail("should have thrown exception");
+        } catch (IOException expected) {
+        }
+        verifyMocks();
+    }
 
     @Test
-    public void testCacheGet() throws UnsupportedEncodingException,
+    public void testCachePutThrowsIOExceptionIfCannotSerializeEntry() throws IOException {
+        final String url = "foo";
+        final String key = "key";
+        final HttpCacheEntry value = HttpTestUtils.makeCacheEntry();
+
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, value))
+            .andReturn(mockMemcachedCacheEntry);
+        EasyMock.expect(mockMemcachedCacheEntry.toByteArray())
+            .andThrow(new MemcachedSerializationException(new Exception()));
+        EasyMock.expect(mockKeyHashingScheme.hash(url))
+            .andReturn(key).times(0,1);
+
+        replayMocks();
+        try {
+            impl.putEntry(url, value);
+            fail("should have thrown exception");
+        } catch (IOException expected) {
+            
+        }
+        verifyMocks();
+    }
+    
+    @Test
+    public void testSuccessfulCacheGet() throws UnsupportedEncodingException,
             IOException {
         final String url = "foo";
+        final String key = "key";
+        byte[] serialized = HttpTestUtils.getRandomBytes(128);
         final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry();
-        EasyMock.expect(mockMemcachedClient.get(url)).andReturn(new byte[] {});
-        EasyMock.expect(
-                mockSerializer.readFrom(EasyMock.isA(InputStream.class)))
-                .andReturn(cacheEntry);
+        
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key);
+        EasyMock.expect(mockMemcachedClient.get(key)).andReturn(serialized);
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getUnsetCacheEntry())
+            .andReturn(mockMemcachedCacheEntry);
+        mockMemcachedCacheEntry.set(serialized);
+        EasyMock.expect(mockMemcachedCacheEntry.getStorageKey()).andReturn(url);
+        EasyMock.expect(mockMemcachedCacheEntry.getHttpCacheEntry()).andReturn(cacheEntry);
+        
         replayMocks();
         HttpCacheEntry resultingEntry = impl.getEntry(url);
         verifyMocks();
         assertSame(cacheEntry, resultingEntry);
     }
-
+    
     @Test
-    public void testCacheGetNullEntry() throws IOException {
+    public void testTreatsNoneByteArrayFromMemcachedAsCacheMiss() throws UnsupportedEncodingException,
+            IOException {
         final String url = "foo";
-
-        EasyMock.expect(mockMemcachedClient.get(url)).andReturn(null);
-
+        final String key = "key";
+        
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key);
+        EasyMock.expect(mockMemcachedClient.get(key)).andReturn(new Object());
+        
+        replayMocks();
+        HttpCacheEntry resultingEntry = impl.getEntry(url);
+        verifyMocks();
+        assertNull(resultingEntry);
+    }
+    
+    @Test
+    public void testTreatsNullFromMemcachedAsCacheMiss() throws UnsupportedEncodingException,
+            IOException {
+        final String url = "foo";
+        final String key = "key";
+        
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key);
+        EasyMock.expect(mockMemcachedClient.get(key)).andReturn(null);
+        
         replayMocks();
         HttpCacheEntry resultingEntry = impl.getEntry(url);
         verifyMocks();
-
         assertNull(resultingEntry);
     }
+    
+    @Test
+    public void testTreatsAsCacheMissIfCannotReconstituteEntry() throws UnsupportedEncodingException,
+            IOException {
+        final String url = "foo";
+        final String key = "key";
+        byte[] serialized = HttpTestUtils.getRandomBytes(128);
+        
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key);
+        EasyMock.expect(mockMemcachedClient.get(key)).andReturn(serialized);
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getUnsetCacheEntry())
+            .andReturn(mockMemcachedCacheEntry);
+        mockMemcachedCacheEntry.set(serialized);
+        EasyMock.expectLastCall().andThrow(new MemcachedSerializationException(new Exception()));
+        
+        replayMocks();
+        assertNull(impl.getEntry(url));
+        verifyMocks();
+    }
+
+    @Test
+    public void testTreatsAsCacheMissIfCantHashStorageKey() throws UnsupportedEncodingException,
+            IOException {
+        final String url = "foo";
+        
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andThrow(new MemcachedKeyHashingException(new Exception()));
+        
+        replayMocks();
+        assertNull(impl.getEntry(url));
+        verifyMocks();
+    }
+
+    @Test
+    public void testThrowsIOExceptionIfMemcachedTimesOutOnGet() throws UnsupportedEncodingException,
+            IOException {
+        final String url = "foo";
+        final String key = "key";
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key);
+        EasyMock.expect(mockMemcachedClient.get(key))
+            .andThrow(new OperationTimeoutException(""));
+        
+        replayMocks();
+        try {
+            impl.getEntry(url);
+            fail("should have thrown exception");
+        } catch (IOException expected) {
+        }
+        verifyMocks();
+    }
 
     @Test
     public void testCacheRemove() throws IOException {
         final String url = "foo";
-        EasyMock.expect(mockMemcachedClient.delete(url)).andReturn(null);
+        final String key = "key";
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key);
+        EasyMock.expect(mockMemcachedClient.delete(key)).andReturn(null);
         replayMocks();
         impl.removeEntry(url);
         verifyMocks();
     }
+    
+    @Test
+    public void testCacheRemoveHandlesKeyHashingFailure() throws IOException {
+        final String url = "foo";
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(null);
+        replayMocks();
+        impl.removeEntry(url);
+        verifyMocks();
+    }
+
+    @Test
+    public void testCacheRemoveThrowsIOExceptionOnMemcachedTimeout() throws IOException {
+        final String url = "foo";
+        final String key = "key";
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key);
+        EasyMock.expect(mockMemcachedClient.delete(key))
+            .andThrow(new OperationTimeoutException(""));
+        
+        replayMocks();
+        try {
+            impl.removeEntry(url);
+            fail("should have thrown exception");
+        } catch (IOException expected) {
+        }
+        verifyMocks();
+    }
 
     @Test
-    public void testCacheUpdateNullEntry() throws IOException,
+    public void testCacheUpdateCanUpdateNullEntry() throws IOException,
             HttpCacheUpdateException {
         final String url = "foo";
+        final String key = "key";
         final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
+        final byte[] serialized = HttpTestUtils.getRandomBytes(128);
 
         HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
             public HttpCacheEntry update(HttpCacheEntry old) {
@@ -137,15 +330,51 @@ public class TestMemcachedHttpCacheStorage extends TestCase {
         };
 
         // get empty old entry
-        EasyMock.expect(mockMemcachedClient.gets(url)).andReturn(null);
-        // EasyMock.expect(mockCache.get(key)).andReturn(null);
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key).anyTimes();
+        EasyMock.expect(mockMemcachedClient.gets(key)).andReturn(null);
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, updatedValue))
+            .andReturn(mockMemcachedCacheEntry);
+        EasyMock.expect(mockMemcachedCacheEntry.toByteArray()).andReturn(serialized);
+        EasyMock.expect(
+                mockMemcachedClient.set(EasyMock.eq(key), EasyMock.eq(0),
+                        EasyMock.aryEq(serialized))).andReturn(null);
+
+        replayMocks();
+        impl.updateEntry(url, callback);
+        verifyMocks();
+    }
+    
+    @Test
+    public void testCacheUpdateOverwritesNonMatchingHashCollision() throws IOException,
+            HttpCacheUpdateException {
+        final String url = "foo";
+        final String key = "key";
+        final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
+        final byte[] oldBytes = HttpTestUtils.getRandomBytes(128);
+        final CASValue<Object> casValue = new CASValue<Object>(-1, oldBytes);
+        final byte[] newBytes = HttpTestUtils.getRandomBytes(128);
 
-        // put new entry
-        mockSerializer.writeTo(EasyMock.same(updatedValue), EasyMock
-                .isA(OutputStream.class));
+        HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
+            public HttpCacheEntry update(HttpCacheEntry old) {
+                assertNull(old);
+                return updatedValue;
+            }
+        };
+
+        // get empty old entry
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key).anyTimes();
+        EasyMock.expect(mockMemcachedClient.gets(key)).andReturn(casValue);
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getUnsetCacheEntry())
+            .andReturn(mockMemcachedCacheEntry);
+        mockMemcachedCacheEntry.set(oldBytes);
+        EasyMock.expect(mockMemcachedCacheEntry.getStorageKey()).andReturn("not" + url).anyTimes();
+        
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, updatedValue))
+            .andReturn(mockMemcachedCacheEntry2);
+        EasyMock.expect(mockMemcachedCacheEntry2.toByteArray()).andReturn(newBytes);
         EasyMock.expect(
-                mockMemcachedClient.set(EasyMock.eq(url), EasyMock.eq(0),
-                        EasyMock.aryEq(new byte[0]))).andReturn(null);
+                mockMemcachedClient.set(EasyMock.eq(key), EasyMock.eq(0),
+                        EasyMock.aryEq(newBytes))).andReturn(null);
 
         replayMocks();
         impl.updateEntry(url, callback);
@@ -153,115 +382,200 @@ public class TestMemcachedHttpCacheStorage extends TestCase {
     }
 
     @Test
-    public void testCacheUpdate() throws IOException, HttpCacheUpdateException {
+    public void testCacheUpdateCanUpdateExistingEntry() throws IOException,
+            HttpCacheUpdateException {
         final String url = "foo";
+        final String key = "key";
         final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry();
         final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
-
-        CASValue<Object> v = new CASValue<Object>(1234, new byte[] {});
+        final byte[] oldBytes = HttpTestUtils.getRandomBytes(128);
+        CASValue<Object> casValue = new CASValue<Object>(1, oldBytes);
+        final byte[] newBytes = HttpTestUtils.getRandomBytes(128);
+        
 
         HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
             public HttpCacheEntry update(HttpCacheEntry old) {
-                assertEquals(existingValue, old);
+                assertSame(existingValue, old);
                 return updatedValue;
             }
         };
 
-        // get existing old entry
-        EasyMock.expect(mockMemcachedClient.gets(url)).andReturn(v);
-        EasyMock.expect(
-                mockSerializer.readFrom(EasyMock.isA(InputStream.class)))
-                .andReturn(existingValue);
+        // get empty old entry
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key).anyTimes();
+        EasyMock.expect(mockMemcachedClient.gets(key)).andReturn(casValue);
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getUnsetCacheEntry())
+            .andReturn(mockMemcachedCacheEntry);
+        mockMemcachedCacheEntry.set(oldBytes);
+        EasyMock.expect(mockMemcachedCacheEntry.getStorageKey()).andReturn(url);
+        EasyMock.expect(mockMemcachedCacheEntry.getHttpCacheEntry()).andReturn(existingValue);
+        
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, updatedValue))
+            .andReturn(mockMemcachedCacheEntry2);
+        EasyMock.expect(mockMemcachedCacheEntry2.toByteArray()).andReturn(newBytes);
 
-        // update
         EasyMock.expect(
-                mockMemcachedClient.cas(EasyMock.eq(url), EasyMock.eq(v
-                        .getCas()), EasyMock.aryEq(new byte[0]))).andReturn(
-                CASResponse.OK);
-        mockSerializer.writeTo(EasyMock.same(updatedValue), EasyMock
-                .isA(OutputStream.class));
+                mockMemcachedClient.cas(EasyMock.eq(key), EasyMock.eq(casValue.getCas()),
+                        EasyMock.aryEq(newBytes))).andReturn(CASResponse.OK);
 
         replayMocks();
         impl.updateEntry(url, callback);
         verifyMocks();
     }
-
+    
     @Test
-    public void testSingleCacheUpdateRetry() throws IOException,
+    public void testCacheUpdateThrowsExceptionsIfCASFailsEnoughTimes() throws IOException,
             HttpCacheUpdateException {
         final String url = "foo";
+        final String key = "key";
         final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry();
         final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
-        CASValue<Object> v = new CASValue<Object>(1234, new byte[] {});
+        final byte[] oldBytes = HttpTestUtils.getRandomBytes(128);
+        CASValue<Object> casValue = new CASValue<Object>(1, oldBytes);
+        final byte[] newBytes = HttpTestUtils.getRandomBytes(128);
+        
+        CacheConfig config = new CacheConfig();
+        config.setMaxUpdateRetries(0);
+        impl = new MemcachedHttpCacheStorage(mockMemcachedClient, config,
+                mockMemcachedCacheEntryFactory, mockKeyHashingScheme);
 
         HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
             public HttpCacheEntry update(HttpCacheEntry old) {
-                assertEquals(existingValue, old);
+                assertSame(existingValue, old);
                 return updatedValue;
             }
         };
-        // get existing old entry, will happen twice
-        EasyMock.expect(mockMemcachedClient.gets(url)).andReturn(v).times(2);
-        EasyMock.expect(
-                mockSerializer.readFrom(EasyMock.isA(InputStream.class)))
-                .andReturn(existingValue).times(2);
 
-        // update but fail
-        mockSerializer.writeTo(EasyMock.same(updatedValue), EasyMock
-                .isA(OutputStream.class));
-        EasyMock.expectLastCall().times(2);
-        EasyMock.expect(
-                mockMemcachedClient.cas(EasyMock.eq(url), EasyMock.eq(v
-                        .getCas()), EasyMock.aryEq(new byte[0]))).andReturn(
-                CASResponse.NOT_FOUND);
+        // get empty old entry
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key).anyTimes();
+        EasyMock.expect(mockMemcachedClient.gets(key)).andReturn(casValue);
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getUnsetCacheEntry())
+            .andReturn(mockMemcachedCacheEntry);
+        mockMemcachedCacheEntry.set(oldBytes);
+        EasyMock.expect(mockMemcachedCacheEntry.getStorageKey()).andReturn(url);
+        EasyMock.expect(mockMemcachedCacheEntry.getHttpCacheEntry()).andReturn(existingValue);
+        
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, updatedValue))
+            .andReturn(mockMemcachedCacheEntry2);
+        EasyMock.expect(mockMemcachedCacheEntry2.toByteArray()).andReturn(newBytes);
 
-        // update again and succeed
         EasyMock.expect(
-                mockMemcachedClient.cas(EasyMock.eq(url), EasyMock.eq(v
-                        .getCas()), EasyMock.aryEq(new byte[0]))).andReturn(
-                CASResponse.OK);
+                mockMemcachedClient.cas(EasyMock.eq(key), EasyMock.eq(casValue.getCas()),
+                        EasyMock.aryEq(newBytes))).andReturn(CASResponse.EXISTS);
 
         replayMocks();
-        impl.updateEntry(url, callback);
+        try {
+            impl.updateEntry(url, callback);
+            fail("should have thrown exception");
+        } catch (HttpCacheUpdateException expected) {
+        }
         verifyMocks();
     }
 
+    
     @Test
-    public void testCacheUpdateFail() throws IOException {
+    public void testCacheUpdateCanUpdateExistingEntryWithRetry() throws IOException,
+            HttpCacheUpdateException {
         final String url = "foo";
+        final String key = "key";
         final HttpCacheEntry existingValue = HttpTestUtils.makeCacheEntry();
+        final HttpCacheEntry existingValue2 = HttpTestUtils.makeCacheEntry();
         final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
-        CASValue<Object> v = new CASValue<Object>(1234, new byte[] {});
+        final HttpCacheEntry updatedValue2 = HttpTestUtils.makeCacheEntry();
+        final byte[] oldBytes = HttpTestUtils.getRandomBytes(128);
+        final byte[] oldBytes2 = HttpTestUtils.getRandomBytes(128);
+        CASValue<Object> casValue = new CASValue<Object>(1, oldBytes);
+        CASValue<Object> casValue2 = new CASValue<Object>(2, oldBytes2);
+        final byte[] newBytes = HttpTestUtils.getRandomBytes(128);
+        final byte[] newBytes2 = HttpTestUtils.getRandomBytes(128);
 
         HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
             public HttpCacheEntry update(HttpCacheEntry old) {
-                assertEquals(existingValue, old);
-                return updatedValue;
+                if (old == existingValue) return updatedValue;
+                assertSame(existingValue2, old);
+                return updatedValue2;
             }
         };
 
-        // get existing old entry
-        EasyMock.expect(mockMemcachedClient.gets(url)).andReturn(v).times(2);
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key).anyTimes();
+        EasyMock.expect(mockMemcachedClient.gets(key)).andReturn(casValue);
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getUnsetCacheEntry())
+            .andReturn(mockMemcachedCacheEntry);
+        mockMemcachedCacheEntry.set(oldBytes);
+        EasyMock.expect(mockMemcachedCacheEntry.getStorageKey()).andReturn(url);
+        EasyMock.expect(mockMemcachedCacheEntry.getHttpCacheEntry()).andReturn(existingValue);
+        
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, updatedValue))
+            .andReturn(mockMemcachedCacheEntry2);
+        EasyMock.expect(mockMemcachedCacheEntry2.toByteArray()).andReturn(newBytes);
+
         EasyMock.expect(
-                mockSerializer.readFrom(EasyMock.isA(InputStream.class)))
-                .andReturn(existingValue).times(2);
+                mockMemcachedClient.cas(EasyMock.eq(key), EasyMock.eq(casValue.getCas()),
+                        EasyMock.aryEq(newBytes))).andReturn(CASResponse.EXISTS);
+
+        // take two
+        EasyMock.expect(mockMemcachedClient.gets(key)).andReturn(casValue2);
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getUnsetCacheEntry())
+            .andReturn(mockMemcachedCacheEntry3);
+        mockMemcachedCacheEntry3.set(oldBytes2);
+        EasyMock.expect(mockMemcachedCacheEntry3.getStorageKey()).andReturn(url);
+        EasyMock.expect(mockMemcachedCacheEntry3.getHttpCacheEntry()).andReturn(existingValue2);
+        
+        EasyMock.expect(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, updatedValue2))
+            .andReturn(mockMemcachedCacheEntry4);
+        EasyMock.expect(mockMemcachedCacheEntry4.toByteArray()).andReturn(newBytes2);
 
-        // update but fail
-        mockSerializer.writeTo(EasyMock.same(updatedValue), EasyMock
-                .isA(OutputStream.class));
-        EasyMock.expectLastCall().times(2);
         EasyMock.expect(
-                mockMemcachedClient.cas(EasyMock.eq(url), EasyMock.eq(v
-                        .getCas()), EasyMock.aryEq(new byte[0]))).andReturn(
-                CASResponse.NOT_FOUND).times(2);
+                mockMemcachedClient.cas(EasyMock.eq(key), EasyMock.eq(casValue2.getCas()),
+                        EasyMock.aryEq(newBytes2))).andReturn(CASResponse.OK);
+        
+        replayMocks();
+        impl.updateEntry(url, callback);
+        verifyMocks();
+    }
+
+
+    @Test
+    public void testUpdateThrowsIOExceptionIfMemcachedTimesOut() throws IOException,
+            HttpCacheUpdateException {
+        final String url = "foo";
+        final String key = "key";
+        final HttpCacheEntry updatedValue = HttpTestUtils.makeCacheEntry();
+
+        HttpCacheUpdateCallback callback = new HttpCacheUpdateCallback() {
+            public HttpCacheEntry update(HttpCacheEntry old) {
+                assertNull(old);
+                return updatedValue;
+            }
+        };
+
+        // get empty old entry
+        EasyMock.expect(mockKeyHashingScheme.hash(url)).andReturn(key).anyTimes();
+        EasyMock.expect(mockMemcachedClient.gets(key))
+            .andThrow(new OperationTimeoutException(""));
 
         replayMocks();
         try {
             impl.updateEntry(url, callback);
-            fail("Expected HttpCacheUpdateException");
-        } catch (HttpCacheUpdateException e) {
+            fail("should have thrown exception");
+        } catch (IOException expected) {
         }
         verifyMocks();
     }
 
+    
+    @Test(expected=HttpCacheUpdateException.class)
+    public void testThrowsExceptionOnUpdateIfCannotHashStorageKey() throws Exception {
+        final String url = "foo";
+
+        EasyMock.expect(mockKeyHashingScheme.hash(url))
+            .andThrow(new MemcachedKeyHashingException(new Exception()));
+
+        replayMocks();
+        try {
+            impl.updateEntry(url, null);
+            fail("should have thrown exception");
+        } catch (HttpCacheUpdateException expected) {
+        }
+        verifyMocks();
+    }
 }
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestPrefixKeyHashingScheme.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestPrefixKeyHashingScheme.java
new file mode 100644
index 0000000..295a224
--- /dev/null
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestPrefixKeyHashingScheme.java
@@ -0,0 +1,57 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class TestPrefixKeyHashingScheme {
+
+    private static final String KEY = "key";
+    private static final String PREFIX = "prefix";
+    private PrefixKeyHashingScheme impl;
+    private KeyHashingScheme scheme;
+
+    @Before
+    public void setUp() {
+        scheme = new KeyHashingScheme() {
+            public String hash(String storageKey) {
+                assertEquals(KEY, storageKey);
+                return "hash";
+            }
+        };
+        impl = new PrefixKeyHashingScheme(PREFIX, scheme);
+    }
+    
+    @Test
+    public void addsPrefixToBackingScheme() {
+        assertEquals("prefixhash", impl.hash(KEY));
+    }
+}
diff --git a/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestSHA256HashingScheme.java b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestSHA256HashingScheme.java
new file mode 100644
index 0000000..d70081f
--- /dev/null
+++ b/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/memcached/TestSHA256HashingScheme.java
@@ -0,0 +1,43 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client.cache.memcached;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+
+public class TestSHA256HashingScheme {
+
+    @Test
+    public void canHash() {
+        SHA256KeyHashingScheme impl = new SHA256KeyHashingScheme();
+        String result = impl.hash("hello, hashing world");
+        assertTrue(result != null && result.length() > 0);
+    }
+    
+}
diff --git a/httpclient-contrib/pom.xml b/httpclient-contrib/pom.xml
index 55598c3..6deb5fc 100644
--- a/httpclient-contrib/pom.xml
+++ b/httpclient-contrib/pom.xml
@@ -30,7 +30,7 @@
   <parent>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpcomponents-client</artifactId>
-    <version>4.1.1</version>
+    <version>4.2.1</version>
   </parent>
   <artifactId>httpclient-contrib</artifactId>
   <name>HttpClient Contrib</name>
diff --git a/httpclient-osgi/pom.xml b/httpclient-osgi/pom.xml
index 6d6f9b1..21ddcac 100644
--- a/httpclient-osgi/pom.xml
+++ b/httpclient-osgi/pom.xml
@@ -30,7 +30,7 @@
   <parent>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpcomponents-client</artifactId>
-    <version>4.1.1</version>
+    <version>4.2.1</version>
   </parent>
   <artifactId>httpclient-osgi</artifactId>
   <name>HttpClient OSGi bundle</name>
@@ -66,6 +66,12 @@
       <version>${project.version}</version>
       <scope>compile</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>fluent-hc</artifactId>
+      <version>${project.version}</version>
+      <scope>compile</scope>
+    </dependency>
   </dependencies>
 
   <properties>
@@ -105,7 +111,7 @@
             org.apache.http.impl.conn.*;version=${project.version},
             org.apache.http.impl.client.*;version=${project.version}
             </_exportcontents>
-            <Embed-Dependency>*;scope=compile|runtime;inline=false</Embed-Dependency>
+            <Embed-Dependency>*;scope=compile|runtime;inline=true</Embed-Dependency>
             <Import-Package>
             javax.crypto,
             javax.crypto.spec,
@@ -113,15 +119,18 @@
             org.ietf.jgss,
             org.apache.commons.logging;version=${commons-logging.version},
             org.apache.http;version=${httpcore.version},
+            org.apache.http.concurrent;version=${httpcore.version},
             org.apache.http.entity;version=${httpcore.version},
             org.apache.http.io;version=${httpcore.version},
             org.apache.http.message;version=${httpcore.version},
             org.apache.http.params;version=${httpcore.version},
+            org.apache.http.pool;version=${httpcore.version},
             org.apache.http.protocol;version=${httpcore.version},
             org.apache.http.util;version=${httpcore.version},
             org.apache.http.impl;version=${httpcore.version},
             org.apache.http.impl.entity;version=${httpcore.version},
             org.apache.http.impl.io;version=${httpcore.version},
+            org.apache.http.impl.pool;version=${httpcore.version},
             net.sf.ehcache.*;resolution:=optional,
             net.spy.memcached.*;resolution:=optional
             </Import-Package>
@@ -141,15 +150,16 @@
     <finalName>org.apache.httpcomponents.httpclient_${project.version}</finalName>
   </build>
 
-  <reports>
+  <reporting>
     <plugins>
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>clirr-maven-plugin</artifactId>
+        <version>${hc.clirr.version}</version>
         <configuration>
           <skip>true</skip>
         </configuration>
       </plugin>
     </plugins>
-  </reports>
+  </reporting>
 </project>
diff --git a/httpclient/pom.xml b/httpclient/pom.xml
index 4ce1cab..9eef424 100644
--- a/httpclient/pom.xml
+++ b/httpclient/pom.xml
@@ -30,7 +30,7 @@
   <parent>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpcomponents-client</artifactId>
-    <version>4.1.1</version>
+    <version>4.2.1</version>
   </parent>
   <artifactId>httpclient</artifactId>
   <name>HttpClient</name>
@@ -44,31 +44,26 @@
     <dependency>
       <groupId>org.apache.httpcomponents</groupId>
       <artifactId>httpcore</artifactId>
-      <version>${httpcore.version}</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
-      <version>${commons-logging.version}</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>commons-codec</groupId>
       <artifactId>commons-codec</artifactId>
-      <version>${commons-codec.version}</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
-      <version>${junit.version}</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-core</artifactId>
-      <version>${mockito.version}</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
@@ -142,6 +137,7 @@
 
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
+         <version>${hc.javadoc.version}</version>
         <configuration>
           <!-- reduce console output. Can override with -Dquiet=false -->
           <quiet>true</quiet>
@@ -171,10 +167,12 @@
 
       <plugin>
         <artifactId>maven-jxr-plugin</artifactId>
+         <version>${hc.jxr.version}</version>
       </plugin>
 
       <plugin>
         <artifactId>maven-surefire-report-plugin</artifactId>
+         <version>${hc.surefire-report.version}</version>
       </plugin>
 
     </plugins>
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientEvictExpiredConnections.java b/httpclient/src/examples/org/apache/http/examples/client/ClientEvictExpiredConnections.java
index 85cb149..c3197e1 100644
--- a/httpclient/src/examples/org/apache/http/examples/client/ClientEvictExpiredConnections.java
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientEvictExpiredConnections.java
@@ -34,7 +34,7 @@ import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.conn.ClientConnectionManager;
 import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
 import org.apache.http.util.EntityUtils;
 
 /**
@@ -44,7 +44,7 @@ import org.apache.http.util.EntityUtils;
 public class ClientEvictExpiredConnections {
 
     public static void main(String[] args) throws Exception {
-        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager();
+        PoolingClientConnectionManager cm = new PoolingClientConnectionManager();
         cm.setMaxTotal(100);
 
         HttpClient httpclient = new DefaultHttpClient(cm);
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientFormLogin.java b/httpclient/src/examples/org/apache/http/examples/client/ClientFormLogin.java
index aae7778..fedcaca 100644
--- a/httpclient/src/examples/org/apache/http/examples/client/ClientFormLogin.java
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientFormLogin.java
@@ -28,6 +28,8 @@ package org.apache.http.examples.client;
 
 import java.util.ArrayList;
 import java.util.List;
+
+import org.apache.http.Consts;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.NameValuePair;
@@ -37,7 +39,6 @@ import org.apache.http.client.methods.HttpPost;
 import org.apache.http.cookie.Cookie;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.protocol.HTTP;
 import org.apache.http.util.EntityUtils;
 
 /**
@@ -77,7 +78,7 @@ public class ClientFormLogin {
             nvps.add(new BasicNameValuePair("IDToken1", "username"));
             nvps.add(new BasicNameValuePair("IDToken2", "password"));
 
-            httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
+            httpost.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8));
 
             response = httpclient.execute(httpost);
             entity = response.getEntity();
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientGZipContentCompression.java b/httpclient/src/examples/org/apache/http/examples/client/ClientGZipContentCompression.java
index 0f6844e..318d961 100644
--- a/httpclient/src/examples/org/apache/http/examples/client/ClientGZipContentCompression.java
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientGZipContentCompression.java
@@ -28,8 +28,6 @@
 package org.apache.http.examples.client;
 
 import java.io.IOException;
-import java.io.InputStream;
-import java.util.zip.GZIPInputStream;
 
 import org.apache.http.Header;
 import org.apache.http.HeaderElement;
@@ -39,8 +37,8 @@ import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequestInterceptor;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.client.entity.GzipDecompressingEntity;
 import org.apache.http.client.methods.HttpGet;
-import org.apache.http.entity.HttpEntityWrapper;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.util.EntityUtils;
@@ -81,14 +79,16 @@ public class ClientGZipContentCompression {
                         final HttpResponse response,
                         final HttpContext context) throws HttpException, IOException {
                     HttpEntity entity = response.getEntity();
-                    Header ceheader = entity.getContentEncoding();
-                    if (ceheader != null) {
-                        HeaderElement[] codecs = ceheader.getElements();
-                        for (int i = 0; i < codecs.length; i++) {
-                            if (codecs[i].getName().equalsIgnoreCase("gzip")) {
-                                response.setEntity(
-                                        new GzipDecompressingEntity(response.getEntity()));
-                                return;
+                    if (entity != null) {
+                        Header ceheader = entity.getContentEncoding();
+                        if (ceheader != null) {
+                            HeaderElement[] codecs = ceheader.getElements();
+                            for (int i = 0; i < codecs.length; i++) {
+                                if (codecs[i].getName().equalsIgnoreCase("gzip")) {
+                                    response.setEntity(
+                                            new GzipDecompressingEntity(response.getEntity()));
+                                    return;
+                                }
                             }
                         }
                     }
@@ -125,29 +125,5 @@ public class ClientGZipContentCompression {
         }
     }
 
-    static class GzipDecompressingEntity extends HttpEntityWrapper {
-
-        public GzipDecompressingEntity(final HttpEntity entity) {
-            super(entity);
-        }
-
-        @Override
-        public InputStream getContent()
-            throws IOException, IllegalStateException {
-
-            // the wrapped entity's getContent() decides about repeatability
-            InputStream wrappedin = wrappedEntity.getContent();
-
-            return new GZIPInputStream(wrappedin);
-        }
-
-        @Override
-        public long getContentLength() {
-            // length of ungzipped content is not known
-            return -1;
-        }
-
-    }
-
 }
 
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientInteractiveAuthentication.java b/httpclient/src/examples/org/apache/http/examples/client/ClientInteractiveAuthentication.java
index 5294e26..5e6d4c1 100644
--- a/httpclient/src/examples/org/apache/http/examples/client/ClientInteractiveAuthentication.java
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientInteractiveAuthentication.java
@@ -29,8 +29,10 @@ import java.io.BufferedReader;
 import java.io.InputStreamReader;
 
 import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpStatus;
+import org.apache.http.auth.AuthScheme;
 import org.apache.http.auth.AuthScope;
 import org.apache.http.auth.AuthState;
 import org.apache.http.auth.Credentials;
@@ -39,6 +41,7 @@ import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.protocol.ClientContext;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.ExecutionContext;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.util.EntityUtils;
 
@@ -71,22 +74,23 @@ public class ClientInteractiveAuthentication {
                 int sc = response.getStatusLine().getStatusCode();
 
                 AuthState authState = null;
+                HttpHost authhost = null;
                 if (sc == HttpStatus.SC_UNAUTHORIZED) {
                     // Target host authentication required
                     authState = (AuthState) localContext.getAttribute(ClientContext.TARGET_AUTH_STATE);
+                    authhost = (HttpHost) localContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
                 }
                 if (sc == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) {
                     // Proxy authentication required
                     authState = (AuthState) localContext.getAttribute(ClientContext.PROXY_AUTH_STATE);
+                    authhost = (HttpHost) localContext.getAttribute(ExecutionContext.HTTP_PROXY_HOST);
                 }
 
                 if (authState != null) {
                     System.out.println("----------------------------------------");
-                    AuthScope authScope = authState.getAuthScope();
-                    System.out.println("Please provide credentials");
-                    System.out.println(" Host: " + authScope.getHost() + ":" + authScope.getPort());
-                    System.out.println(" Realm: " + authScope.getRealm());
-
+                    AuthScheme authscheme = authState.getAuthScheme();
+                    System.out.println("Please provide credentials for " +
+                            authscheme.getRealm() + "@" + authhost.toHostString());
 
                     BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
 
@@ -97,7 +101,7 @@ public class ClientInteractiveAuthentication {
 
                     if (user != null && user.length() > 0) {
                         Credentials creds = new UsernamePasswordCredentials(user, password);
-                        httpclient.getCredentialsProvider().setCredentials(authScope, creds);
+                        httpclient.getCredentialsProvider().setCredentials(new AuthScope(authhost), creds);
                         trying = true;
                     } else {
                         trying = false;
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientKerberosAuthentication.java b/httpclient/src/examples/org/apache/http/examples/client/ClientKerberosAuthentication.java
index 4ce0820..08737c5 100644
--- a/httpclient/src/examples/org/apache/http/examples/client/ClientKerberosAuthentication.java
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientKerberosAuthentication.java
@@ -34,12 +34,12 @@ import org.apache.http.auth.Credentials;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.client.params.AuthPolicy;
-import org.apache.http.impl.auth.NegotiateSchemeFactory;
+import org.apache.http.impl.auth.SPNegoSchemeFactory;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.util.EntityUtils;
 
 /**
- * Kerberos auth example.
+ * SPNEGO (Kerberos) auth example.
  *
  * <p><b>Information</b></p>
  * <p>For the best compatibility use Java >= 1.6 as it supports SPNEGO authentication more
@@ -127,11 +127,7 @@ public class ClientKerberosAuthentication {
 
         DefaultHttpClient httpclient = new DefaultHttpClient();
         try {
-            NegotiateSchemeFactory nsf = new NegotiateSchemeFactory();
-//            nsf.setStripPort(false);
-//            nsf.setSpengoGenerator(new BouncySpnegoTokenGenerator());
-
-            httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);
+            httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, new SPNegoSchemeFactory());
 
             Credentials use_jaas_creds = new Credentials() {
 
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ClientMultiThreadedExecution.java b/httpclient/src/examples/org/apache/http/examples/client/ClientMultiThreadedExecution.java
index db6cd70..bc8751f 100644
--- a/httpclient/src/examples/org/apache/http/examples/client/ClientMultiThreadedExecution.java
+++ b/httpclient/src/examples/org/apache/http/examples/client/ClientMultiThreadedExecution.java
@@ -31,7 +31,7 @@ import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
 import org.apache.http.protocol.BasicHttpContext;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.util.EntityUtils;
@@ -46,7 +46,7 @@ public class ClientMultiThreadedExecution {
         // Create an HttpClient with the ThreadSafeClientConnManager.
         // This connection manager must be used if more than one thread will
         // be using the HttpClient.
-        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager();
+        PoolingClientConnectionManager cm = new PoolingClientConnectionManager();
         cm.setMaxTotal(100);
 
         HttpClient httpclient = new DefaultHttpClient(cm);
diff --git a/httpclient/src/examples/org/apache/http/examples/client/ProxyTunnelDemo.java b/httpclient/src/examples/org/apache/http/examples/client/ProxyTunnelDemo.java
new file mode 100644
index 0000000..3fd6464
--- /dev/null
+++ b/httpclient/src/examples/org/apache/http/examples/client/ProxyTunnelDemo.java
@@ -0,0 +1,73 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.examples.client;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.Socket;
+
+import org.apache.http.HttpHost;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.impl.client.ProxyClient;
+import org.apache.http.protocol.HTTP;
+
+/**
+ * Example code for using {@link ProxyClient} in order to establish a tunnel through an HTTP proxy.
+ */
+public class ProxyTunnelDemo {
+
+    public final static void main(String[] args) throws Exception {
+
+        ProxyClient proxyClient = new ProxyClient();
+        HttpHost target = new HttpHost("www.yahoo.com", 80);
+        HttpHost proxy = new HttpHost("localhost", 8888);
+        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("user", "pwd");
+        Socket socket = proxyClient.tunnel(proxy, target, credentials);
+        try {
+            Writer out = new OutputStreamWriter(socket.getOutputStream(), HTTP.DEF_CONTENT_CHARSET);
+            out.write("GET / HTTP/1.1\r\n");
+            out.write("Host: " + target.toHostString() + "\r\n");
+            out.write("Agent: whatever\r\n");
+            out.write("Connection: close\r\n");
+            out.write("\r\n");
+            out.flush();
+            BufferedReader in = new BufferedReader(
+                    new InputStreamReader(socket.getInputStream(), HTTP.DEF_CONTENT_CHARSET));
+            String line = null;
+            while ((line = in.readLine()) != null) {
+                System.out.println(line);
+            }
+        } finally {
+            socket.close();
+        }
+    }
+
+}
+
diff --git a/httpclient/src/examples/org/apache/http/examples/client/QuickStart.java b/httpclient/src/examples/org/apache/http/examples/client/QuickStart.java
new file mode 100644
index 0000000..d4e7ac7
--- /dev/null
+++ b/httpclient/src/examples/org/apache/http/examples/client/QuickStart.java
@@ -0,0 +1,84 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.examples.client;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+
+public class QuickStart {
+
+    public static void main(String[] args) throws Exception {
+        DefaultHttpClient httpclient = new DefaultHttpClient();
+        HttpGet httpGet = new HttpGet("http://targethost/homepage");
+
+        HttpResponse response1 = httpclient.execute(httpGet);
+
+        // The underlying HTTP connection is still held by the response object 
+        // to allow the response content to be streamed directly from the network socket. 
+        // In order to ensure correct deallocation of system resources 
+        // the user MUST either fully consume the response content  or abort request 
+        // execution by calling HttpGet#releaseConnection().
+
+        try {
+            System.out.println(response1.getStatusLine());
+            HttpEntity entity1 = response1.getEntity();
+            // do something useful with the response body
+            // and ensure it is fully consumed
+            EntityUtils.consume(entity1);
+        } finally {
+            httpGet.releaseConnection();
+        }
+
+        HttpPost httpPost = new HttpPost("http://targethost/login");
+        List <NameValuePair> nvps = new ArrayList <NameValuePair>();
+        nvps.add(new BasicNameValuePair("username", "vip"));
+        nvps.add(new BasicNameValuePair("password", "secret"));
+        httpPost.setEntity(new UrlEncodedFormEntity(nvps));
+        HttpResponse response2 = httpclient.execute(httpPost);
+
+        try {
+            System.out.println(response2.getStatusLine());
+            HttpEntity entity2 = response2.getEntity();
+            // do something useful with the response body
+            // and ensure it is fully consumed
+            EntityUtils.consume(entity2);
+        } finally {
+            httpPost.releaseConnection();
+        }
+    }
+
+}
diff --git a/httpclient/src/examples/org/apache/http/examples/conn/ManagerConnectDirect.java b/httpclient/src/examples/org/apache/http/examples/conn/ManagerConnectDirect.java
deleted file mode 100644
index c38a5ee..0000000
--- a/httpclient/src/examples/org/apache/http/examples/conn/ManagerConnectDirect.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.examples.conn;
-
-import org.apache.http.Header;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpVersion;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.conn.scheme.SchemeSocketFactory;
-import org.apache.http.conn.ClientConnectionRequest;
-import org.apache.http.conn.ManagedClientConnection;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.message.BasicHttpRequest;
-import org.apache.http.params.HttpParams;
-import org.apache.http.params.HttpProtocolParams;
-import org.apache.http.params.SyncBasicHttpParams;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.protocol.BasicHttpContext;
-
-/**
- * How to open a direct connection using
- * {@link ClientConnectionManager ClientConnectionManager}.
- * This exemplifies the <i>opening</i> of the connection only.
- * The subsequent message exchange in this example should not
- * be used as a template.
- *
- * @since 4.0
- */
-public class ManagerConnectDirect {
-
-    /**
-     * Main entry point to this example.
-     *
-     * @param args      ignored
-     */
-    public final static void main(String[] args) throws Exception {
-
-        HttpHost target = new HttpHost("www.apache.org", 80, "http");
-
-        // Register the "http" protocol scheme, it is required
-        // by the default operator to look up socket factories.
-        SchemeRegistry supportedSchemes = new SchemeRegistry();
-        SchemeSocketFactory sf = PlainSocketFactory.getSocketFactory();
-        supportedSchemes.register(new Scheme("http", 80, sf));
-
-        // Prepare parameters.
-        // Since this example doesn't use the full core framework,
-        // only few parameters are actually required.
-        HttpParams params = new SyncBasicHttpParams();
-        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
-        HttpProtocolParams.setUseExpectContinue(params, false);
-
-        ClientConnectionManager clcm = new ThreadSafeClientConnManager(supportedSchemes);
-
-        HttpRequest req = new BasicHttpRequest("OPTIONS", "*", HttpVersion.HTTP_1_1);
-        req.addHeader("Host", target.getHostName());
-
-        HttpContext ctx = new BasicHttpContext();
-
-        System.out.println("preparing route to " + target);
-        HttpRoute route = new HttpRoute
-            (target, null, supportedSchemes.getScheme(target).isLayered());
-
-        System.out.println("requesting connection for " + route);
-        ClientConnectionRequest connRequest = clcm.requestConnection(route, null);
-        ManagedClientConnection conn = connRequest.getConnection(0, null);
-        try {
-            System.out.println("opening connection");
-            conn.open(route, ctx, params);
-
-            System.out.println("sending request");
-            conn.sendRequestHeader(req);
-            // there is no request entity
-            conn.flush();
-
-            System.out.println("receiving response header");
-            HttpResponse rsp = conn.receiveResponseHeader();
-
-            System.out.println("----------------------------------------");
-            System.out.println(rsp.getStatusLine());
-            Header[] headers = rsp.getAllHeaders();
-            for (int i=0; i<headers.length; i++) {
-                System.out.println(headers[i]);
-            }
-            System.out.println("----------------------------------------");
-
-            System.out.println("closing connection");
-            conn.close();
-
-        } finally {
-
-            if (conn.isOpen()) {
-                System.out.println("shutting down connection");
-                try {
-                    conn.shutdown();
-                } catch (Exception ex) {
-                    System.out.println("problem during shutdown");
-                    ex.printStackTrace();
-                }
-            }
-
-            System.out.println("releasing connection");
-            clcm.releaseConnection(conn, -1, null);
-        }
-
-    }
-
-}
-
diff --git a/httpclient/src/examples/org/apache/http/examples/conn/ManagerConnectProxy.java b/httpclient/src/examples/org/apache/http/examples/conn/ManagerConnectProxy.java
deleted file mode 100644
index df176d6..0000000
--- a/httpclient/src/examples/org/apache/http/examples/conn/ManagerConnectProxy.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.examples.conn;
-
-import org.apache.http.Header;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpVersion;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.conn.ClientConnectionRequest;
-import org.apache.http.conn.ManagedClientConnection;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.conn.ssl.SSLSocketFactory;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.message.BasicHttpRequest;
-import org.apache.http.params.HttpParams;
-import org.apache.http.params.HttpProtocolParams;
-import org.apache.http.params.SyncBasicHttpParams;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.protocol.BasicHttpContext;
-
-/**
- * How to open a secure connection through a proxy using
- * {@link ClientConnectionManager ClientConnectionManager}.
- * This exemplifies the <i>opening</i> of the connection only.
- * The message exchange, both subsequently and for tunnelling,
- * should not be used as a template.
- *
- * @since 4.0
- */
-public class ManagerConnectProxy {
-
-    /**
-     * Main entry point to this example.
-     *
-     * @param args      ignored
-     */
-    public final static void main(String[] args) throws Exception {
-
-        // make sure to use a proxy that supports CONNECT
-        HttpHost target = new HttpHost("issues.apache.org", 443, "https");
-        HttpHost proxy = new HttpHost("127.0.0.1", 8666, "http");
-
-        // Register the "http" and "https" protocol schemes, they are
-        // required by the default operator to look up socket factories.
-        SchemeRegistry supportedSchemes = new SchemeRegistry();
-        supportedSchemes.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
-        supportedSchemes.register(new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
-
-        // Prepare parameters.
-        // Since this example doesn't use the full core framework,
-        // only few parameters are actually required.
-        HttpParams params = new SyncBasicHttpParams();
-        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
-        HttpProtocolParams.setUseExpectContinue(params, false);
-
-        ClientConnectionManager clcm = new ThreadSafeClientConnManager(supportedSchemes);
-
-        HttpRequest req = new BasicHttpRequest("OPTIONS", "*", HttpVersion.HTTP_1_1);
-        req.addHeader("Host", target.getHostName());
-
-        HttpContext ctx = new BasicHttpContext();
-
-        System.out.println("preparing route to " + target + " via " + proxy);
-        HttpRoute route = new HttpRoute
-            (target, null, proxy,
-             supportedSchemes.getScheme(target).isLayered());
-
-        System.out.println("requesting connection for " + route);
-        ClientConnectionRequest connRequest = clcm.requestConnection(route, null);
-        ManagedClientConnection conn = connRequest.getConnection(0, null);
-        try {
-            System.out.println("opening connection");
-            conn.open(route, ctx, params);
-
-            String authority = target.getHostName() + ":" + target.getPort();
-            HttpRequest connect = new BasicHttpRequest("CONNECT", authority, HttpVersion.HTTP_1_1);
-            connect.addHeader("Host", authority);
-
-            System.out.println("opening tunnel to " + target);
-            conn.sendRequestHeader(connect);
-            // there is no request entity
-            conn.flush();
-
-            System.out.println("receiving confirmation for tunnel");
-            HttpResponse connected = conn.receiveResponseHeader();
-            System.out.println("----------------------------------------");
-            System.out.println(connected.getStatusLine());
-            Header[] headers = connected.getAllHeaders();
-            for (int i = 0; i < headers.length; i++) {
-                System.out.println(headers[i]);
-            }
-            System.out.println("----------------------------------------");
-            int status = connected.getStatusLine().getStatusCode();
-            if ((status < 200) || (status > 299)) {
-                System.out.println("unexpected status code " + status);
-                System.exit(1);
-            }
-            System.out.println("receiving response body (ignored)");
-            conn.receiveResponseEntity(connected);
-
-            conn.tunnelTarget(false, params);
-
-            System.out.println("layering secure connection");
-            conn.layerProtocol(ctx, params);
-
-            // finally we have the secure connection and can send the request
-
-            System.out.println("sending request");
-            conn.sendRequestHeader(req);
-            // there is no request entity
-            conn.flush();
-
-            System.out.println("receiving response header");
-            HttpResponse rsp = conn.receiveResponseHeader();
-
-            System.out.println("----------------------------------------");
-            System.out.println(rsp.getStatusLine());
-            headers = rsp.getAllHeaders();
-            for (int i = 0; i < headers.length; i++) {
-                System.out.println(headers[i]);
-            }
-            System.out.println("----------------------------------------");
-
-            System.out.println("closing connection");
-            conn.close();
-
-        } finally {
-
-            if (conn.isOpen()) {
-                System.out.println("shutting down connection");
-                try {
-                    conn.shutdown();
-                } catch (Exception ex) {
-                    System.out.println("problem during shutdown");
-                    ex.printStackTrace();
-                }
-            }
-
-            System.out.println("releasing connection");
-            clcm.releaseConnection(conn, -1, null);
-        }
-
-    }
-
-}
-
diff --git a/httpclient/src/main/java/org/apache/http/annotation/GuardedBy.java b/httpclient/src/main/java/org/apache/http/annotation/GuardedBy.java
deleted file mode 100644
index 5907d26..0000000
--- a/httpclient/src/main/java/org/apache/http/annotation/GuardedBy.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * ====================================================================
- *
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.http.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * The field or method to which this annotation is applied can only be accessed
- * when holding a particular lock, which may be a built-in (synchronization) lock,
- * or may be an explicit java.util.concurrent.Lock.
- *
- * The argument determines which lock guards the annotated field or method:
- * <ul>
- * <li>
- * <code>this</code> : The intrinsic lock of the object in whose class the field is defined.
- * </li>
- * <li>
- * <code>class-name.this</code> : For inner classes, it may be necessary to disambiguate 'this';
- * the <em>class-name.this</em> designation allows you to specify which 'this' reference is intended
- * </li>
- * <li>
- * <code>itself</code> : For reference fields only; the object to which the field refers.
- * </li>
- * <li>
- * <code>field-name</code> : The lock object is referenced by the (instance or static) field
- * specified by <em>field-name</em>.
- * </li>
- * <li>
- * <code>class-name.field-name</code> : The lock object is reference by the static field specified
- * by <em>class-name.field-name</em>.
- * </li>
- * <li>
- * <code>method-name()</code> : The lock object is returned by calling the named nil-ary method.
- * </li>
- * <li>
- * <code>class-name.class</code> : The Class object for the specified class should be used as the lock object.
- * </li>
- * <p>
- * Based on code developed by Brian Goetz and Tim Peierls and concepts
- * published in 'Java Concurrency in Practice' by Brian Goetz, Tim Peierls,
- * Joshua Bloch, Joseph Bowbeer, David Holmes and Doug Lea.
- */
- at Documented
- at Target({ElementType.FIELD, ElementType.METHOD})
- at Retention(RetentionPolicy.CLASS) // The original version used RUNTIME
-public @interface GuardedBy {
-    String value();
-}
diff --git a/httpclient/src/main/java/org/apache/http/annotation/Immutable.java b/httpclient/src/main/java/org/apache/http/annotation/Immutable.java
deleted file mode 100644
index da201b3..0000000
--- a/httpclient/src/main/java/org/apache/http/annotation/Immutable.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * ====================================================================
- *
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.http.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * The class to which this annotation is applied is immutable.  This means that
- * its state cannot be seen to change by callers, which implies that
- * <ul>
- * <li> all public fields are final, </li>
- * <li> all public final reference fields refer to other immutable objects, and </li>
- * <li> constructors and methods do not publish references to any internal state
- *      which is potentially mutable by the implementation. </li>
- * </ul>
- * Immutable objects may still have internal mutable state for purposes of performance
- * optimization; some state variables may be lazily computed, so long as they are computed
- * from immutable state and that callers cannot tell the difference.
- * <p>
- * Immutable objects are inherently thread-safe; they may be passed between threads or
- * published without synchronization.
- * <p>
- * Based on code developed by Brian Goetz and Tim Peierls and concepts
- * published in 'Java Concurrency in Practice' by Brian Goetz, Tim Peierls,
- * Joshua Bloch, Joseph Bowbeer, David Holmes and Doug Lea.
- */
- at Documented
- at Target(ElementType.TYPE)
- at Retention(RetentionPolicy.CLASS) // The original version used RUNTIME
-public @interface Immutable {
-}
diff --git a/httpclient/src/main/java/org/apache/http/annotation/NotThreadSafe.java b/httpclient/src/main/java/org/apache/http/annotation/NotThreadSafe.java
deleted file mode 100644
index ddc6787..0000000
--- a/httpclient/src/main/java/org/apache/http/annotation/NotThreadSafe.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * ====================================================================
- *
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.http.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * The class to which this annotation is applied is not thread-safe.
- * This annotation primarily exists for clarifying the non-thread-safety of a class
- * that might otherwise be assumed to be thread-safe, despite the fact that it is a bad
- * idea to assume a class is thread-safe without good reason.
- * @see ThreadSafe
- * <p>
- * Based on code developed by Brian Goetz and Tim Peierls and concepts
- * published in 'Java Concurrency in Practice' by Brian Goetz, Tim Peierls,
- * Joshua Bloch, Joseph Bowbeer, David Holmes and Doug Lea.
- */
- at Documented
- at Target(ElementType.TYPE)
- at Retention(RetentionPolicy.CLASS) // The original version used RUNTIME
-public @interface NotThreadSafe {
-}
diff --git a/httpclient/src/main/java/org/apache/http/annotation/ThreadSafe.java b/httpclient/src/main/java/org/apache/http/annotation/ThreadSafe.java
deleted file mode 100644
index 53c91b9..0000000
--- a/httpclient/src/main/java/org/apache/http/annotation/ThreadSafe.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * ====================================================================
- *
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.http.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * The class to which this annotation is applied is thread-safe.  This means that
- * no sequences of accesses (reads and writes to public fields, calls to public methods)
- * may put the object into an invalid state, regardless of the interleaving of those actions
- * by the runtime, and without requiring any additional synchronization or coordination on the
- * part of the caller.
- * @see NotThreadSafe
- * <p>
- * Based on code developed by Brian Goetz and Tim Peierls and concepts
- * published in 'Java Concurrency in Practice' by Brian Goetz, Tim Peierls,
- * Joshua Bloch, Joseph Bowbeer, David Holmes and Doug Lea.
- */
- at Documented
- at Target(ElementType.TYPE)
- at Retention(RetentionPolicy.CLASS) // The original version used RUNTIME
-public @interface ThreadSafe {
-}
diff --git a/httpclient/src/main/java/org/apache/http/auth/AuthOption.java b/httpclient/src/main/java/org/apache/http/auth/AuthOption.java
new file mode 100644
index 0000000..97c0c58
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/auth/AuthOption.java
@@ -0,0 +1,66 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+import org.apache.http.annotation.Immutable;
+
+/**
+ * @since 4.2
+ */
+ at Immutable
+public final class AuthOption {
+
+    private final AuthScheme authScheme;
+    private final Credentials creds;
+
+    public AuthOption(final AuthScheme authScheme, final Credentials creds) {
+        super();
+        if (authScheme == null) {
+            throw new IllegalArgumentException("Auth scheme may not be null");
+        }
+        if (creds == null) {
+            throw new IllegalArgumentException("User credentials may not be null");
+        }
+        this.authScheme = authScheme;
+        this.creds = creds;
+    }
+
+    public AuthScheme getAuthScheme() {
+        return this.authScheme;
+    }
+
+    public Credentials getCredentials() {
+        return this.creds;
+    }
+
+    @Override
+    public String toString() {
+        return this.authScheme.toString();
+    }
+
+}
+
diff --git a/httpclient/src/main/java/org/apache/http/auth/AuthProtocolState.java b/httpclient/src/main/java/org/apache/http/auth/AuthProtocolState.java
new file mode 100644
index 0000000..786f107
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/auth/AuthProtocolState.java
@@ -0,0 +1,33 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.auth;
+
+public enum AuthProtocolState {
+
+    UNCHALLENGED, CHALLENGED, HANDSHAKE, FAILURE, SUCCESS
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java b/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java
index cccb7c4..d782014 100644
--- a/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java
+++ b/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java
@@ -121,7 +121,7 @@ public interface AuthScheme {
      *
      * @return the authorization string
      *
-     * @deprecated Use {@link ContextAwareAuthScheme#authenticate(Credentials, HttpRequest, org.apache.http.protocol.HttpContext)}
+     * @deprecated (4.1)  Use {@link ContextAwareAuthScheme#authenticate(Credentials, HttpRequest, org.apache.http.protocol.HttpContext)}
      */
     @Deprecated
     Header authenticate(Credentials credentials, HttpRequest request)
diff --git a/httpclient/src/main/java/org/apache/http/auth/AuthScope.java b/httpclient/src/main/java/org/apache/http/auth/AuthScope.java
index f14627c..e48f7d8 100644
--- a/httpclient/src/main/java/org/apache/http/auth/AuthScope.java
+++ b/httpclient/src/main/java/org/apache/http/auth/AuthScope.java
@@ -28,6 +28,7 @@ package org.apache.http.auth;
 
 import java.util.Locale;
 
+import org.apache.http.HttpHost;
 import org.apache.http.annotation.Immutable;
 
 import org.apache.http.util.LangUtils;
@@ -109,6 +110,20 @@ public class AuthScope {
         this.scheme = (scheme == null) ? ANY_SCHEME: scheme.toUpperCase(Locale.ENGLISH);
     }
 
+    /**
+     * @since 4.2
+     */
+    public AuthScope(final HttpHost host, final String realm, final String schemeName) {
+        this(host.getHostName(), host.getPort(), realm, schemeName);
+    }
+
+    /**
+     * @since 4.2
+     */
+    public AuthScope(final HttpHost host) {
+        this(host, ANY_REALM, ANY_SCHEME);
+    }
+
     /** Creates a new credentials scope for the given
      * <tt>host</tt>, <tt>port</tt>, <tt>realm</tt>, and any
      * authentication scheme.
diff --git a/httpclient/src/main/java/org/apache/http/auth/AuthState.java b/httpclient/src/main/java/org/apache/http/auth/AuthState.java
index d3d2b2d..925d7f8 100644
--- a/httpclient/src/main/java/org/apache/http/auth/AuthState.java
+++ b/httpclient/src/main/java/org/apache/http/auth/AuthState.java
@@ -26,19 +26,21 @@
 
 package org.apache.http.auth;
 
-import org.apache.http.annotation.NotThreadSafe;
+import java.util.Queue;
 
+import org.apache.http.annotation.NotThreadSafe;
 
 /**
- * This class provides detailed information about the state of the
- * authentication process.
- *
+ * This class provides detailed information about the state of the authentication process.
  *
  * @since 4.0
  */
 @NotThreadSafe
 public class AuthState {
 
+    /** Actual state of authentication protocol */
+    private AuthProtocolState state;
+
     /** Actual authentication scheme */
     private AuthScheme authScheme;
 
@@ -48,97 +50,190 @@ public class AuthState {
     /** Credentials selected for authentication */
     private Credentials credentials;
 
-    /**
-     * Default constructor.
-     *
-     */
+    /** Available auth options */
+    private Queue<AuthOption> authOptions;
+
     public AuthState() {
         super();
+        this.state = AuthProtocolState.UNCHALLENGED;
     }
 
     /**
-     * Invalidates the authentication state by resetting its parameters.
+     * Resets the auth state.
+     *
+     * @since 4.2
      */
-    public void invalidate() {
+    public void reset() {
+        this.state = AuthProtocolState.UNCHALLENGED;
+        this.authOptions = null;
         this.authScheme = null;
         this.authScope = null;
         this.credentials = null;
     }
 
-    public boolean isValid() {
-        return this.authScheme != null;
+    /**
+     * @since 4.2
+     */
+    public AuthProtocolState getState() {
+        return this.state;
     }
 
     /**
-     * Assigns the given {@link AuthScheme authentication scheme}.
+     * @since 4.2
+     */
+    public void setState(final AuthProtocolState state) {
+        this.state = state != null ? state : AuthProtocolState.UNCHALLENGED;
+    }
+
+    /**
+     * Returns actual {@link AuthScheme}. May be null.
+     */
+    public AuthScheme getAuthScheme() {
+        return this.authScheme;
+    }
+
+    /**
+     * Returns actual {@link Credentials}. May be null.
+     */
+    public Credentials getCredentials() {
+        return this.credentials;
+    }
+
+    /**
+     * Updates the auth state with {@link AuthScheme} and {@link Credentials}.
      *
-     * @param authScheme the {@link AuthScheme authentication scheme}
+     * @param authScheme auth scheme. May not be null.
+     * @param credentials user crednetials. May not be null.
+     *
+     * @since 4.2
      */
-    public void setAuthScheme(final AuthScheme authScheme) {
+    public void update(final AuthScheme authScheme, final Credentials credentials) {
         if (authScheme == null) {
-            invalidate();
-            return;
+            throw new IllegalArgumentException("Auth scheme may not be null or empty");
+        }
+        if (credentials == null) {
+            throw new IllegalArgumentException("Credentials may not be null or empty");
         }
         this.authScheme = authScheme;
+        this.credentials = credentials;
+        this.authOptions = null;
     }
 
     /**
-     * Returns the {@link AuthScheme authentication scheme}.
+     * Returns available {@link AuthOption}s. May be null.
      *
-     * @return {@link AuthScheme authentication scheme}
+     * @since 4.2
      */
-    public AuthScheme getAuthScheme() {
-        return this.authScheme;
+    public Queue<AuthOption> getAuthOptions() {
+        return this.authOptions;
+    }
+
+    /**
+     * Returns <code>true</code> if {@link AuthOption}s are available, <code>false</code>
+     * otherwise.
+     *
+     * @since 4.2
+     */
+    public boolean hasAuthOptions() {
+        return this.authOptions != null && !this.authOptions.isEmpty();
     }
 
+    /**
+     * Updates the auth state with a queue of {@link AuthOption}s.
+     *
+     * @param authOptions a queue of auth options. May not be null or empty.
+     *
+     * @since 4.2
+     */
+    public void update(final Queue<AuthOption> authOptions) {
+        if (authOptions == null || authOptions.isEmpty()) {
+            throw new IllegalArgumentException("Queue of auth options may not be null or empty");
+        }
+        this.authOptions = authOptions;
+        this.authScheme = null;
+        this.credentials = null;
+    }
 
     /**
-     * Returns user {@link Credentials} selected for authentication if available
+     * Invalidates the authentication state by resetting its parameters.
      *
-     * @return user credentials if available, <code>null</code otherwise
+     * @deprecated (4.2)  use {@link #reset()}
      */
-    public Credentials getCredentials() {
-        return this.credentials;
+    @Deprecated 
+    public void invalidate() {
+        reset();
     }
 
+    /**
+     * @deprecated (4.2) do not use
+     */
+    @Deprecated
+    public boolean isValid() {
+        return this.authScheme != null;
+    }
+
+    /**
+     * Assigns the given {@link AuthScheme authentication scheme}.
+     *
+     * @param authScheme the {@link AuthScheme authentication scheme}
+     *
+     * @deprecated (4.2)  use {@link #update(AuthScheme, Credentials)}
+     */
+    @Deprecated 
+    public void setAuthScheme(final AuthScheme authScheme) {
+        if (authScheme == null) {
+            reset();
+            return;
+        }
+        this.authScheme = authScheme;
+    }
 
     /**
      * Sets user {@link Credentials} to be used for authentication
      *
      * @param credentials User credentials
+     *
+     * @deprecated (4.2)  use {@link #update(AuthScheme, Credentials)}
      */
+    @Deprecated
     public void setCredentials(final Credentials credentials) {
         this.credentials = credentials;
     }
 
-
     /**
      * Returns actual {@link AuthScope} if available
      *
      * @return actual authentication scope if available, <code>null</code otherwise
+     *
+     * @deprecated (4.2)  do not use.
      */
-     public AuthScope getAuthScope() {
+    @Deprecated
+    public AuthScope getAuthScope() {
         return this.authScope;
-     }
-
-     /**
-      * Sets actual {@link AuthScope}.
-      *
-      * @param authScope Authentication scope
-      */
-     public void setAuthScope(final AuthScope authScope) {
-        this.authScope = authScope;
-     }
+    }
 
+    /**
+     * Sets actual {@link AuthScope}.
+     *
+     * @param authScope Authentication scope
+     *
+     * @deprecated (4.2)  do not use.
+     */
+    @Deprecated
+    public void setAuthScope(final AuthScope authScope) {
+        this.authScope = authScope;
+    }
 
     @Override
     public String toString() {
         StringBuilder buffer = new StringBuilder();
-        buffer.append("auth scope [");
-        buffer.append(this.authScope);
-        buffer.append("]; credentials set [");
-        buffer.append(this.credentials != null ? "true" : "false");
-        buffer.append("]");
+        buffer.append("state:").append(this.state).append(";");
+        if (this.authScheme != null) {
+            buffer.append("auth scheme:").append(this.authScheme.getSchemeName()).append(";");
+        }
+        if (this.credentials != null) {
+            buffer.append("credentials present");
+        }
         return buffer.toString();
     }
 
diff --git a/httpclient/src/main/java/org/apache/http/auth/ChallengeState.java b/httpclient/src/main/java/org/apache/http/auth/ChallengeState.java
new file mode 100644
index 0000000..4d25d61
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/auth/ChallengeState.java
@@ -0,0 +1,37 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.http.auth;
+
+/**
+ * Challenge mode (TARGET or PROXY)
+ *
+ * @since 4.2
+ */
+public enum ChallengeState {
+
+    TARGET, PROXY
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/auth/params/AuthParams.java b/httpclient/src/main/java/org/apache/http/auth/params/AuthParams.java
index 55cf9a2..8e8250a 100644
--- a/httpclient/src/main/java/org/apache/http/auth/params/AuthParams.java
+++ b/httpclient/src/main/java/org/apache/http/auth/params/AuthParams.java
@@ -61,7 +61,7 @@ public final class AuthParams {
         String charset = (String) params.getParameter
             (AuthPNames.CREDENTIAL_CHARSET);
         if (charset == null) {
-            charset = HTTP.DEFAULT_PROTOCOL_CHARSET;
+            charset = HTTP.DEF_PROTOCOL_CHARSET.name();
         }
         return charset;
     }
diff --git a/httpclient/src/main/java/org/apache/http/client/AuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/client/AuthenticationHandler.java
index 2d0da42..60065bd 100644
--- a/httpclient/src/main/java/org/apache/http/client/AuthenticationHandler.java
+++ b/httpclient/src/main/java/org/apache/http/client/AuthenticationHandler.java
@@ -47,7 +47,10 @@ import org.apache.http.protocol.HttpContext;
  * from multiple threads.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link AuthenticationStrategy}
  */
+ at Deprecated
 public interface AuthenticationHandler {
 
     /**
diff --git a/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java b/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java
new file mode 100644
index 0000000..4f4ef96
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java
@@ -0,0 +1,130 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import java.util.Map;
+import java.util.Queue;
+
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthOption;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.protocol.HttpContext;
+
+/**
+/**
+ * A handler for determining if an HTTP response represents an authentication challenge that was
+ * sent back to the client as a result of authentication failure.
+ * <p>
+ * Implementations of this interface must be thread-safe. Access to shared data must be
+ * synchronized as methods of this interface may be executed from multiple threads.
+ *
+ * @since 4.2
+ */
+public interface AuthenticationStrategy {
+
+    /**
+     * Determines if the given HTTP response response represents
+     * an authentication challenge that was sent back as a result
+     * of authentication failure.
+     *
+     * @param authhost authentication host.
+     * @param response HTTP response.
+     * @param context HTTP context.
+     * @return <code>true</code> if user authentication is required,
+     *   <code>false</code> otherwise.
+     */
+    boolean isAuthenticationRequested(
+            HttpHost authhost,
+            HttpResponse response,
+            HttpContext context);
+
+    /**
+     * Extracts from the given HTTP response a collection of authentication
+     * challenges, each of which represents an authentication scheme supported
+     * by the authentication host.
+     *
+     * @param authhost authentication host.
+     * @param response HTTP response.
+     * @param context HTTP context.
+     * @return a collection of challenges keyed by names of corresponding
+     * authentication schemes.
+     * @throws MalformedChallengeException if one of the authentication
+     *  challenges is not valid or malformed.
+     */
+    Map<String, Header> getChallenges(
+            HttpHost authhost,
+            HttpResponse response,
+            HttpContext context) throws MalformedChallengeException;
+
+    /**
+     * Selects one authentication challenge out of all available and
+     * creates and generates {@link AuthOption} instance capable of
+     * processing that challenge.
+     *
+     * @param challenges collection of challenges.
+     * @param authhost authentication host.
+     * @param response HTTP response.
+     * @param context HTTP context.
+     * @return authentication auth schemes that can be used for authentication. Can be empty.
+     * @throws MalformedChallengeException if one of the authentication
+     *  challenges is not valid or malformed.
+     */
+    Queue<AuthOption> select(
+            Map<String, Header> challenges,
+            HttpHost authhost,
+            HttpResponse response,
+            HttpContext context) throws MalformedChallengeException;
+
+    /**
+     * Callback invoked in case of successful authentication.
+     *
+     * @param authhost authentication host.
+     * @param authScheme authentication scheme used.
+     * @param context HTTP context.
+     */
+    void authSucceeded(
+            HttpHost authhost,
+            AuthScheme authScheme,
+            HttpContext context);
+
+    /**
+     * Callback invoked in case of unsuccessful authentication.
+     *
+     * @param authhost authentication host.
+     * @param authScheme authentication scheme used.
+     * @param context HTTP context.
+     */
+    void authFailed(
+            HttpHost authhost,
+            AuthScheme authScheme,
+            HttpContext context);
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/BackoffManager.java b/httpclient/src/main/java/org/apache/http/client/BackoffManager.java
new file mode 100644
index 0000000..f13330d
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/client/BackoffManager.java
@@ -0,0 +1,53 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.client;
+
+import org.apache.http.conn.routing.HttpRoute;
+
+/**
+ * Represents a controller that dynamically adjusts the size
+ * of an available connection pool based on feedback from
+ * using the connections.
+ *
+ * @since 4.2
+ *
+ */
+public interface BackoffManager {
+
+    /**
+     * Called when we have decided that the result of
+     * using a connection should be interpreted as a
+     * backoff signal.
+     */
+    public void backOff(HttpRoute route);
+
+    /**
+     * Called when we have determined that the result of
+     * using a connection has succeeded and that we may
+     * probe for more connections.
+     */
+    public void probe(HttpRoute route);
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/ConnectionBackoffStrategy.java b/httpclient/src/main/java/org/apache/http/client/ConnectionBackoffStrategy.java
new file mode 100644
index 0000000..3154817
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/client/ConnectionBackoffStrategy.java
@@ -0,0 +1,63 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.client;
+
+import org.apache.http.HttpResponse;
+
+/**
+ * When managing a dynamic number of connections for a given route, this
+ * strategy assesses whether a given request execution outcome should
+ * result in a backoff signal or not, based on either examining the
+ * <code>Throwable</code> that resulted or by examining the resulting
+ * response (e.g. for its status code).
+ *
+ * @since 4.2
+ *
+ */
+public interface ConnectionBackoffStrategy {
+
+    /**
+     * Determines whether seeing the given <code>Throwable</code> as
+     * a result of request execution should result in a backoff
+     * signal.
+     * @param t the <code>Throwable</code> that happened
+     * @return <code>true</code> if a backoff signal should be
+     *   given
+     */
+    boolean shouldBackoff(Throwable t);
+
+    /**
+     * Determines whether receiving the given {@link HttpResponse} as
+     * a result of request execution should result in a backoff
+     * signal. Implementations MUST restrict themselves to examining
+     * the response header and MUST NOT consume any of the response
+     * body, if any.
+     * @param resp the <code>HttpResponse</code> that was received
+     * @return <code>true</code> if a backoff signal should be
+     *   given
+     */
+    boolean shouldBackoff(HttpResponse resp);
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/RedirectHandler.java b/httpclient/src/main/java/org/apache/http/client/RedirectHandler.java
index af47638..7bd4bd4 100644
--- a/httpclient/src/main/java/org/apache/http/client/RedirectHandler.java
+++ b/httpclient/src/main/java/org/apache/http/client/RedirectHandler.java
@@ -44,7 +44,7 @@ import org.apache.http.protocol.HttpContext;
  *
  * @since 4.0
  *
- * @deprecated use {@link RedirectStrategy}
+ * @deprecated (4.1)  use {@link RedirectStrategy}
  */
 @Deprecated
 public interface RedirectHandler {
diff --git a/httpclient/src/main/java/org/apache/http/client/RedirectStrategy.java b/httpclient/src/main/java/org/apache/http/client/RedirectStrategy.java
index 572f505..9a82469 100644
--- a/httpclient/src/main/java/org/apache/http/client/RedirectStrategy.java
+++ b/httpclient/src/main/java/org/apache/http/client/RedirectStrategy.java
@@ -78,4 +78,4 @@ public interface RedirectStrategy {
             HttpResponse response,
             HttpContext context) throws ProtocolException;
 
-}
\ No newline at end of file
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/ServiceUnavailableRetryStrategy.java b/httpclient/src/main/java/org/apache/http/client/ServiceUnavailableRetryStrategy.java
new file mode 100644
index 0000000..69c449f
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/client/ServiceUnavailableRetryStrategy.java
@@ -0,0 +1,60 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Strategy interface that allows API users to plug in their own logic to
+ * control whether or not a retry should automatically be done, how many times
+ * it should be retried and so on.
+ *
+ * @since 4.2
+ */
+public interface ServiceUnavailableRetryStrategy {
+
+    /**
+     * Determines if a method should be retried given the response from the target server.
+     *
+     * @param response the response from the target server
+     * @param executionCount the number of times this method has been
+     * unsuccessfully executed
+     * @param context the context for the request execution
+
+     * @return <code>true</code> if the method should be retried, <code>false</code>
+     * otherwise
+     */
+    boolean retryRequest(HttpResponse response, int executionCount, HttpContext context);
+
+    /**
+     * @return The interval between the subsequent auto-retries.
+     */
+    long getRetryInterval();
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/entity/DecompressingEntity.java b/httpclient/src/main/java/org/apache/http/client/entity/DecompressingEntity.java
index ebb7495..06272a6 100644
--- a/httpclient/src/main/java/org/apache/http/client/entity/DecompressingEntity.java
+++ b/httpclient/src/main/java/org/apache/http/client/entity/DecompressingEntity.java
@@ -45,6 +45,12 @@ abstract class DecompressingEntity extends HttpEntityWrapper {
     private static final int BUFFER_SIZE = 1024 * 2;
 
     /**
+     * DecompressingEntities are not repeatable, so they will return the same
+     * InputStream instance when {@link #getContent()} is called.
+     */
+    private InputStream content;
+
+    /**
      * Creates a new {@link DecompressingEntity}.
      *
      * @param wrapped
@@ -54,6 +60,23 @@ abstract class DecompressingEntity extends HttpEntityWrapper {
         super(wrapped);
     }
 
+    abstract InputStream getDecompressingInputStream(final InputStream wrapped) throws IOException;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public InputStream getContent() throws IOException {
+        if (wrappedEntity.isStreaming()) {
+            if (content == null) {
+                content = getDecompressingInputStream(wrappedEntity.getContent());
+            }
+            return content;
+        } else {
+            return getDecompressingInputStream(wrappedEntity.getContent());
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -62,15 +85,17 @@ abstract class DecompressingEntity extends HttpEntityWrapper {
         if (outstream == null) {
             throw new IllegalArgumentException("Output stream may not be null");
         }
-
         InputStream instream = getContent();
+        try {
+            byte[] buffer = new byte[BUFFER_SIZE];
 
-        byte[] buffer = new byte[BUFFER_SIZE];
-
-        int l;
+            int l;
 
-        while ((l = instream.read(buffer)) != -1) {
-            outstream.write(buffer, 0, l);
+            while ((l = instream.read(buffer)) != -1) {
+                outstream.write(buffer, 0, l);
+            }
+        } finally {
+            instream.close();
         }
     }
 
diff --git a/httpclient/src/main/java/org/apache/http/client/entity/DeflateDecompressingEntity.java b/httpclient/src/main/java/org/apache/http/client/entity/DeflateDecompressingEntity.java
index 9cef5eb..bb9cbf8 100644
--- a/httpclient/src/main/java/org/apache/http/client/entity/DeflateDecompressingEntity.java
+++ b/httpclient/src/main/java/org/apache/http/client/entity/DeflateDecompressingEntity.java
@@ -63,12 +63,14 @@ public class DeflateDecompressingEntity extends DecompressingEntity {
     }
 
     /**
-     * {@inheritDoc}
+     * Returns the non-null InputStream that should be returned to by all requests to
+     * {@link #getContent()}.
+     *
+     * @return a non-null InputStream
+     * @throws IOException if there was a problem
      */
     @Override
-    public InputStream getContent() throws IOException {
-        InputStream wrapped = this.wrappedEntity.getContent();
-
+    InputStream getDecompressingInputStream(final InputStream wrapped) throws IOException {
         /*
          * A zlib stream will have a header.
          *
diff --git a/httpclient/src/main/java/org/apache/http/client/entity/GzipDecompressingEntity.java b/httpclient/src/main/java/org/apache/http/client/entity/GzipDecompressingEntity.java
index a1dc7b9..9e22eb4 100644
--- a/httpclient/src/main/java/org/apache/http/client/entity/GzipDecompressingEntity.java
+++ b/httpclient/src/main/java/org/apache/http/client/entity/GzipDecompressingEntity.java
@@ -51,16 +51,9 @@ public class GzipDecompressingEntity extends DecompressingEntity {
         super(entity);
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public InputStream getContent() throws IOException, IllegalStateException {
-
-        // the wrapped entity's getContent() decides about repeatability
-        InputStream wrappedin = wrappedEntity.getContent();
-
-        return new GZIPInputStream(wrappedin);
+    InputStream getDecompressingInputStream(final InputStream wrapped) throws IOException {
+        return new GZIPInputStream(wrapped);
     }
 
     /**
diff --git a/httpclient/src/main/java/org/apache/http/client/entity/UrlEncodedFormEntity.java b/httpclient/src/main/java/org/apache/http/client/entity/UrlEncodedFormEntity.java
index 7b6f086..33e389f 100644
--- a/httpclient/src/main/java/org/apache/http/client/entity/UrlEncodedFormEntity.java
+++ b/httpclient/src/main/java/org/apache/http/client/entity/UrlEncodedFormEntity.java
@@ -27,12 +27,14 @@
 package org.apache.http.client.entity;
 
 import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
 import java.util.List;
 
 import org.apache.http.annotation.NotThreadSafe;
 
 import org.apache.http.NameValuePair;
 import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.entity.ContentType;
 import org.apache.http.entity.StringEntity;
 import org.apache.http.protocol.HTTP;
 
@@ -50,15 +52,32 @@ public class UrlEncodedFormEntity extends StringEntity {
      * of parameters in the specified encoding.
      *
      * @param parameters list of name/value pairs
-     * @param encoding encoding the name/value pairs be encoded with
+     * @param charset encoding the name/value pairs be encoded with
      * @throws UnsupportedEncodingException if the encoding isn't supported
      */
     public UrlEncodedFormEntity (
         final List <? extends NameValuePair> parameters,
-        final String encoding) throws UnsupportedEncodingException {
-        super(URLEncodedUtils.format(parameters, encoding), encoding);
-        setContentType(URLEncodedUtils.CONTENT_TYPE + HTTP.CHARSET_PARAM +
-                (encoding != null ? encoding : HTTP.DEFAULT_CONTENT_CHARSET));
+        final String charset) throws UnsupportedEncodingException {
+        super(URLEncodedUtils.format(parameters,
+                charset != null ? charset : HTTP.DEF_CONTENT_CHARSET.name()),
+                ContentType.create(URLEncodedUtils.CONTENT_TYPE, charset));
+    }
+
+    /**
+     * Constructs a new {@link UrlEncodedFormEntity} with the list
+     * of parameters in the specified encoding.
+     *
+     * @param parameters iterable collection of name/value pairs
+     * @param charset encoding the name/value pairs be encoded with
+     *
+     * @since 4.2
+     */
+    public UrlEncodedFormEntity (
+        final Iterable <? extends NameValuePair> parameters,
+        final Charset charset) {
+        super(URLEncodedUtils.format(parameters,
+                charset != null ? charset : HTTP.DEF_CONTENT_CHARSET),
+                ContentType.create(URLEncodedUtils.CONTENT_TYPE, charset));
     }
 
     /**
@@ -70,7 +89,20 @@ public class UrlEncodedFormEntity extends StringEntity {
      */
     public UrlEncodedFormEntity (
         final List <? extends NameValuePair> parameters) throws UnsupportedEncodingException {
-        this(parameters, HTTP.DEFAULT_CONTENT_CHARSET);
+        this(parameters, (Charset) null);
+    }
+
+    /**
+     * Constructs a new {@link UrlEncodedFormEntity} with the list
+     * of parameters with the default encoding of {@link HTTP#DEFAULT_CONTENT_CHARSET}
+     *
+     * @param parameters iterable collection of name/value pairs
+     *
+     * @since 4.2
+     */
+    public UrlEncodedFormEntity (
+        final Iterable <? extends NameValuePair> parameters) {
+        this(parameters, null);
     }
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/client/methods/AbortableHttpRequest.java b/httpclient/src/main/java/org/apache/http/client/methods/AbortableHttpRequest.java
index b397fac..29a8710 100644
--- a/httpclient/src/main/java/org/apache/http/client/methods/AbortableHttpRequest.java
+++ b/httpclient/src/main/java/org/apache/http/client/methods/AbortableHttpRequest.java
@@ -34,7 +34,6 @@ import org.apache.http.conn.ClientConnectionManager;
 import org.apache.http.conn.ClientConnectionRequest;
 import org.apache.http.conn.ConnectionReleaseTrigger;
 import org.apache.http.conn.ManagedClientConnection;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
 
 /**
  * Interface representing an HTTP request that can be aborted by shutting
@@ -52,7 +51,6 @@ public interface AbortableHttpRequest {
      * If the request is already aborted, throws an {@link IOException}.
      *
      * @see ClientConnectionManager
-     * @see ThreadSafeClientConnManager
      */
     void setConnectionRequest(ClientConnectionRequest connRequest) throws IOException;
 
diff --git a/httpclient/src/main/java/org/apache/http/client/methods/HttpPatch.java b/httpclient/src/main/java/org/apache/http/client/methods/HttpPatch.java
new file mode 100644
index 0000000..3a6b126
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/client/methods/HttpPatch.java
@@ -0,0 +1,75 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.methods;
+
+import java.net.URI;
+
+import org.apache.http.annotation.NotThreadSafe;
+
+/**
+ * HTTP PATCH method.
+ * <p>
+ * The HTTP PATCH method is defined in <a
+ * href="http://tools.ietf.org/html/rfc5789">RF5789</a>: <blockquote> The PATCH
+ * method requests that a set of changes described in the request entity be
+ * applied to the resource identified by the Request- URI. Differs from the PUT
+ * method in the way the server processes the enclosed entity to modify the
+ * resource identified by the Request-URI. In a PUT request, the enclosed entity
+ * origin server, and the client is requesting that the stored version be
+ * replaced. With PATCH, however, the enclosed entity contains a set of
+ * instructions describing how a resource currently residing on the origin
+ * server should be modified to produce a new version. </blockquote>
+ * </p>
+ *
+ * @since 4.2
+ */
+ at NotThreadSafe
+public class HttpPatch extends HttpEntityEnclosingRequestBase {
+
+    public final static String METHOD_NAME = "PATCH";
+
+    public HttpPatch() {
+        super();
+    }
+
+    public HttpPatch(final URI uri) {
+        super();
+        setURI(uri);
+    }
+
+    public HttpPatch(final String uri) {
+        super();
+        setURI(URI.create(uri));
+    }
+
+    @Override
+    public String getMethod() {
+        return METHOD_NAME;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/methods/HttpRequestBase.java b/httpclient/src/main/java/org/apache/http/client/methods/HttpRequestBase.java
index 92b03e5..5c23fe3 100644
--- a/httpclient/src/main/java/org/apache/http/client/methods/HttpRequestBase.java
+++ b/httpclient/src/main/java/org/apache/http/client/methods/HttpRequestBase.java
@@ -46,8 +46,8 @@ import org.apache.http.params.HttpParams;
 import org.apache.http.params.HttpProtocolParams;
 
 /**
- * Basic implementation of an HTTP request that can be modified.
- *
+ * Basic implementation of an HTTP request that can be modified. Methods of the
+ * {@link AbortableHttpRequest} interface implemented by this class are thread safe.
  *
  * @since 4.0
  */
@@ -56,8 +56,7 @@ public abstract class HttpRequestBase extends AbstractHttpMessage
     implements HttpUriRequest, AbortableHttpRequest, Cloneable {
 
     private Lock abortLock;
-
-    private boolean aborted;
+    private volatile boolean aborted;
 
     private URI uri;
     private ClientConnectionRequest connRequest;
@@ -104,13 +103,11 @@ public abstract class HttpRequestBase extends AbstractHttpMessage
 
     public void setConnectionRequest(final ClientConnectionRequest connRequest)
             throws IOException {
+        if (this.aborted) {
+            throw new IOException("Request already aborted");
+        }
         this.abortLock.lock();
         try {
-            if (this.aborted) {
-                throw new IOException("Request already aborted");
-            }
-
-            this.releaseTrigger = null;
             this.connRequest = connRequest;
         } finally {
             this.abortLock.unlock();
@@ -119,56 +116,73 @@ public abstract class HttpRequestBase extends AbstractHttpMessage
 
     public void setReleaseTrigger(final ConnectionReleaseTrigger releaseTrigger)
             throws IOException {
+        if (this.aborted) {
+            throw new IOException("Request already aborted");
+        }
         this.abortLock.lock();
         try {
-            if (this.aborted) {
-                throw new IOException("Request already aborted");
-            }
-
-            this.connRequest = null;
             this.releaseTrigger = releaseTrigger;
         } finally {
             this.abortLock.unlock();
         }
     }
 
-    public void abort() {
-        ClientConnectionRequest localRequest;
-        ConnectionReleaseTrigger localTrigger;
+    private void cleanup() {
+        if (this.connRequest != null) {
+            this.connRequest.abortRequest();
+            this.connRequest = null;
+        }
+        if (this.releaseTrigger != null) {
+            try {
+                this.releaseTrigger.abortConnection();
+            } catch (IOException ex) {
+            }
+            this.releaseTrigger = null;
+        }
+    }
 
+    public void abort() {
+        if (this.aborted) {
+            return;
+        }
         this.abortLock.lock();
         try {
-            if (this.aborted) {
-                return;
-            }
             this.aborted = true;
-
-            localRequest = connRequest;
-            localTrigger = releaseTrigger;
+            cleanup();
         } finally {
             this.abortLock.unlock();
         }
-
-        // Trigger the callbacks outside of the lock, to prevent
-        // deadlocks in the scenario where the callbacks have
-        // their own locks that may be used while calling
-        // setReleaseTrigger or setConnectionRequest.
-        if (localRequest != null) {
-            localRequest.abortRequest();
-        }
-        if (localTrigger != null) {
-            try {
-                localTrigger.abortConnection();
-            } catch (IOException ex) {
-                // ignore
-            }
-        }
     }
 
     public boolean isAborted() {
         return this.aborted;
     }
 
+    /**
+     * Resets internal state of the request making it reusable.
+     *
+     * @since 4.2
+     */
+    public void reset() {
+        this.abortLock.lock();
+        try {
+            cleanup();
+            this.aborted = false;
+        } finally {
+            this.abortLock.unlock();
+        }
+    }
+
+    /**
+     * A convenience method to simplify migration from HttpClient 3.1 API. This method is
+     * equivalent to {@link #reset()}.
+     *
+     * @since 4.2
+     */
+    public void releaseConnection() {
+        reset();
+    }
+
     @Override
     public Object clone() throws CloneNotSupportedException {
         HttpRequestBase clone = (HttpRequestBase) super.clone();
@@ -181,4 +195,9 @@ public abstract class HttpRequestBase extends AbstractHttpMessage
         return clone;
     }
 
+    @Override
+    public String toString() {
+        return getMethod() + " " + getURI() + " " + getProtocolVersion();
+    }    
+
 }
diff --git a/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java b/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java
index 3f77310..4391dcf 100644
--- a/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java
+++ b/httpclient/src/main/java/org/apache/http/client/params/AuthPolicy.java
@@ -60,10 +60,17 @@ public final class AuthPolicy {
     public static final String BASIC = "Basic";
 
     /**
-     * SPNEGO/Kerberos Authentication scheme.
+     * SPNEGO Authentication scheme.
      *
      * @since 4.1
      */
     public static final String SPNEGO = "negotiate";
 
+    /**
+     * Kerberos Authentication scheme.
+     *
+     * @since 4.2
+     */
+    public static final String KERBEROS = "Kerberos";
+
 }
diff --git a/httpclient/src/main/java/org/apache/http/client/params/ClientPNames.java b/httpclient/src/main/java/org/apache/http/client/params/ClientPNames.java
index 6a84119..cb895a8 100644
--- a/httpclient/src/main/java/org/apache/http/client/params/ClientPNames.java
+++ b/httpclient/src/main/java/org/apache/http/client/params/ClientPNames.java
@@ -34,18 +34,10 @@ package org.apache.http.client.params;
 public interface ClientPNames {
 
     /**
-     * Defines the class name of the default {@link org.apache.http.conn.ClientConnectionManager}
-     * <p>
-     * This parameter expects a value of type {@link String}.
-     * </p>
-     */
-    public static final String CONNECTION_MANAGER_FACTORY_CLASS_NAME = "http.connection-manager.factory-class-name";
-
-    /**
-     * @deprecated use #CONNECTION_MANAGER_FACTORY_CLASS_NAME
+     * @deprecated (4.2)  do not use
      */
     @Deprecated
-    public static final String CONNECTION_MANAGER_FACTORY = "http.connection-manager.factory-object";
+    public static final String CONNECTION_MANAGER_FACTORY_CLASS_NAME = "http.connection-manager.factory-class-name";
 
     /**
      * Defines whether redirects should be handled automatically
@@ -100,11 +92,12 @@ public interface ClientPNames {
     public static final String COOKIE_POLICY = "http.protocol.cookie-policy";
 
     /**
-     * Defines the virtual host name to be used in the <code>Host</code>
-     * request header instead of the physical host name.
+     * Defines the virtual host to be used in the <code>Host</code>
+     * request header instead of the physical host.
      * <p>
      * This parameter expects a value of type {@link org.apache.http.HttpHost}.
      * </p>
+     * If a port is not provided, it will be derived from the request URL.
      */
     public static final String VIRTUAL_HOST = "http.virtual-host";
 
@@ -126,5 +119,16 @@ public interface ClientPNames {
      */
     public static final String DEFAULT_HOST = "http.default-host";
 
+    /**
+     * Defines the timeout in milliseconds used when retrieving an instance of
+     * {@link org.apache.http.conn.ManagedClientConnection} from the
+     * {@link org.apache.http.conn.ClientConnectionManager}.
+     * <p>
+     * This parameter expects a value of type {@link Long}.
+     * <p>
+     * @since 4.2
+     */
+    public static final String CONN_MANAGER_TIMEOUT = "http.conn-manager.timeout";
+
 }
 
diff --git a/httpclient/src/main/java/org/apache/http/client/params/ClientParamBean.java b/httpclient/src/main/java/org/apache/http/client/params/ClientParamBean.java
index 794818b..a9b1f73 100644
--- a/httpclient/src/main/java/org/apache/http/client/params/ClientParamBean.java
+++ b/httpclient/src/main/java/org/apache/http/client/params/ClientParamBean.java
@@ -33,7 +33,6 @@ import org.apache.http.annotation.NotThreadSafe;
 
 import org.apache.http.Header;
 import org.apache.http.HttpHost;
-import org.apache.http.conn.ClientConnectionManagerFactory;
 import org.apache.http.params.HttpAbstractParamBean;
 import org.apache.http.params.HttpParams;
 
@@ -51,15 +50,14 @@ public class ClientParamBean extends HttpAbstractParamBean {
         super(params);
     }
 
+    /**
+     * @deprecated (4.2)  do not use.
+     */
+    @Deprecated 
     public void setConnectionManagerFactoryClassName (final String factory) {
         params.setParameter(ClientPNames.CONNECTION_MANAGER_FACTORY_CLASS_NAME, factory);
     }
 
-    @Deprecated
-    public void setConnectionManagerFactory(ClientConnectionManagerFactory factory) {
-        params.setParameter(ClientPNames.CONNECTION_MANAGER_FACTORY, factory);
-    }
-
     public void setHandleRedirects (final boolean handle) {
         params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, handle);
     }
@@ -96,4 +94,11 @@ public class ClientParamBean extends HttpAbstractParamBean {
         params.setParameter(ClientPNames.DEFAULT_HOST, host);
     }
 
+    /**
+     * @since 4.2
+     */
+    public void setConnectionManagerTimeout(final long timeout) {
+        params.setLongParameter(ClientPNames.CONN_MANAGER_TIMEOUT, timeout);
+    }
+
 }
diff --git a/httpclient/src/main/java/org/apache/http/client/params/CookiePolicy.java b/httpclient/src/main/java/org/apache/http/client/params/CookiePolicy.java
index 3a1e5bb..a80b817 100644
--- a/httpclient/src/main/java/org/apache/http/client/params/CookiePolicy.java
+++ b/httpclient/src/main/java/org/apache/http/client/params/CookiePolicy.java
@@ -64,12 +64,12 @@ public final class CookiePolicy {
     public static final String BEST_MATCH = "best-match";
 
     /**
-     * The policy that ignores cookies. 
-     * 
+     * The policy that ignores cookies.
+     *
      * @since 4.1-beta1
      */
     public static final String IGNORE_COOKIES = "ignoreCookies";
-    
+
     private CookiePolicy() {
         super();
     }
diff --git a/httpclient/src/main/java/org/apache/http/client/params/HttpClientParams.java b/httpclient/src/main/java/org/apache/http/client/params/HttpClientParams.java
index 93fa855..84c5d48 100644
--- a/httpclient/src/main/java/org/apache/http/client/params/HttpClientParams.java
+++ b/httpclient/src/main/java/org/apache/http/client/params/HttpClientParams.java
@@ -28,6 +28,7 @@ package org.apache.http.client.params;
 
 import org.apache.http.annotation.Immutable;
 
+import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
 
 /**
@@ -93,4 +94,36 @@ public class HttpClientParams {
         params.setParameter(ClientPNames.COOKIE_POLICY, cookiePolicy);
     }
 
+    /**
+     * Set the parameter {@code ClientPNames.CONN_MANAGER_TIMEOUT}.
+     *
+     * @since 4.2
+     */
+    public static void setConnectionManagerTimeout(final HttpParams params, long timeout) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        params.setLongParameter(ClientPNames.CONN_MANAGER_TIMEOUT, timeout);
+    }
+
+    /**
+     * Get the connectiion manager timeout value.
+     * This is defined by the parameter {@code ClientPNames.CONN_MANAGER_TIMEOUT}.
+     * Failing that it uses the parameter {@code CoreConnectionPNames.CONNECTION_TIMEOUT}
+     * which defaults to 0 if not defined.
+     *
+     * @since 4.2
+     * @return the timeout value
+     */
+    public static long getConnectionManagerTimeout(final HttpParams params) {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        Long timeout = (Long) params.getParameter(ClientPNames.CONN_MANAGER_TIMEOUT);
+        if (timeout != null) {
+            return timeout.longValue();
+        }
+        return HttpConnectionParams.getConnectionTimeout(params);
+    }
+
 }
diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/ClientContext.java b/httpclient/src/main/java/org/apache/http/client/protocol/ClientContext.java
index 9e7cbd3..72ce5cc 100644
--- a/httpclient/src/main/java/org/apache/http/client/protocol/ClientContext.java
+++ b/httpclient/src/main/java/org/apache/http/client/protocol/ClientContext.java
@@ -95,7 +95,7 @@ public interface ClientContext {
     public static final String PROXY_AUTH_STATE      = "http.auth.proxy-scope";
 
     /**
-     * @deprecated do not use
+     * @deprecated (4.1)  do not use
      */
     @Deprecated
     public static final String AUTH_SCHEME_PREF      = "http.auth.scheme-pref";
diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/ClientContextConfigurer.java b/httpclient/src/main/java/org/apache/http/client/protocol/ClientContextConfigurer.java
index 59a9f6f..8fd1d10 100644
--- a/httpclient/src/main/java/org/apache/http/client/protocol/ClientContextConfigurer.java
+++ b/httpclient/src/main/java/org/apache/http/client/protocol/ClientContextConfigurer.java
@@ -27,15 +27,12 @@
 
 package org.apache.http.client.protocol;
 
-import java.util.List;
-
 import org.apache.http.annotation.NotThreadSafe;
 
 import org.apache.http.auth.AuthSchemeRegistry;
 import org.apache.http.client.CookieStore;
 import org.apache.http.client.CredentialsProvider;
 import org.apache.http.cookie.CookieSpecRegistry;
-import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.HttpContext;
 
 /**
@@ -70,14 +67,4 @@ public class ClientContextConfigurer implements ClientContext {
         this.context.setAttribute(CREDS_PROVIDER, provider);
     }
 
-    /**
-     * @deprecated (4.1-alpha1) Use {@link HttpParams#setParameter(String, Object)} to set the parameters
-     * {@link org.apache.http.auth.params.AuthPNames#TARGET_AUTH_PREF AuthPNames#TARGET_AUTH_PREF} and 
-     * {@link org.apache.http.auth.params.AuthPNames#PROXY_AUTH_PREF AuthPNames#PROXY_AUTH_PREF} instead
-     */
-    @Deprecated
-    public void setAuthSchemePref(final List<String> list) {
-        this.context.setAttribute(AUTH_SCHEME_PREF, list);
-    }
-
 }
diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/RequestAcceptEncoding.java b/httpclient/src/main/java/org/apache/http/client/protocol/RequestAcceptEncoding.java
index 1df09a9..3b72666 100644
--- a/httpclient/src/main/java/org/apache/http/client/protocol/RequestAcceptEncoding.java
+++ b/httpclient/src/main/java/org/apache/http/client/protocol/RequestAcceptEncoding.java
@@ -52,7 +52,9 @@ public class RequestAcceptEncoding implements HttpRequestInterceptor {
             final HttpContext context) throws HttpException, IOException {
 
         /* Signal support for Accept-Encoding transfer encodings. */
-        request.addHeader("Accept-Encoding", "gzip,deflate");
+        if (!request.containsHeader("Accept-Encoding")) {
+            request.addHeader("Accept-Encoding", "gzip,deflate");
+        } 
     }
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthCache.java b/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthCache.java
index 4693418..efa23e1 100644
--- a/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthCache.java
+++ b/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthCache.java
@@ -36,12 +36,15 @@ import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequestInterceptor;
 import org.apache.http.annotation.Immutable;
+import org.apache.http.auth.AuthProtocolState;
 import org.apache.http.auth.AuthScheme;
 import org.apache.http.auth.AuthScope;
 import org.apache.http.auth.AuthState;
 import org.apache.http.auth.Credentials;
 import org.apache.http.client.AuthCache;
 import org.apache.http.client.CredentialsProvider;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.protocol.ExecutionContext;
 import org.apache.http.protocol.HttpContext;
 
@@ -84,8 +87,16 @@ public class RequestAuthCache implements HttpRequestInterceptor {
         }
 
         HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
+        if (target.getPort() < 0) {
+            SchemeRegistry schemeRegistry = (SchemeRegistry) context.getAttribute(
+                    ClientContext.SCHEME_REGISTRY);
+            Scheme scheme = schemeRegistry.getScheme(target);
+            target = new HttpHost(target.getHostName(),
+                    scheme.resolvePort(target.getPort()), target.getSchemeName());
+        }
+
         AuthState targetState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
-        if (target != null && targetState != null && targetState.getAuthScheme() == null) {
+        if (target != null && targetState != null && targetState.getState() == AuthProtocolState.UNCHALLENGED) {
             AuthScheme authScheme = authCache.get(target);
             if (authScheme != null) {
                 doPreemptiveAuth(target, authScheme, targetState, credsProvider);
@@ -94,7 +105,7 @@ public class RequestAuthCache implements HttpRequestInterceptor {
 
         HttpHost proxy = (HttpHost) context.getAttribute(ExecutionContext.HTTP_PROXY_HOST);
         AuthState proxyState = (AuthState) context.getAttribute(ClientContext.PROXY_AUTH_STATE);
-        if (proxy != null && proxyState != null && proxyState.getAuthScheme() == null) {
+        if (proxy != null && proxyState != null && proxyState.getState() == AuthProtocolState.UNCHALLENGED) {
             AuthScheme authScheme = authCache.get(proxy);
             if (authScheme != null) {
                 doPreemptiveAuth(proxy, authScheme, proxyState, credsProvider);
@@ -112,13 +123,12 @@ public class RequestAuthCache implements HttpRequestInterceptor {
             this.log.debug("Re-using cached '" + schemeName + "' auth scheme for " + host);
         }
 
-        AuthScope authScope = new AuthScope(host.getHostName(), host.getPort(),
-                AuthScope.ANY_REALM, schemeName);
+        AuthScope authScope = new AuthScope(host, AuthScope.ANY_REALM, schemeName);
         Credentials creds = credsProvider.getCredentials(authScope);
 
         if (creds != null) {
-            authState.setAuthScheme(authScheme);
-            authState.setCredentials(creds);
+            authState.setState(AuthProtocolState.SUCCESS);
+            authState.update(authScheme, creds);
         } else {
             this.log.debug("No credentials for preemptive authentication");
         }
diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthenticationBase.java b/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthenticationBase.java
new file mode 100644
index 0000000..43a34b5
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthenticationBase.java
@@ -0,0 +1,131 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+import java.util.Queue;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.auth.AuthOption;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.ContextAwareAuthScheme;
+import org.apache.http.auth.Credentials;
+import org.apache.http.protocol.HttpContext;
+
+abstract class RequestAuthenticationBase implements HttpRequestInterceptor {
+
+    final Log log = LogFactory.getLog(getClass());
+
+    public RequestAuthenticationBase() {
+        super();
+    }
+
+    void process(
+            final AuthState authState,
+            final HttpRequest request,
+            final HttpContext context) throws HttpException, IOException {
+        AuthScheme authScheme = authState.getAuthScheme();
+        Credentials creds = authState.getCredentials();
+        switch (authState.getState()) {
+        case FAILURE:
+            return;
+        case SUCCESS:
+            ensureAuthScheme(authScheme);
+            if (authScheme.isConnectionBased()) {
+                return;
+            }
+            break;
+        case CHALLENGED:
+            Queue<AuthOption> authOptions = authState.getAuthOptions();
+            if (authOptions != null) {
+                while (!authOptions.isEmpty()) {
+                    AuthOption authOption = authOptions.remove();
+                    authScheme = authOption.getAuthScheme();
+                    creds = authOption.getCredentials();
+                    authState.update(authScheme, creds);
+                    if (this.log.isDebugEnabled()) {
+                        this.log.debug("Generating response to an authentication challenge using "
+                                + authScheme.getSchemeName() + " scheme");
+                    }
+                    try {
+                        Header header = authenticate(authScheme, creds, request, context);
+                        request.addHeader(header);
+                        break;
+                    } catch (AuthenticationException ex) {
+                        if (this.log.isWarnEnabled()) {
+                            this.log.warn(authScheme + " authentication error: " + ex.getMessage());
+                        }
+                    }
+                }
+                return;
+            } else {
+                ensureAuthScheme(authScheme);
+            }
+        }
+        if (authScheme != null) {
+            try {
+                Header header = authenticate(authScheme, creds, request, context);
+                request.addHeader(header);
+            } catch (AuthenticationException ex) {
+                if (this.log.isErrorEnabled()) {
+                    this.log.error(authScheme + " authentication error: " + ex.getMessage());
+                }
+            }
+        }
+    }
+
+    private void ensureAuthScheme(final AuthScheme authScheme) {
+        if (authScheme == null) {
+            throw new IllegalStateException("Auth scheme is not set");
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private Header authenticate(
+            final AuthScheme authScheme,
+            final Credentials creds,
+            final HttpRequest request,
+            final HttpContext context) throws AuthenticationException {
+        if (authScheme == null) {
+            throw new IllegalStateException("Auth state object is null");
+        }
+        if (authScheme instanceof ContextAwareAuthScheme) {
+            return ((ContextAwareAuthScheme) authScheme).authenticate(creds, request, context);
+        } else {
+            return authScheme.authenticate(creds, request);
+        }
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/RequestProxyAuthentication.java b/httpclient/src/main/java/org/apache/http/client/protocol/RequestProxyAuthentication.java
index 05f7056..9da3ede 100644
--- a/httpclient/src/main/java/org/apache/http/client/protocol/RequestProxyAuthentication.java
+++ b/httpclient/src/main/java/org/apache/http/client/protocol/RequestProxyAuthentication.java
@@ -29,20 +29,11 @@ package org.apache.http.client.protocol;
 
 import java.io.IOException;
 
-import org.apache.http.annotation.Immutable;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.http.Header;
 import org.apache.http.HttpException;
 import org.apache.http.HttpRequest;
-import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.annotation.Immutable;
 import org.apache.http.auth.AUTH;
-import org.apache.http.auth.AuthScheme;
 import org.apache.http.auth.AuthState;
-import org.apache.http.auth.AuthenticationException;
-import org.apache.http.auth.ContextAwareAuthScheme;
-import org.apache.http.auth.Credentials;
 import org.apache.http.conn.HttpRoutedConnection;
 import org.apache.http.conn.routing.HttpRoute;
 import org.apache.http.protocol.ExecutionContext;
@@ -55,15 +46,12 @@ import org.apache.http.protocol.HttpContext;
  * @since 4.0
  */
 @Immutable
-public class RequestProxyAuthentication implements HttpRequestInterceptor {
-
-    private final Log log = LogFactory.getLog(getClass());
+public class RequestProxyAuthentication extends RequestAuthenticationBase {
 
     public RequestProxyAuthentication() {
         super();
     }
 
-    @SuppressWarnings("deprecation")
     public void process(final HttpRequest request, final HttpContext context)
             throws HttpException, IOException {
         if (request == null) {
@@ -95,33 +83,10 @@ public class RequestProxyAuthentication implements HttpRequestInterceptor {
             this.log.debug("Proxy auth state not set in the context");
             return;
         }
-
-        AuthScheme authScheme = authState.getAuthScheme();
-        if (authScheme == null) {
-            return;
-        }
-
-        Credentials creds = authState.getCredentials();
-        if (creds == null) {
-            this.log.debug("User credentials not available");
-            return;
-        }
-        if (authState.getAuthScope() != null || !authScheme.isConnectionBased()) {
-            try {
-                Header header;
-                if (authScheme instanceof ContextAwareAuthScheme) {
-                    header = ((ContextAwareAuthScheme) authScheme).authenticate(
-                            creds, request, context);
-                } else {
-                    header = authScheme.authenticate(creds, request);
-                }
-                request.addHeader(header);
-            } catch (AuthenticationException ex) {
-                if (this.log.isErrorEnabled()) {
-                    this.log.error("Proxy authentication error: " + ex.getMessage());
-                }
-            }
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Proxy auth state: " + authState.getState());
         }
+        process(authState, request, context);
     }
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/RequestTargetAuthentication.java b/httpclient/src/main/java/org/apache/http/client/protocol/RequestTargetAuthentication.java
index 10074ed..475b14b 100644
--- a/httpclient/src/main/java/org/apache/http/client/protocol/RequestTargetAuthentication.java
+++ b/httpclient/src/main/java/org/apache/http/client/protocol/RequestTargetAuthentication.java
@@ -29,20 +29,11 @@ package org.apache.http.client.protocol;
 
 import java.io.IOException;
 
-import org.apache.http.annotation.Immutable;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.http.Header;
 import org.apache.http.HttpException;
 import org.apache.http.HttpRequest;
-import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.annotation.Immutable;
 import org.apache.http.auth.AUTH;
-import org.apache.http.auth.AuthScheme;
 import org.apache.http.auth.AuthState;
-import org.apache.http.auth.AuthenticationException;
-import org.apache.http.auth.ContextAwareAuthScheme;
-import org.apache.http.auth.Credentials;
 import org.apache.http.protocol.HttpContext;
 
 /**
@@ -52,15 +43,12 @@ import org.apache.http.protocol.HttpContext;
  * @since 4.0
  */
 @Immutable
-public class RequestTargetAuthentication implements HttpRequestInterceptor {
-
-    private final Log log = LogFactory.getLog(getClass());
+public class RequestTargetAuthentication extends RequestAuthenticationBase {
 
     public RequestTargetAuthentication() {
         super();
     }
 
-    @SuppressWarnings("deprecation")
     public void process(final HttpRequest request, final HttpContext context)
             throws HttpException, IOException {
         if (request == null) {
@@ -86,34 +74,10 @@ public class RequestTargetAuthentication implements HttpRequestInterceptor {
             this.log.debug("Target auth state not set in the context");
             return;
         }
-
-        AuthScheme authScheme = authState.getAuthScheme();
-        if (authScheme == null) {
-            return;
-        }
-
-        Credentials creds = authState.getCredentials();
-        if (creds == null) {
-            this.log.debug("User credentials not available");
-            return;
-        }
-
-        if (authState.getAuthScope() != null || !authScheme.isConnectionBased()) {
-            try {
-                Header header;
-                if (authScheme instanceof ContextAwareAuthScheme) {
-                    header = ((ContextAwareAuthScheme) authScheme).authenticate(
-                            creds, request, context);
-                } else {
-                    header = authScheme.authenticate(creds, request);
-                }
-                request.addHeader(header);
-            } catch (AuthenticationException ex) {
-                if (this.log.isErrorEnabled()) {
-                    this.log.error("Authentication error: " + ex.getMessage());
-                }
-            }
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Target auth state: " + authState.getState());
         }
+        process(authState, request, context);
     }
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/ResponseAuthCache.java b/httpclient/src/main/java/org/apache/http/client/protocol/ResponseAuthCache.java
index 7c5cbb2..6696fab 100644
--- a/httpclient/src/main/java/org/apache/http/client/protocol/ResponseAuthCache.java
+++ b/httpclient/src/main/java/org/apache/http/client/protocol/ResponseAuthCache.java
@@ -39,7 +39,10 @@ import org.apache.http.annotation.Immutable;
 import org.apache.http.auth.AuthScheme;
 import org.apache.http.auth.AuthState;
 import org.apache.http.client.AuthCache;
+import org.apache.http.client.AuthenticationStrategy;
 import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.impl.client.BasicAuthCache;
 import org.apache.http.protocol.ExecutionContext;
 import org.apache.http.protocol.HttpContext;
@@ -51,8 +54,11 @@ import org.apache.http.protocol.HttpContext;
  * additional authentication round-trips.
  *
  * @since 4.1
+ *
+ * @deprecated (4.2)  use {@link AuthenticationStrategy}
  */
 @Immutable
+ at Deprecated 
 public class ResponseAuthCache implements HttpResponseInterceptor {
 
     private final Log log = LogFactory.getLog(getClass());
@@ -74,24 +80,49 @@ public class ResponseAuthCache implements HttpResponseInterceptor {
         HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
         AuthState targetState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
         if (target != null && targetState != null) {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Target auth state: " + targetState.getState());
+            }
             if (isCachable(targetState)) {
+                if (target.getPort() < 0) {
+                    SchemeRegistry schemeRegistry = (SchemeRegistry) context.getAttribute(
+                            ClientContext.SCHEME_REGISTRY);
+                    Scheme scheme = schemeRegistry.getScheme(target);
+                    target = new HttpHost(target.getHostName(),
+                            scheme.resolvePort(target.getPort()), target.getSchemeName());
+                }
                 if (authCache == null) {
                     authCache = new BasicAuthCache();
                     context.setAttribute(ClientContext.AUTH_CACHE, authCache);
                 }
-                cache(authCache, target, targetState);
+                switch (targetState.getState()) {
+                case CHALLENGED:
+                    cache(authCache, target, targetState.getAuthScheme());
+                    break;
+                case FAILURE:
+                    uncache(authCache, target, targetState.getAuthScheme());
+                }
             }
         }
 
         HttpHost proxy = (HttpHost) context.getAttribute(ExecutionContext.HTTP_PROXY_HOST);
         AuthState proxyState = (AuthState) context.getAttribute(ClientContext.PROXY_AUTH_STATE);
         if (proxy != null && proxyState != null) {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Proxy auth state: " + proxyState.getState());
+            }
             if (isCachable(proxyState)) {
                 if (authCache == null) {
                     authCache = new BasicAuthCache();
                     context.setAttribute(ClientContext.AUTH_CACHE, authCache);
                 }
-                cache(authCache, proxy, proxyState);
+                switch (proxyState.getState()) {
+                case CHALLENGED:
+                    cache(authCache, proxy, proxyState.getAuthScheme());
+                    break;
+                case FAILURE:
+                    uncache(authCache, proxy, proxyState.getAuthScheme());
+                }
             }
         }
     }
@@ -106,19 +137,19 @@ public class ResponseAuthCache implements HttpResponseInterceptor {
                 schemeName.equalsIgnoreCase(AuthPolicy.DIGEST);
     }
 
-    private void cache(final AuthCache authCache, final HttpHost host, final AuthState authState) {
-        AuthScheme authScheme = authState.getAuthScheme();
-        if (authState.getAuthScope() != null) {
-            if (authState.getCredentials() != null) {
-                if (this.log.isDebugEnabled()) {
-                    this.log.debug("Caching '" + authScheme.getSchemeName() +
-                            "' auth scheme for " + host);
-                }
-                authCache.put(host, authScheme);
-            } else {
-                authCache.remove(host);
-            }
+    private void cache(final AuthCache authCache, final HttpHost host, final AuthScheme authScheme) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Caching '" + authScheme.getSchemeName() +
+                    "' auth scheme for " + host);
         }
+        authCache.put(host, authScheme);
     }
 
+    private void uncache(final AuthCache authCache, final HttpHost host, final AuthScheme authScheme) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Removing from cache '" + authScheme.getSchemeName() +
+                    "' auth scheme for " + host);
+        }
+        authCache.remove(host);
+    }
 }
diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/ResponseContentEncoding.java b/httpclient/src/main/java/org/apache/http/client/protocol/ResponseContentEncoding.java
index f218aa0..b86a088 100644
--- a/httpclient/src/main/java/org/apache/http/client/protocol/ResponseContentEncoding.java
+++ b/httpclient/src/main/java/org/apache/http/client/protocol/ResponseContentEncoding.java
@@ -52,6 +52,8 @@ import org.apache.http.protocol.HttpContext;
 @Immutable
 public class ResponseContentEncoding implements HttpResponseInterceptor {
 
+    public static final String UNCOMPRESSED = "http.client.response.uncompressed"; 
+    
     /**
      * Handles the following {@code Content-Encoding}s by
      * using the appropriate decompressor to wrap the response Entity:
@@ -80,9 +82,11 @@ public class ResponseContentEncoding implements HttpResponseInterceptor {
                     String codecname = codec.getName().toLowerCase(Locale.US);
                     if ("gzip".equals(codecname) || "x-gzip".equals(codecname)) {
                         response.setEntity(new GzipDecompressingEntity(response.getEntity()));
+                        if (context != null) context.setAttribute(UNCOMPRESSED, true);  
                         return;
                     } else if ("deflate".equals(codecname)) {
                         response.setEntity(new DeflateDecompressingEntity(response.getEntity()));
+                        if (context != null) context.setAttribute(UNCOMPRESSED, true);
                         return;
                     } else if ("identity".equals(codecname)) {
 
diff --git a/httpclient/src/main/java/org/apache/http/client/utils/HttpClientUtils.java b/httpclient/src/main/java/org/apache/http/client/utils/HttpClientUtils.java
new file mode 100644
index 0000000..ea9342d
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/client/utils/HttpClientUtils.java
@@ -0,0 +1,107 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.client.utils;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * Static helpers for dealing with {@link HttpResponse}s and {@link HttpClient}s.
+ * 
+ * @since 4.2
+ */
+public class HttpClientUtils {
+
+    private HttpClientUtils() {
+    }
+
+    /**
+     * Unconditionally close a response.
+     * <p>
+     * Example Code:
+     * 
+     * <pre>
+     * HttpResponse httpResponse = null;
+     * try {
+     *     httpResponse = httpClient.execute(httpGet);
+     * } catch (Exception e) {
+     *     // error handling
+     * } finally {
+     *     HttpClientUtils.closeQuietly(httpResponse);
+     * }
+     * </pre>
+     * 
+     * @param response
+     *            the HttpResponse to release resources, may be null or already
+     *            closed.
+     * 
+     * @since 4.2
+     */
+    public static void closeQuietly(final HttpResponse response) {
+        if (response != null) {
+            HttpEntity entity = response.getEntity();
+            if (entity != null) {
+                try {
+                    EntityUtils.consume(entity);
+                } catch (final IOException ex) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Unconditionally close a httpClient. Shuts down the underlying connection
+     * manager and releases the resources.
+     * <p>
+     * Example Code:
+     * 
+     * <pre>
+     * HttpClient httpClient = null;
+     * try {
+     *   httpClient = new DefaultHttpClient(...);
+     * } catch (Exception e) {
+     *   // error handling
+     * } finally {
+     *   HttpClientUtils.closeQuietly(httpClient);
+     * }
+     * </pre>
+     * 
+     * @param httpClient
+     *            the HttpClient to close, may be null or already closed.
+     * @since 4.2
+     */
+    public static void closeQuietly(final HttpClient httpClient) {
+        if (httpClient != null) {
+            httpClient.getConnectionManager().shutdown();
+        }
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/utils/Idn.java b/httpclient/src/main/java/org/apache/http/client/utils/Idn.java
index 8ce84eb..36fb291 100644
--- a/httpclient/src/main/java/org/apache/http/client/utils/Idn.java
+++ b/httpclient/src/main/java/org/apache/http/client/utils/Idn.java
@@ -43,4 +43,4 @@ public interface Idn {
      * @return the Unicode domain name
      */
     String toUnicode(String punycode);
-}
\ No newline at end of file
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/utils/JdkIdn.java b/httpclient/src/main/java/org/apache/http/client/utils/JdkIdn.java
index dacd75a..1ecd171 100644
--- a/httpclient/src/main/java/org/apache/http/client/utils/JdkIdn.java
+++ b/httpclient/src/main/java/org/apache/http/client/utils/JdkIdn.java
@@ -72,4 +72,4 @@ public class JdkIdn implements Idn {
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/utils/Rfc3492Idn.java b/httpclient/src/main/java/org/apache/http/client/utils/Rfc3492Idn.java
index 0bcb8a7..e7f83cb 100644
--- a/httpclient/src/main/java/org/apache/http/client/utils/Rfc3492Idn.java
+++ b/httpclient/src/main/java/org/apache/http/client/utils/Rfc3492Idn.java
@@ -123,4 +123,4 @@ public class Rfc3492Idn implements Idn {
         return output.toString();
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/utils/URIBuilder.java b/httpclient/src/main/java/org/apache/http/client/utils/URIBuilder.java
new file mode 100644
index 0000000..421e78e
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/client/utils/URIBuilder.java
@@ -0,0 +1,362 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.utils;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.http.Consts;
+import org.apache.http.NameValuePair;
+import org.apache.http.annotation.NotThreadSafe;
+import org.apache.http.conn.util.InetAddressUtils;
+import org.apache.http.message.BasicNameValuePair;
+
+/**
+ * {@link URI} builder for HTTP requests.
+ *
+ * @since 4.2
+ */
+ at NotThreadSafe
+public class URIBuilder {
+
+    private String scheme;
+    private String encodedSchemeSpecificPart;
+    private String encodedAuthority;
+    private String userInfo;
+    private String encodedUserInfo;
+    private String host;
+    private int port;
+    private String path;
+    private String encodedPath;
+    private String encodedQuery;
+    private List<NameValuePair> queryParams;
+    private String fragment;
+    private String encodedFragment;
+
+    /**
+     * Constructs an empty instance.
+     */
+    public URIBuilder() {
+        super();
+        this.port = -1;
+    }
+
+    /**
+     * Construct an instance from the string which must be a valid URI.
+     * 
+     * @param string a valid URI in string form
+     * @throws URISyntaxException if the input is not a valid URI
+     */
+    public URIBuilder(final String string) throws URISyntaxException {
+        super();
+        digestURI(new URI(string));
+    }
+
+    /**
+     * Construct an instance from the provided URI.
+     * @param uri
+     */
+    public URIBuilder(final URI uri) {
+        super();
+        digestURI(uri);
+    }
+
+    private List <NameValuePair> parseQuery(final String query, final Charset charset) {
+        if (query != null && query.length() > 0) {
+            return URLEncodedUtils.parse(query, charset);
+        }
+        return null;
+    }
+
+    /**
+     * Builds a {@link URI} instance.
+     */
+    public URI build() throws URISyntaxException {
+        return new URI(buildString());
+    }
+    
+    private String buildString() {
+        StringBuilder sb = new StringBuilder();
+        if (this.scheme != null) {
+            sb.append(this.scheme).append(':');
+        }
+        if (this.encodedSchemeSpecificPart != null) {
+            sb.append(this.encodedSchemeSpecificPart);
+        } else {
+            if (this.encodedAuthority != null) {
+                sb.append("//").append(this.encodedAuthority);
+            } else if (this.host != null) {
+                sb.append("//");
+                if (this.encodedUserInfo != null) {
+                    sb.append(this.encodedUserInfo).append("@");
+                } else if (this.userInfo != null) {
+                    sb.append(encodeUserInfo(this.userInfo)).append("@");
+                }
+                if (InetAddressUtils.isIPv6Address(this.host)) {
+                    sb.append("[").append(this.host).append("]");
+                } else {
+                    sb.append(this.host);
+                }
+                if (this.port >= 0) {
+                    sb.append(":").append(this.port);
+                }
+            }
+            if (this.encodedPath != null) {
+                sb.append(normalizePath(this.encodedPath));
+            } else if (this.path != null) {
+                sb.append(encodePath(normalizePath(this.path)));
+            }
+            if (this.encodedQuery != null) {
+                sb.append("?").append(this.encodedQuery);
+            } else if (this.queryParams != null) {
+                sb.append("?").append(encodeQuery(this.queryParams));
+            }
+        }
+        if (this.encodedFragment != null) {
+            sb.append("#").append(this.encodedFragment);
+        } else if (this.fragment != null) {
+            sb.append("#").append(encodeFragment(this.fragment));
+        }
+        return sb.toString();
+    }
+
+    private void digestURI(final URI uri) {
+        this.scheme = uri.getScheme();
+        this.encodedSchemeSpecificPart = uri.getRawSchemeSpecificPart();
+        this.encodedAuthority = uri.getRawAuthority();
+        this.host = uri.getHost();
+        this.port = uri.getPort();
+        this.encodedUserInfo = uri.getRawUserInfo();
+        this.userInfo = uri.getUserInfo();
+        this.encodedPath = uri.getRawPath();
+        this.path = uri.getPath();
+        this.encodedQuery = uri.getRawQuery();
+        this.queryParams = parseQuery(uri.getRawQuery(), Consts.UTF_8);
+        this.encodedFragment = uri.getRawFragment();
+        this.fragment = uri.getFragment();
+    }
+
+    private String encodeUserInfo(final String userInfo) {
+        return URLEncodedUtils.encUserInfo(userInfo, Consts.UTF_8);
+    }
+
+    private String encodePath(final String path) {
+        return URLEncodedUtils.encPath(path, Consts.UTF_8);
+    }
+
+    private String encodeQuery(final List<NameValuePair> params) {
+        return URLEncodedUtils.format(params, Consts.UTF_8);
+    }
+
+    private String encodeFragment(final String fragment) {
+        return URLEncodedUtils.encFragment(fragment, Consts.UTF_8);
+    }
+
+    /**
+     * Sets URI scheme.
+     */
+    public URIBuilder setScheme(final String scheme) {
+        this.scheme = scheme;
+        return this;
+    }
+
+    /**
+     * Sets URI user info. The value is expected to be unescaped and may contain non ASCII
+     * characters.
+     */
+    public URIBuilder setUserInfo(final String userInfo) {
+        this.userInfo = userInfo;
+        this.encodedSchemeSpecificPart = null;
+        this.encodedAuthority = null;
+        this.encodedUserInfo = null;
+        return this;
+    }
+
+    /**
+     * Sets URI user info as a combination of username and password. These values are expected to
+     * be unescaped and may contain non ASCII characters.
+     */
+    public URIBuilder setUserInfo(final String username, final String password) {
+        return setUserInfo(username + ':' + password);
+    }
+
+    /**
+     * Sets URI host.
+     */
+    public URIBuilder setHost(final String host) {
+        this.host = host;
+        this.encodedSchemeSpecificPart = null;
+        this.encodedAuthority = null;
+        return this;
+    }
+
+    /**
+     * Sets URI port.
+     */
+    public URIBuilder setPort(final int port) {
+        this.port = port < 0 ? -1 : port;
+        this.encodedSchemeSpecificPart = null;
+        this.encodedAuthority = null;
+        return this;
+    }
+
+    /**
+     * Sets URI path. The value is expected to be unescaped and may contain non ASCII characters.
+     */
+    public URIBuilder setPath(final String path) {
+        this.path = path;
+        this.encodedSchemeSpecificPart = null;
+        this.encodedPath = null;
+        return this;
+    }
+
+    /**
+     * Removes URI query.
+     */
+    public URIBuilder removeQuery() {
+        this.queryParams = null;
+        this.encodedQuery = null;
+        this.encodedSchemeSpecificPart = null;
+        return this;
+    }
+
+    /**
+     * Sets URI query.
+     * <p>
+     * The value is expected to be encoded form data.
+     */
+    public URIBuilder setQuery(final String query) {
+        this.queryParams = parseQuery(query, Consts.UTF_8);
+        this.encodedQuery = null;
+        this.encodedSchemeSpecificPart = null;
+        return this;
+    }
+
+    /**
+     * Adds parameter to URI query. The parameter name and value are expected to be unescaped
+     * and may contain non ASCII characters.
+     */
+    public URIBuilder addParameter(final String param, final String value) {
+        if (this.queryParams == null) {
+            this.queryParams = new ArrayList<NameValuePair>();
+        }
+        this.queryParams.add(new BasicNameValuePair(param, value));
+        this.encodedQuery = null;
+        this.encodedSchemeSpecificPart = null;
+        return this;
+    }
+
+    /**
+     * Sets parameter of URI query overriding existing value if set. The parameter name and value
+     * are expected to be unescaped and may contain non ASCII characters.
+     */
+    public URIBuilder setParameter(final String param, final String value) {
+        if (this.queryParams == null) {
+            this.queryParams = new ArrayList<NameValuePair>();
+        }
+        if (!this.queryParams.isEmpty()) {
+            for (Iterator<NameValuePair> it = this.queryParams.iterator(); it.hasNext(); ) {
+                NameValuePair nvp = it.next();
+                if (nvp.getName().equals(param)) {
+                    it.remove();
+                }
+            }
+        }
+        this.queryParams.add(new BasicNameValuePair(param, value));
+        this.encodedQuery = null;
+        this.encodedSchemeSpecificPart = null;
+        return this;
+    }
+
+    /**
+     * Sets URI fragment. The value is expected to be unescaped and may contain non ASCII
+     * characters.
+     */
+    public URIBuilder setFragment(final String fragment) {
+        this.fragment = fragment;
+        this.encodedFragment = null;
+        return this;
+    }
+
+    public String getScheme() {
+        return this.scheme;
+    }
+
+    public String getUserInfo() {
+        return this.userInfo;
+    }
+
+    public String getHost() {
+        return this.host;
+    }
+
+    public int getPort() {
+        return this.port;
+    }
+
+    public String getPath() {
+        return this.path;
+    }
+
+    public List<NameValuePair> getQueryParams() {
+        if (this.queryParams != null) {
+            return new ArrayList<NameValuePair>(this.queryParams);
+        } else {
+            return new ArrayList<NameValuePair>();
+        }
+    }
+
+    public String getFragment() {
+        return this.fragment;
+    }
+
+    @Override
+    public String toString() {
+        return buildString();
+    }
+
+    private static String normalizePath(String path) {
+        if (path == null) {
+            return null;
+        }
+        int n = 0;
+        for (; n < path.length(); n++) {
+            if (path.charAt(n) != '/') {
+                break;
+            }
+        }
+        if (n > 1) {
+            path = path.substring(n - 1);
+        }
+        return path;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java b/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java
index 50dc9f3..c120fa7 100644
--- a/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java
+++ b/httpclient/src/main/java/org/apache/http/client/utils/URIUtils.java
@@ -68,7 +68,10 @@ public class URIUtils {
          *             components violates RFC 2396, or if the authority
          *             component of the string is present but cannot be parsed
          *             as a server-based authority
+         *
+         * @deprecated (4.2) use {@link URIBuilder}.
          */
+    @Deprecated
     public static URI createURI(
             final String scheme,
             final String host,
@@ -76,7 +79,6 @@ public class URIUtils {
             final String path,
             final String query,
             final String fragment) throws URISyntaxException {
-
         StringBuilder buffer = new StringBuilder();
         if (host != null) {
             if (scheme != null) {
@@ -127,41 +129,22 @@ public class URIUtils {
             final HttpHost target,
             boolean dropFragment) throws URISyntaxException {
         if (uri == null) {
-            throw new IllegalArgumentException("URI may nor be null");
+            throw new IllegalArgumentException("URI may not be null");
         }
+        URIBuilder uribuilder = new URIBuilder(uri);
         if (target != null) {
-            return URIUtils.createURI(
-                    target.getSchemeName(),
-                    target.getHostName(),
-                    target.getPort(),
-                    normalizePath(uri.getRawPath()),
-                    uri.getRawQuery(),
-                    dropFragment ? null : uri.getRawFragment());
+            uribuilder.setScheme(target.getSchemeName());
+            uribuilder.setHost(target.getHostName());
+            uribuilder.setPort(target.getPort());
         } else {
-            return URIUtils.createURI(
-                    null,
-                    null,
-                    -1,
-                    normalizePath(uri.getRawPath()),
-                    uri.getRawQuery(),
-                    dropFragment ? null : uri.getRawFragment());
-        }
-    }
-
-    private static String normalizePath(String path) {
-        if (path == null) {
-            return null;
-        }
-        int n = 0;
-        for (; n < path.length(); n++) {
-            if (path.charAt(n) != '/') {
-                break;
-            }
+            uribuilder.setScheme(null);
+            uribuilder.setHost(null);
+            uribuilder.setPort(-1);
         }
-        if (n > 1) {
-            path = path.substring(n - 1);
+        if (dropFragment) {
+            uribuilder.setFragment(null);
         }
-        return path;
+        return uribuilder.build();
     }
 
     /**
@@ -176,6 +159,27 @@ public class URIUtils {
     }
 
     /**
+     * A convenience method that creates a new {@link URI} whose scheme, host, port, path,
+     * query are taken from the existing URI, dropping any fragment or user-information.
+     * The existing URI is returned unmodified if it has no fragment or user-information.
+     *
+     * @param uri
+     *            original URI.
+     * @throws URISyntaxException
+     *             If the resulting URI is invalid.
+     */
+    public static URI rewriteURI(final URI uri) throws URISyntaxException {
+        if (uri == null) {
+            throw new IllegalArgumentException("URI may not be null");
+        }
+        if (uri.getFragment() != null || uri.getUserInfo() != null) {
+            return new URIBuilder(uri).setFragment(null).setUserInfo(null).build();
+        } else {
+            return uri;
+        }
+    }
+
+    /**
      * Resolves a URI reference against a base URI. Work-around for bug in
      * java.net.URI (<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535>)
      *
@@ -274,11 +278,11 @@ public class URIUtils {
 
     /**
      * Extracts target host from the given {@link URI}.
-     * 
-     * @param uri 
-     * @return the target host if the URI is absolute or <code>null</null> if the URI is 
+     *
+     * @param uri
+     * @return the target host if the URI is absolute or <code>null</null> if the URI is
      * relative or does not contain a valid host name.
-     * 
+     *
      * @since 4.1
      */
     public static HttpHost extractHost(final URI uri) {
@@ -303,15 +307,27 @@ public class URIUtils {
                         }
                     }
                     // Extract the port suffix, if present
-                    if (host != null) { 
+                    if (host != null) {
                         int colon = host.indexOf(':');
                         if (colon >= 0) {
-                            if (colon+1 < host.length()) {
-                                port = Integer.parseInt(host.substring(colon+1));
+                            int pos = colon + 1;
+                            int len = 0;
+                            for (int i = pos; i < host.length(); i++) {
+                                if (Character.isDigit(host.charAt(i))) {
+                                    len++;
+                                } else {
+                                    break;
+                                }
+                            }
+                            if (len > 0) {
+                                try {
+                                    port = Integer.parseInt(host.substring(pos, pos + len));
+                                } catch (NumberFormatException ex) {
+                                }
                             }
-                            host = host.substring(0,colon);
-                        }                
-                    }                    
+                            host = host.substring(0, colon);
+                        }
+                    }
                 }
             }
             String scheme = uri.getScheme();
@@ -321,7 +337,7 @@ public class URIUtils {
         }
         return target;
     }
-    
+
     /**
      * This class should not be instantiated.
      */
diff --git a/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java b/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java
index 505ea91..800d0cc 100644
--- a/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java
+++ b/httpclient/src/main/java/org/apache/http/client/utils/URLEncodedUtils.java
@@ -28,23 +28,29 @@
 package org.apache.http.client.utils;
 
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
-import java.net.URLDecoder;
-import java.net.URLEncoder;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.List;
 import java.util.Scanner;
 
 import org.apache.http.annotation.Immutable;
+import org.apache.http.entity.ContentType;
 
+import org.apache.http.Consts;
 import org.apache.http.Header;
 import org.apache.http.HeaderElement;
 import org.apache.http.HttpEntity;
 import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicHeaderValueParser;
 import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.message.ParserCursor;
 import org.apache.http.protocol.HTTP;
+import org.apache.http.util.CharArrayBuffer;
 import org.apache.http.util.EntityUtils;
 
 /**
@@ -73,13 +79,15 @@ public class URLEncodedUtils {
      *            encoding to use while parsing the query
      */
     public static List <NameValuePair> parse (final URI uri, final String encoding) {
-        List <NameValuePair> result = Collections.emptyList();
         final String query = uri.getRawQuery();
         if (query != null && query.length() > 0) {
-            result = new ArrayList <NameValuePair>();
-            parse(result, new Scanner(query), encoding);
+            List<NameValuePair> result = new ArrayList<NameValuePair>();
+            Scanner scanner = new Scanner(query);
+            parse(result, scanner, encoding);
+            return result;
+        } else {
+            return Collections.emptyList();
         }
-        return result;
     }
 
     /**
@@ -96,32 +104,18 @@ public class URLEncodedUtils {
      */
     public static List <NameValuePair> parse (
             final HttpEntity entity) throws IOException {
-        List <NameValuePair> result = Collections.emptyList();
-
-        String contentType = null;
-        String charset = null;
-
-        Header h = entity.getContentType();
-        if (h != null) {
-            HeaderElement[] elems = h.getElements();
-            if (elems.length > 0) {
-                HeaderElement elem = elems[0];
-                contentType = elem.getName();
-                NameValuePair param = elem.getParameterByName("charset");
-                if (param != null) {
-                    charset = param.getValue();
-                }
-            }
-        }
-
-        if (contentType != null && contentType.equalsIgnoreCase(CONTENT_TYPE)) {
-            final String content = EntityUtils.toString(entity, HTTP.ASCII);
+        ContentType contentType = ContentType.get(entity);
+        if (contentType != null && contentType.getMimeType().equalsIgnoreCase(CONTENT_TYPE)) {
+            String content = EntityUtils.toString(entity, Consts.ASCII);
             if (content != null && content.length() > 0) {
-                result = new ArrayList <NameValuePair>();
-                parse(result, new Scanner(content), charset);
+                Charset charset = contentType.getCharset();
+                if (charset == null) {
+                    charset = HTTP.DEF_CONTENT_CHARSET;
+                }
+                return parse(content, charset);
             }
         }
-        return result;
+        return Collections.emptyList();
     }
 
     /**
@@ -154,27 +148,62 @@ public class URLEncodedUtils {
      *            List to add parameters to.
      * @param scanner
      *            Input that contains the parameters to parse.
-     * @param encoding
+     * @param charset
      *            Encoding to use when decoding the parameters.
      */
     public static void parse (
             final List <NameValuePair> parameters,
             final Scanner scanner,
-            final String encoding) {
+            final String charset) {
         scanner.useDelimiter(PARAMETER_SEPARATOR);
         while (scanner.hasNext()) {
-            final String[] nameValue = scanner.next().split(NAME_VALUE_SEPARATOR);
-            if (nameValue.length == 0 || nameValue.length > 2)
-                throw new IllegalArgumentException("bad parameter");
-
-            final String name = decode(nameValue[0], encoding);
+            String name = null;
             String value = null;
-            if (nameValue.length == 2)
-                value = decode(nameValue[1], encoding);
+            String token = scanner.next();
+            int i = token.indexOf(NAME_VALUE_SEPARATOR);
+            if (i != -1) {
+                name = decodeFormFields(token.substring(0, i).trim(), charset);
+                value = decodeFormFields(token.substring(i + 1).trim(), charset);
+            } else {
+                name = decodeFormFields(token.trim(), charset);
+            }
             parameters.add(new BasicNameValuePair(name, value));
         }
     }
 
+    private static final char[] DELIM = new char[] { '&' };
+
+    /**
+     * Returns a list of {@link NameValuePair NameValuePairs} as parsed from the given string
+     * using the given character encoding.
+     *
+     * @param s
+     *            text to parse.
+     * @param charset
+     *            Encoding to use when decoding the parameters.
+     *
+     * @since 4.2
+     */
+    public static List<NameValuePair> parse (final String s, final Charset charset) {
+        if (s == null) {
+            return Collections.emptyList();
+        }
+        BasicHeaderValueParser parser = BasicHeaderValueParser.DEFAULT;
+        CharArrayBuffer buffer = new CharArrayBuffer(s.length());
+        buffer.append(s);
+        ParserCursor cursor = new ParserCursor(0, buffer.length());
+        List<NameValuePair> list = new ArrayList<NameValuePair>();
+        while (!cursor.atEnd()) {
+            NameValuePair nvp = parser.parseNameValuePair(buffer, cursor, DELIM);
+            if (nvp.getName().length() > 0) {
+                list.add(new BasicNameValuePair(
+                        decodeFormFields(nvp.getName(), charset),
+                        decodeFormFields(nvp.getValue(), charset)));
+            }
+        }
+        return list;
+    }
+
     /**
      * Returns a String that is suitable for use as an <code>application/x-www-form-urlencoded</code>
      * list of parameters in an HTTP PUT or HTTP POST.
@@ -187,34 +216,329 @@ public class URLEncodedUtils {
             final String encoding) {
         final StringBuilder result = new StringBuilder();
         for (final NameValuePair parameter : parameters) {
-            final String encodedName = encode(parameter.getName(), encoding);
-            final String value = parameter.getValue();
-            final String encodedValue = value != null ? encode(value, encoding) : "";
-            if (result.length() > 0)
+            final String encodedName = encodeFormFields(parameter.getName(), encoding);
+            final String encodedValue = encodeFormFields(parameter.getValue(), encoding);
+            if (result.length() > 0) {
                 result.append(PARAMETER_SEPARATOR);
+            }
             result.append(encodedName);
-            result.append(NAME_VALUE_SEPARATOR);
-            result.append(encodedValue);
+            if (encodedValue != null) {
+                result.append(NAME_VALUE_SEPARATOR);
+                result.append(encodedValue);
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * Returns a String that is suitable for use as an <code>application/x-www-form-urlencoded</code>
+     * list of parameters in an HTTP PUT or HTTP POST.
+     *
+     * @param parameters  The parameters to include.
+     * @param charset The encoding to use.
+     *
+     * @since 4.2
+     */
+    public static String format (
+            final Iterable<? extends NameValuePair> parameters,
+            final Charset charset) {
+        final StringBuilder result = new StringBuilder();
+        for (final NameValuePair parameter : parameters) {
+            final String encodedName = encodeFormFields(parameter.getName(), charset);
+            final String encodedValue = encodeFormFields(parameter.getValue(), charset);
+            if (result.length() > 0) {
+                result.append(PARAMETER_SEPARATOR);
+            }
+            result.append(encodedName);
+            if (encodedValue != null) {
+                result.append(NAME_VALUE_SEPARATOR);
+                result.append(encodedValue);
+            }
         }
         return result.toString();
     }
 
-    private static String decode (final String content, final String encoding) {
-        try {
-            return URLDecoder.decode(content,
-                    encoding != null ? encoding : HTTP.DEFAULT_CONTENT_CHARSET);
-        } catch (UnsupportedEncodingException problem) {
-            throw new IllegalArgumentException(problem);
+    /** 
+     * Unreserved characters, i.e. alphanumeric, plus: {@code _ - ! . ~ ' ( ) *}
+     * <p>
+     *  This list is the same as the {@code unreserved} list in
+     *  <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>
+     */
+    private static final BitSet UNRESERVED   = new BitSet(256);
+    /**
+     * Punctuation characters: , ; : $ & + =
+     * <p>
+     * These are the additional characters allowed by userinfo.
+     */
+    private static final BitSet PUNCT        = new BitSet(256);
+    /** Characters which are safe to use in userinfo, i.e. {@link #UNRESERVED} plus {@link #PUNCT}uation */
+    private static final BitSet USERINFO     = new BitSet(256);
+    /** Characters which are safe to use in a path, i.e. {@link #UNRESERVED} plus {@link #PUNCT}uation plus / @ */
+    private static final BitSet PATHSAFE     = new BitSet(256);
+    /** Characters which are safe to use in a fragment, i.e. {@link #RESERVED} plus {@link #UNRESERVED} */
+    private static final BitSet FRAGMENT     = new BitSet(256);
+
+    /** 
+     * Reserved characters, i.e. {@code ;/?:@&=+$,[]}
+     * <p>
+     *  This list is the same as the {@code reserved} list in 
+     *  <a href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</a>
+     *  as augmented by
+     *  <a href="http://www.ietf.org/rfc/rfc2732.txt">RFC 2732</a>
+     */
+    private static final BitSet RESERVED     = new BitSet(256);
+
+    
+    /** 
+     * Safe characters for x-www-form-urlencoded data, as per java.net.URLEncoder and browser behaviour,
+     * i.e. alphanumeric plus {@code "-", "_", ".", "*"}
+     */
+    private static final BitSet URLENCODER   = new BitSet(256);
+
+    static {
+        // unreserved chars
+        // alpha characters
+        for (int i = 'a'; i <= 'z'; i++) {
+            UNRESERVED.set(i);
+        }
+        for (int i = 'A'; i <= 'Z'; i++) {
+            UNRESERVED.set(i);
+        }
+        // numeric characters
+        for (int i = '0'; i <= '9'; i++) {
+            UNRESERVED.set(i);
+        }
+        UNRESERVED.set('_'); // these are the charactes of the "mark" list
+        UNRESERVED.set('-');
+        UNRESERVED.set('.');
+        UNRESERVED.set('*');
+        URLENCODER.or(UNRESERVED); // skip remaining unreserved characters
+        UNRESERVED.set('!');
+        UNRESERVED.set('~');
+        UNRESERVED.set('\'');
+        UNRESERVED.set('(');
+        UNRESERVED.set(')');
+        // punct chars
+        PUNCT.set(',');
+        PUNCT.set(';');
+        PUNCT.set(':');
+        PUNCT.set('$');
+        PUNCT.set('&');
+        PUNCT.set('+');
+        PUNCT.set('=');
+        // Safe for userinfo
+        USERINFO.or(UNRESERVED);
+        USERINFO.or(PUNCT);
+
+        // URL path safe
+        PATHSAFE.or(UNRESERVED);
+        PATHSAFE.set('/'); // segment separator
+        PATHSAFE.set(';'); // param separator
+        PATHSAFE.set(':'); // rest as per list in 2396, i.e. : @ & = + $ ,
+        PATHSAFE.set('@');
+        PATHSAFE.set('&');
+        PATHSAFE.set('=');
+        PATHSAFE.set('+');
+        PATHSAFE.set('$');
+        PATHSAFE.set(',');
+        
+        RESERVED.set(';');
+        RESERVED.set('/');
+        RESERVED.set('?');
+        RESERVED.set(':');
+        RESERVED.set('@');
+        RESERVED.set('&');
+        RESERVED.set('=');
+        RESERVED.set('+');
+        RESERVED.set('$');
+        RESERVED.set(',');
+        RESERVED.set('['); // added by RFC 2732
+        RESERVED.set(']'); // added by RFC 2732
+        
+        FRAGMENT.or(RESERVED);
+        FRAGMENT.or(UNRESERVED);
+    }
+
+    private static final int RADIX = 16;
+
+    /**
+     * Emcode/escape a portion of a URL, to use with the query part ensure {@code plusAsBlank} is true.
+     * 
+     * @param content the portion to decode
+     * @param charset the charset to use
+     * @param blankAsPlus if {@code true}, then convert space to '+' (e.g. for www-url-form-encoded content), otherwise leave as is.
+     * @return
+     */
+    private static String urlencode(
+            final String content,
+            final Charset charset,
+            final BitSet safechars,
+            final boolean blankAsPlus) {
+        if (content == null) {
+            return null;
         }
+        StringBuilder buf = new StringBuilder();
+        ByteBuffer bb = charset.encode(content);
+        while (bb.hasRemaining()) {
+            int b = bb.get() & 0xff;
+            if (safechars.get(b)) {
+                buf.append((char) b);
+            } else if (blankAsPlus && b == ' ') {
+                buf.append('+');
+            } else {
+                buf.append("%");
+                char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, RADIX));
+                char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, RADIX));
+                buf.append(hex1);
+                buf.append(hex2);
+            }
+        }
+        return buf.toString();
     }
 
-    private static String encode (final String content, final String encoding) {
-        try {
-            return URLEncoder.encode(content,
-                    encoding != null ? encoding : HTTP.DEFAULT_CONTENT_CHARSET);
-        } catch (UnsupportedEncodingException problem) {
-            throw new IllegalArgumentException(problem);
+    /**
+     * Decode/unescape a portion of a URL, to use with the query part ensure {@code plusAsBlank} is true.
+     * 
+     * @param content the portion to decode
+     * @param charset the charset to use
+     * @param plusAsBlank if {@code true}, then convert '+' to space (e.g. for www-url-form-encoded content), otherwise leave as is.
+     * @return
+     */
+    private static String urldecode(
+            final String content,
+            final Charset charset,
+            final boolean plusAsBlank) {
+        if (content == null) {
+            return null;
+        }
+        ByteBuffer bb = ByteBuffer.allocate(content.length());
+        CharBuffer cb = CharBuffer.wrap(content);
+        while (cb.hasRemaining()) {
+            char c = cb.get();
+            if (c == '%' && cb.remaining() >= 2) {
+                char uc = cb.get();
+                char lc = cb.get();
+                int u = Character.digit(uc, 16);
+                int l = Character.digit(lc, 16);
+                if (u != -1 && l != -1) {
+                    bb.put((byte) ((u << 4) + l));
+                } else {
+                    bb.put((byte) '%');
+                    bb.put((byte) uc);
+                    bb.put((byte) lc);
+                }
+            } else if (plusAsBlank && c == '+') {
+                bb.put((byte) ' ');
+            } else {
+                bb.put((byte) c);
+            }
+        }
+        bb.flip();
+        return charset.decode(bb).toString();
+    }
+
+    /**
+     * Decode/unescape www-url-form-encoded content.
+     * 
+     * @param content the content to decode, will decode '+' as space
+     * @param charset the charset to use
+     * @return
+     */
+    private static String decodeFormFields (final String content, final String charset) {
+        if (content == null) {
+            return null;
         }
+        return urldecode(content, charset != null ? Charset.forName(charset) : Consts.UTF_8, true);
+    }
+
+    /**
+     * Decode/unescape www-url-form-encoded content.
+     * 
+     * @param content the content to decode, will decode '+' as space
+     * @param charset the charset to use
+     * @return
+     */
+    private static String decodeFormFields (final String content, final Charset charset) {
+        if (content == null) {
+            return null;
+        }
+        return urldecode(content, charset != null ? charset : Consts.UTF_8, true);
+    }
+
+    /**
+     * Encode/escape www-url-form-encoded content.
+     * <p>
+     * Uses the {@link #URLENCODER} set of characters, rather than
+     * the {@link #UNRSERVED} set; this is for compatibilty with previous
+     * releases, URLEncoder.encode() and most browsers.
+     * 
+     * @param content the content to encode, will convert space to '+'
+     * @param charset the charset to use
+     * @return
+     */
+    private static String encodeFormFields (final String content, final String charset) {
+        if (content == null) {
+            return null;
+        }
+        return urlencode(content, charset != null ? Charset.forName(charset) :
+            Consts.UTF_8, URLENCODER, true);
+    }
+
+    /**
+     * Encode/escape www-url-form-encoded content.
+     * <p>
+     * Uses the {@link #URLENCODER} set of characters, rather than
+     * the {@link #UNRSERVED} set; this is for compatibilty with previous
+     * releases, URLEncoder.encode() and most browsers.
+     * 
+     * @param content the content to encode, will convert space to '+'
+     * @param charset the charset to use
+     * @return
+     */
+    private static String encodeFormFields (final String content, final Charset charset) {
+        if (content == null) {
+            return null;
+        }
+        return urlencode(content, charset != null ? charset : Consts.UTF_8, URLENCODER, true);
+    }
+
+    /**
+     * Encode a String using the {@link #USERINFO} set of characters.
+     * <p>
+     * Used by URIBuilder to encode the userinfo segment.
+     * 
+     * @param content the string to encode, does not convert space to '+'
+     * @param charset the charset to use
+     * @return the encoded string
+     */
+    static String encUserInfo(final String content, final Charset charset) {
+        return urlencode(content, charset, USERINFO, false);
+    }
+
+    /**
+     * Encode a String using the {@link #FRAGMENT} set of characters.
+     * <p>
+     * Used by URIBuilder to encode the userinfo segment.
+     * 
+     * @param content the string to encode, does not convert space to '+'
+     * @param charset the charset to use
+     * @return the encoded string
+     */
+    static String encFragment(final String content, final Charset charset) {
+        return urlencode(content, charset, FRAGMENT, false);
+    }
+
+    /**
+     * Encode a String using the {@link #PATHSAFE} set of characters.
+     * <p>
+     * Used by URIBuilder to encode path segments.
+     * 
+     * @param content the string to encode, does not convert space to '+'
+     * @param charset the charset to use
+     * @return the encoded string
+     */
+    static String encPath(final String content, final Charset charset) {
+        return urlencode(content, charset, PATHSAFE, false);
     }
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/conn/BasicManagedEntity.java b/httpclient/src/main/java/org/apache/http/conn/BasicManagedEntity.java
index 81df17e..3f6975c 100644
--- a/httpclient/src/main/java/org/apache/http/conn/BasicManagedEntity.java
+++ b/httpclient/src/main/java/org/apache/http/conn/BasicManagedEntity.java
@@ -30,6 +30,7 @@ package org.apache.http.conn;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.SocketException;
 
 import org.apache.http.annotation.NotThreadSafe;
 
@@ -103,7 +104,9 @@ public class BasicManagedEntity extends HttpEntityWrapper
         }
     }
 
-    @Deprecated
+    /**
+     * @deprecated (4.1) Use {@link EntityUtils#consume(HttpEntity)}
+     */
     @Override
     public void consumeContent() throws IOException {
         ensureConsumed();
@@ -147,10 +150,17 @@ public class BasicManagedEntity extends HttpEntityWrapper
     public boolean streamClosed(InputStream wrapped) throws IOException {
         try {
             if (attemptReuse && (managedConn != null)) {
+                boolean valid = managedConn.isOpen();
                 // this assumes that closing the stream will
                 // consume the remainder of the response body:
-                wrapped.close();
-                managedConn.markReusable();
+                try {
+                    wrapped.close();
+                    managedConn.markReusable();
+                } catch (SocketException ex) {
+                    if (valid) {
+                        throw ex;
+                    }
+                }
             }
         } finally {
             releaseManagedConnection();
diff --git a/httpclient/src/main/java/org/apache/http/conn/DnsResolver.java b/httpclient/src/main/java/org/apache/http/conn/DnsResolver.java
new file mode 100644
index 0000000..c4501f3
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/conn/DnsResolver.java
@@ -0,0 +1,54 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.conn;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * Users may implement this interface to override the normal DNS lookup offered
+ * by the OS.
+ *
+ * @since 4.2
+ */
+public interface DnsResolver {
+
+    /**
+     * Returns the IP address for the specified host name, or null if the given
+     * host is not recognized or the associated IP address cannot be used to
+     * build an InetAddress instance.
+     *
+     * @see InetAddress
+     *
+     * @param host
+     *            The host name to be resolved by this resolver.
+     * @return The IP address associated to the given host name, or null if the
+     *         host name is not known by the implementation class.
+     */
+    InetAddress[] resolve(String host) throws UnknownHostException;
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/conn/HttpInetSocketAddress.java b/httpclient/src/main/java/org/apache/http/conn/HttpInetSocketAddress.java
new file mode 100644
index 0000000..c59d212
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/conn/HttpInetSocketAddress.java
@@ -0,0 +1,63 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+import org.apache.http.HttpHost;
+
+/**
+ * Extended {@link InetSocketAddress} implementation that also provides access to the original
+ * {@link HttpHost} used to resolve the address.
+ *
+ * @since 4.2
+ */
+public class HttpInetSocketAddress extends InetSocketAddress {
+
+    private static final long serialVersionUID = -6650701828361907957L;
+
+    private final HttpHost httphost;
+
+    public HttpInetSocketAddress(final HttpHost httphost, final InetAddress addr, int port) {
+        super(addr, port);
+        if (httphost == null) {
+            throw new IllegalArgumentException("HTTP host may not be null");
+        }
+        this.httphost = httphost;
+    }
+
+    public HttpHost getHttpHost() {
+        return this.httphost;
+    }
+
+    @Override
+    public String toString() {
+        return this.httphost.getHostName() + ":" + getPort();
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/conn/MultihomePlainSocketFactory.java b/httpclient/src/main/java/org/apache/http/conn/MultihomePlainSocketFactory.java
index 551477f..22fa0a7 100644
--- a/httpclient/src/main/java/org/apache/http/conn/MultihomePlainSocketFactory.java
+++ b/httpclient/src/main/java/org/apache/http/conn/MultihomePlainSocketFactory.java
@@ -53,7 +53,7 @@ import org.apache.http.params.HttpParams;
  *
  * @since 4.0
  *
- * @deprecated Do not use. For multihome support socket factories must implement
+ * @deprecated (4.1)  Do not use. For multihome support socket factories must implement
  * {@link SchemeSocketFactory} interface.
  */
 @Deprecated
diff --git a/httpclient/src/main/java/org/apache/http/conn/params/ConnConnectionPNames.java b/httpclient/src/main/java/org/apache/http/conn/params/ConnConnectionPNames.java
index 2ac6f66..a6d66b0 100644
--- a/httpclient/src/main/java/org/apache/http/conn/params/ConnConnectionPNames.java
+++ b/httpclient/src/main/java/org/apache/http/conn/params/ConnConnectionPNames.java
@@ -26,11 +26,14 @@
 
 package org.apache.http.conn.params;
 
+import org.apache.http.impl.conn.DefaultHttpResponseParser;
+
 /**
  * Parameter names for HTTP client connections.
  *
  * @since 4.0
  */
+ at Deprecated
 public interface ConnConnectionPNames {
 
     /**
@@ -49,6 +52,8 @@ public interface ConnConnectionPNames {
      * 0 disallows all garbage/empty lines before the status line.
      * Use {@link java.lang.Integer#MAX_VALUE} for unlimited number.
      * </p>
+     *
+     * @deprecated Use custom {@link DefaultHttpResponseParser} implementation
      */
     public static final String MAX_STATUS_LINE_GARBAGE = "http.connection.max-status-line-garbage";
 
diff --git a/httpclient/src/main/java/org/apache/http/conn/params/ConnConnectionParamBean.java b/httpclient/src/main/java/org/apache/http/conn/params/ConnConnectionParamBean.java
index b44fb3c..eac4c52 100644
--- a/httpclient/src/main/java/org/apache/http/conn/params/ConnConnectionParamBean.java
+++ b/httpclient/src/main/java/org/apache/http/conn/params/ConnConnectionParamBean.java
@@ -27,8 +27,7 @@
 
 package org.apache.http.conn.params;
 
-import org.apache.http.annotation.NotThreadSafe;
-
+import org.apache.http.impl.conn.DefaultHttpResponseParser;
 import org.apache.http.params.HttpAbstractParamBean;
 import org.apache.http.params.HttpParams;
 
@@ -38,8 +37,10 @@ import org.apache.http.params.HttpParams;
  * using Java Beans conventions.
  *
  * @since 4.0
+ * 
+ * @deprecated (4.2) do not use
  */
- at NotThreadSafe
+ at Deprecated 
 public class ConnConnectionParamBean extends HttpAbstractParamBean {
 
     public ConnConnectionParamBean (final HttpParams params) {
@@ -47,7 +48,7 @@ public class ConnConnectionParamBean extends HttpAbstractParamBean {
     }
 
     /**
-     * @see ConnConnectionPNames#MAX_STATUS_LINE_GARBAGE
+     * @deprecated (4.2)  Use custom {@link DefaultHttpResponseParser} implementation
      */
     public void setMaxStatusLineGarbage (final int maxStatusLineGarbage) {
         params.setIntParameter(ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE, maxStatusLineGarbage);
diff --git a/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerPNames.java b/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerPNames.java
index f0eaeb3..375838b 100644
--- a/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerPNames.java
+++ b/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerPNames.java
@@ -26,15 +26,14 @@
 
 package org.apache.http.conn.params;
 
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.params.CoreConnectionPNames;
-
 /**
  * Parameter names for connection managers in HttpConn.
  *
  * @since 4.0
- */
- at Deprecated
+ * 
+ * @deprecated (4.1.2) use configuration methods of the specific connection manager implementation.
+*/
+ at Deprecated 
 public interface ConnManagerPNames {
 
     /**
@@ -43,10 +42,7 @@ public interface ConnManagerPNames {
      * {@link org.apache.http.conn.ClientConnectionManager}.
      * <p>
      * This parameter expects a value of type {@link Long}.
-     * <p>
-     * @deprecated use {@link CoreConnectionPNames#CONNECTION_TIMEOUT}
      */
-    @Deprecated
     public static final String TIMEOUT = "http.conn-manager.timeout";
 
     /**
@@ -56,10 +52,7 @@ public interface ConnManagerPNames {
      * <p>
      * This parameter expects a value of type {@link ConnPerRoute}.
      * <p>
-     * @deprecated use {@link ThreadSafeClientConnManager#setMaxForRoute(org.apache.http.conn.routing.HttpRoute, int)},
-     *  {@link ThreadSafeClientConnManager#getMaxForRoute(org.apache.http.conn.routing.HttpRoute)}
      */
-    @Deprecated
     public static final String MAX_CONNECTIONS_PER_ROUTE = "http.conn-manager.max-per-route";
 
     /**
@@ -68,11 +61,7 @@ public interface ConnManagerPNames {
      * and applies to individual manager instances.
      * <p>
      * This parameter expects a value of type {@link Integer}.
-     * <p>
-     * @deprecated use {@link ThreadSafeClientConnManager#setMaxTotal(int)},
-     *  {@link ThreadSafeClientConnManager#getMaxTotal()}
      */
-    @Deprecated
     public static final String MAX_TOTAL_CONNECTIONS = "http.conn-manager.max-total";
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerParamBean.java b/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerParamBean.java
index ecd8547..953af49 100644
--- a/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerParamBean.java
+++ b/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerParamBean.java
@@ -38,9 +38,11 @@ import org.apache.http.params.HttpParams;
  * using Java Beans conventions.
  *
  * @since 4.0
+ * 
+ * @deprecated (4.1.2) use configuration methods of the specific connection manager implementation.
  */
 @NotThreadSafe
- at Deprecated
+ at Deprecated 
 public class ConnManagerParamBean extends HttpAbstractParamBean {
 
     public ConnManagerParamBean (final HttpParams params) {
@@ -51,14 +53,10 @@ public class ConnManagerParamBean extends HttpAbstractParamBean {
         params.setLongParameter(ConnManagerPNames.TIMEOUT, timeout);
     }
 
-    /** @see ConnManagerPNames#MAX_TOTAL_CONNECTIONS */
-    @Deprecated
     public void setMaxTotalConnections (final int maxConnections) {
         params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, maxConnections);
     }
 
-    /** @see ConnManagerPNames#MAX_CONNECTIONS_PER_ROUTE */
-    @Deprecated
     public void setConnectionsPerRoute(final ConnPerRouteBean connPerRoute) {
         params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, connPerRoute);
     }
diff --git a/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerParams.java b/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerParams.java
index 58eac19..bcb9a2d 100644
--- a/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerParams.java
+++ b/httpclient/src/main/java/org/apache/http/conn/params/ConnManagerParams.java
@@ -29,7 +29,6 @@ package org.apache.http.conn.params;
 import org.apache.http.annotation.Immutable;
 
 import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
 
@@ -40,8 +39,10 @@ import org.apache.http.params.HttpParams;
  * @since 4.0
  *
  * @see ConnManagerPNames
+ * 
+ * @deprecated (4.1.2) use configuration methods of the specific connection manager implementation.
  */
- at Deprecated
+ at Deprecated 
 @Immutable
 public final class ConnManagerParams implements ConnManagerPNames {
 
@@ -55,9 +56,8 @@ public final class ConnManagerParams implements ConnManagerPNames {
      *
      * @return timeout in milliseconds.
      *
-     * @deprecated use {@link HttpConnectionParams#getConnectionTimeout(HttpParams)}
+     * @deprecated (4.1)  use {@link HttpConnectionParams#getConnectionTimeout(HttpParams)}
      */
-    @Deprecated
     public static long getTimeout(final HttpParams params) {
         if (params == null) {
             throw new IllegalArgumentException("HTTP parameters may not be null");
@@ -72,9 +72,8 @@ public final class ConnManagerParams implements ConnManagerPNames {
      *
      * @param timeout the timeout in milliseconds
      *
-     * @deprecated use {@link HttpConnectionParams#setConnectionTimeout(HttpParams, int)}
+     * @deprecated (4.1)  use {@link HttpConnectionParams#setConnectionTimeout(HttpParams, int)}
      */
-    @Deprecated
     public static void setTimeout(final HttpParams params, long timeout) {
         if (params == null) {
             throw new IllegalArgumentException("HTTP parameters may not be null");
@@ -97,10 +96,7 @@ public final class ConnManagerParams implements ConnManagerPNames {
      * @param params HTTP parameters
      * @param connPerRoute lookup interface for maximum number of connections allowed
      *        per route
-     *
-     * @deprecated use {@link ThreadSafeClientConnManager#setMaxForRoute(org.apache.http.conn.routing.HttpRoute, int)}
      */
-    @Deprecated
     public static void setMaxConnectionsPerRoute(final HttpParams params,
                                                 final ConnPerRoute connPerRoute) {
         if (params == null) {
@@ -116,10 +112,7 @@ public final class ConnManagerParams implements ConnManagerPNames {
      * @param params HTTP parameters
      *
      * @return lookup interface for maximum number of connections allowed per route.
-     *
-     * @deprecated use {@link ThreadSafeClientConnManager#getMaxForRoute(org.apache.http.conn.routing.HttpRoute)}
      */
-    @Deprecated
     public static ConnPerRoute getMaxConnectionsPerRoute(final HttpParams params) {
         if (params == null) {
             throw new IllegalArgumentException
@@ -137,10 +130,7 @@ public final class ConnManagerParams implements ConnManagerPNames {
      *
      * @param params HTTP parameters
      * @param maxTotalConnections The maximum number of connections allowed.
-     *
-     * @deprecated use {@link ThreadSafeClientConnManager#setMaxTotal(int)}
      */
-    @Deprecated
     public static void setMaxTotalConnections(
             final HttpParams params,
             int maxTotalConnections) {
@@ -157,10 +147,7 @@ public final class ConnManagerParams implements ConnManagerPNames {
      * @param params HTTP parameters
      *
      * @return The maximum number of connections allowed.
-     *
-     * @deprecated use {@link ThreadSafeClientConnManager#getMaxTotal()}
      */
-    @Deprecated
     public static int getMaxTotalConnections(
             final HttpParams params) {
         if (params == null) {
diff --git a/httpclient/src/main/java/org/apache/http/conn/params/ConnPerRouteBean.java b/httpclient/src/main/java/org/apache/http/conn/params/ConnPerRouteBean.java
index 917c41c..be49a53 100644
--- a/httpclient/src/main/java/org/apache/http/conn/params/ConnPerRouteBean.java
+++ b/httpclient/src/main/java/org/apache/http/conn/params/ConnPerRouteBean.java
@@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import org.apache.http.annotation.ThreadSafe;
 
 import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.pool.ConnPoolControl;
 
 /**
  * This class maintains a map of HTTP routes to maximum number of connections allowed
@@ -40,7 +41,10 @@ import org.apache.http.conn.routing.HttpRoute;
  * a fine-grained control of connections on a per route basis.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link ConnPoolControl}
  */
+ at Deprecated 
 @ThreadSafe
 public final class ConnPerRouteBean implements ConnPerRoute {
 
@@ -61,10 +65,6 @@ public final class ConnPerRouteBean implements ConnPerRoute {
         this(DEFAULT_MAX_CONNECTIONS_PER_ROUTE);
     }
 
-    /**
-     * @deprecated Use {@link #getDefaultMaxPerRoute()} instead
-     */
-    @Deprecated
     public int getDefaultMax() {
         return this.defaultMax;
     }
diff --git a/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java b/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java
index 7db7c16..511f424 100644
--- a/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java
+++ b/httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java
@@ -334,7 +334,7 @@ public final class HttpRoute implements RouteInfo, Cloneable {
         if (this == obj) return true;
         if (obj instanceof HttpRoute) {
             HttpRoute that = (HttpRoute) obj;
-            return 
+            return
                 // Do the cheapest tests first
                 (this.secure    == that.secure) &&
                 (this.tunnelled == that.tunnelled) &&
@@ -376,8 +376,6 @@ public final class HttpRoute implements RouteInfo, Cloneable {
     @Override
     public final String toString() {
         StringBuilder cab = new StringBuilder(50 + getHopCount()*30);
-
-        cab.append("HttpRoute[");
         if (this.localAddress != null) {
             cab.append(this.localAddress);
             cab.append("->");
@@ -395,8 +393,6 @@ public final class HttpRoute implements RouteInfo, Cloneable {
             cab.append("->");
         }
         cab.append(this.targetHost);
-        cab.append(']');
-
         return cab.toString();
     }
 
diff --git a/httpclient/src/main/java/org/apache/http/conn/routing/RouteTracker.java b/httpclient/src/main/java/org/apache/http/conn/routing/RouteTracker.java
index dcf227d..ee9f324 100644
--- a/httpclient/src/main/java/org/apache/http/conn/routing/RouteTracker.java
+++ b/httpclient/src/main/java/org/apache/http/conn/routing/RouteTracker.java
@@ -87,6 +87,16 @@ public final class RouteTracker implements RouteInfo, Cloneable {
         this.layered      = LayerType.PLAIN;
     }
 
+    /**
+     * @since 4.2
+     */
+    public void reset() {
+        this.connected = false;
+        this.proxyChain = null;
+        this.tunnelled = TunnelType.PLAIN;
+        this.layered = LayerType.PLAIN;
+        this.secure = false;
+    }
 
     /**
      * Creates a new tracker for the given route.
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/HostNameResolver.java b/httpclient/src/main/java/org/apache/http/conn/scheme/HostNameResolver.java
index d325f42..b2f2f88 100644
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/HostNameResolver.java
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/HostNameResolver.java
@@ -35,9 +35,9 @@ import java.net.InetAddress;
  *
  * @since 4.0
  *
- * @deprecated Do not use
+ * @deprecated (4.1)  Do not use
  */
- at Deprecated
+ at Deprecated 
 public interface HostNameResolver {
 
     /**
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSchemeSocketFactory.java b/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSchemeSocketFactory.java
index 9bc57c9..4a01bd1 100644
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSchemeSocketFactory.java
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSchemeSocketFactory.java
@@ -32,11 +32,13 @@ import java.net.Socket;
 import java.net.UnknownHostException;
 
 /**
- * A {@link SocketFactory SocketFactory} for layered sockets (SSL/TLS).
- * See there for things to consider when implementing a socket factory.
+ * Extended {@link SchemeSocketFactory} interface for layered sockets such as SSL/TLS.
  *
  * @since 4.1
+ * 
+ * @deprecated (4.2)  use {@link SchemeLayeredSocketFactory}
  */
+ at Deprecated
 public interface LayeredSchemeSocketFactory extends SchemeSocketFactory {
 
     /**
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSchemeSocketFactoryAdaptor.java b/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSchemeSocketFactoryAdaptor.java
deleted file mode 100644
index 29a649a..0000000
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSchemeSocketFactoryAdaptor.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.conn.scheme;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.net.UnknownHostException;
-
- at Deprecated
-class LayeredSchemeSocketFactoryAdaptor extends SchemeSocketFactoryAdaptor
-    implements LayeredSchemeSocketFactory {
-
-    private final LayeredSocketFactory factory;
-
-    LayeredSchemeSocketFactoryAdaptor(final LayeredSocketFactory factory) {
-        super(factory);
-        this.factory = factory;
-    }
-
-    public Socket createLayeredSocket(
-            final Socket socket,
-            final String target, int port,
-            boolean autoClose) throws IOException, UnknownHostException {
-        return this.factory.createSocket(socket, target, port, autoClose);
-    }
-
-}
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactory.java b/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactory.java
index 3dd7581..8cc1bc9 100644
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactory.java
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactory.java
@@ -37,9 +37,9 @@ import java.net.UnknownHostException;
  *
  * @since 4.0
  *
- * @deprecated use {@link SchemeSocketFactory}
+ * @deprecated (4.1)  use {@link SchemeSocketFactory}
  */
- at Deprecated
+ at Deprecated 
 public interface LayeredSocketFactory extends SocketFactory {
 
     /**
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactoryAdaptor.java b/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactoryAdaptor.java
index 7843435..f761778 100644
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactoryAdaptor.java
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/LayeredSocketFactoryAdaptor.java
@@ -31,6 +31,9 @@ import java.io.IOException;
 import java.net.Socket;
 import java.net.UnknownHostException;
 
+/**
+ * @deprecated (4.1) do not use
+ */
 @Deprecated
 class LayeredSocketFactoryAdaptor extends SocketFactoryAdaptor implements LayeredSocketFactory {
 
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/PlainSocketFactory.java b/httpclient/src/main/java/org/apache/http/conn/scheme/PlainSocketFactory.java
index 544ce08..72f7279 100644
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/PlainSocketFactory.java
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/PlainSocketFactory.java
@@ -37,6 +37,7 @@ import java.net.UnknownHostException;
 import org.apache.http.annotation.Immutable;
 
 import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.conn.DnsResolver;
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
 
@@ -67,6 +68,9 @@ public class PlainSocketFactory implements SocketFactory, SchemeSocketFactory {
         return new PlainSocketFactory();
     }
 
+    /**
+     * @deprecated (4.1) use {@link DnsResolver}
+     */
     @Deprecated
     public PlainSocketFactory(final HostNameResolver nameResolver) {
         super();
@@ -122,8 +126,7 @@ public class PlainSocketFactory implements SocketFactory, SchemeSocketFactory {
             sock.setSoTimeout(soTimeout);
             sock.connect(remoteAddress, connTimeout);
         } catch (SocketTimeoutException ex) {
-            throw new ConnectTimeoutException("Connect to " + remoteAddress.getHostName() + "/"
-                    + remoteAddress.getAddress() + " timed out");
+            throw new ConnectTimeoutException("Connect to " + remoteAddress + " timed out");
         }
         return sock;
     }
@@ -154,7 +157,7 @@ public class PlainSocketFactory implements SocketFactory, SchemeSocketFactory {
     }
 
     /**
-     * @deprecated Use {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)}
+     * @deprecated (4.1)  Use {@link #connectSocket(Socket, InetSocketAddress, InetSocketAddress, HttpParams)}
      */
     @Deprecated
     public Socket connectSocket(
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/Scheme.java b/httpclient/src/main/java/org/apache/http/conn/scheme/Scheme.java
index ec20040..c022639 100644
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/Scheme.java
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/Scheme.java
@@ -81,6 +81,7 @@ public final class Scheme {
      *
      * @since 4.1
      */
+    @SuppressWarnings("deprecation")
     public Scheme(final String name, final int port, final SchemeSocketFactory factory) {
         if (name == null) {
             throw new IllegalArgumentException("Scheme name may not be null");
@@ -92,9 +93,17 @@ public final class Scheme {
             throw new IllegalArgumentException("Socket factory may not be null");
         }
         this.name = name.toLowerCase(Locale.ENGLISH);
-        this.socketFactory = factory;
         this.defaultPort = port;
-        this.layered = factory instanceof LayeredSchemeSocketFactory;
+        if (factory instanceof SchemeLayeredSocketFactory) {
+            this.layered = true;
+            this.socketFactory = factory;
+        } else if (factory instanceof LayeredSchemeSocketFactory) {
+            this.layered = true;
+            this.socketFactory = new SchemeLayeredSocketFactoryAdaptor2((LayeredSchemeSocketFactory) factory);
+        } else {
+            this.layered = false;
+            this.socketFactory = factory;
+        }
     }
 
     /**
@@ -108,9 +117,9 @@ public final class Scheme {
      *                  with this scheme
      * @param port      the default port for this scheme
      *
-     * @deprecated Use {@link #Scheme(String, int, SchemeSocketFactory)}
+     * @deprecated (4.1)  Use {@link #Scheme(String, int, SchemeSocketFactory)}
      */
-    @Deprecated
+    @Deprecated 
     public Scheme(final String name,
                   final SocketFactory factory,
                   final int port) {
@@ -130,7 +139,7 @@ public final class Scheme {
 
         this.name = name.toLowerCase(Locale.ENGLISH);
         if (factory instanceof LayeredSocketFactory) {
-            this.socketFactory = new LayeredSchemeSocketFactoryAdaptor(
+            this.socketFactory = new SchemeLayeredSocketFactoryAdaptor(
                     (LayeredSocketFactory) factory);
             this.layered = true;
         } else {
@@ -157,7 +166,7 @@ public final class Scheme {
      *
      * @return  the socket factory for this scheme
      *
-     * @deprecated Use {@link #getSchemeSocketFactory()}
+     * @deprecated (4.1)  Use {@link #getSchemeSocketFactory()}
      */
     @Deprecated
     public final SocketFactory getSocketFactory() {
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeLayeredSocketFactory.java b/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeLayeredSocketFactory.java
new file mode 100644
index 0000000..7ede8cd
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeLayeredSocketFactory.java
@@ -0,0 +1,65 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.scheme;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * Extended {@link SchemeSocketFactory} interface for layered sockets such as SSL/TLS.
+ *
+ * @since 4.2
+ */
+public interface SchemeLayeredSocketFactory extends SchemeSocketFactory {
+
+    /**
+     * Returns a socket connected to the given host that is layered over an
+     * existing socket.  Used primarily for creating secure sockets through
+     * proxies.
+     *
+     * @param socket the existing socket
+     * @param target    the name of the target host.
+     * @param port      the port to connect to on the target host
+     * @param params    HTTP parameters
+     *
+     * @return Socket a new socket
+     *
+     * @throws IOException if an I/O error occurs while creating the socket
+     * @throws UnknownHostException if the IP address of the host cannot be
+     * determined
+     */
+    Socket createLayeredSocket(
+        Socket socket,
+        String target,
+        int port,
+        HttpParams params) throws IOException, UnknownHostException;
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeLayeredSocketFactoryAdaptor.java b/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeLayeredSocketFactoryAdaptor.java
new file mode 100644
index 0000000..38d45e2
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeLayeredSocketFactoryAdaptor.java
@@ -0,0 +1,57 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.scheme;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.apache.http.params.HttpParams;
+
+/**
+ * @deprecated (4.2) do not use
+ */
+ at Deprecated
+class SchemeLayeredSocketFactoryAdaptor extends SchemeSocketFactoryAdaptor
+    implements SchemeLayeredSocketFactory {
+
+    private final LayeredSocketFactory factory;
+
+    SchemeLayeredSocketFactoryAdaptor(final LayeredSocketFactory factory) {
+        super(factory);
+        this.factory = factory;
+    }
+
+    public Socket createLayeredSocket(
+            final Socket socket,
+            final String target, int port,
+            final HttpParams params) throws IOException, UnknownHostException {
+        return this.factory.createSocket(socket, target, port, true);
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeLayeredSocketFactoryAdaptor2.java b/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeLayeredSocketFactoryAdaptor2.java
new file mode 100644
index 0000000..12c1577
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeLayeredSocketFactoryAdaptor2.java
@@ -0,0 +1,74 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.conn.scheme;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.params.HttpParams;
+
+/**
+ * @deprecated (4.2) do not use
+ */
+ at Deprecated 
+class SchemeLayeredSocketFactoryAdaptor2 implements SchemeLayeredSocketFactory {
+
+    private final LayeredSchemeSocketFactory factory;
+
+    SchemeLayeredSocketFactoryAdaptor2(final LayeredSchemeSocketFactory factory) {
+        super();
+        this.factory = factory;
+    }
+
+    public Socket createSocket(final HttpParams params) throws IOException {
+        return this.factory.createSocket(params);
+    }
+
+    public Socket connectSocket(
+            final Socket sock, 
+            final InetSocketAddress remoteAddress,
+            final InetSocketAddress localAddress, 
+            final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
+        return this.factory.connectSocket(sock, remoteAddress, localAddress, params);
+    }
+
+    public boolean isSecure(Socket sock) throws IllegalArgumentException {
+        return this.factory.isSecure(sock);
+    }
+
+    public Socket createLayeredSocket(
+            final Socket socket,
+            final String target, int port,
+            final HttpParams params) throws IOException, UnknownHostException {
+        return this.factory.createLayeredSocket(socket, target, port, true);
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeSocketFactory.java b/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeSocketFactory.java
index f45bc09..43fca33 100644
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeSocketFactory.java
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeSocketFactory.java
@@ -32,7 +32,9 @@ import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.UnknownHostException;
 
+import org.apache.http.HttpHost;
 import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.conn.HttpInetSocketAddress;
 import org.apache.http.params.HttpParams;
 
 /**
@@ -63,12 +65,17 @@ public interface SchemeSocketFactory {
 
     /**
      * Connects a socket to the target host with the given remote address.
+     * <p/>
+     * Please note that {@link HttpInetSocketAddress} class should be used in order to pass
+     * the target remote address along with the original {@link HttpHost} value used to resolve
+     * the address. The use of {@link HttpInetSocketAddress} can also ensure that no reverse
+     * DNS lookup will be performed if the target remote address was specified as an IP address.
      *
      * @param sock      the socket to connect, as obtained from
      *                  {@link #createSocket(HttpParams) createSocket}.
      *                  <code>null</code> indicates that a new socket
      *                  should be created and connected.
-     * @param remoteAddress the remote address to connect to
+     * @param remoteAddress the remote address to connect to.
      * @param localAddress the local address to bind the socket to, or
      *                  <code>null</code> for any
      * @param params    additional {@link HttpParams parameters} for connecting
@@ -82,6 +89,8 @@ public interface SchemeSocketFactory {
      *          can not be determined
      * @throws ConnectTimeoutException if the socket cannot be connected
      *          within the time limit defined in the <code>params</code>
+     *
+     * @see HttpInetSocketAddress
      */
     Socket connectSocket(
         Socket sock,
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeSocketFactoryAdaptor.java b/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeSocketFactoryAdaptor.java
index 077e664..8aeac0f 100644
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeSocketFactoryAdaptor.java
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/SchemeSocketFactoryAdaptor.java
@@ -36,6 +36,9 @@ import java.net.UnknownHostException;
 import org.apache.http.conn.ConnectTimeoutException;
 import org.apache.http.params.HttpParams;
 
+/**
+ * @deprecated (4.1) do not use
+ */
 @Deprecated
 class SchemeSocketFactoryAdaptor implements SchemeSocketFactory {
 
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/SocketFactory.java b/httpclient/src/main/java/org/apache/http/conn/scheme/SocketFactory.java
index f086a44..99cbf50 100644
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/SocketFactory.java
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/SocketFactory.java
@@ -41,7 +41,7 @@ import org.apache.http.params.HttpParams;
  *
  * @since 4.0
  *
- * @deprecated use {@link SchemeSocketFactory}
+ * @deprecated (4.1)  use {@link SchemeSocketFactory}
  */
 @Deprecated
 public interface SocketFactory {
diff --git a/httpclient/src/main/java/org/apache/http/conn/scheme/SocketFactoryAdaptor.java b/httpclient/src/main/java/org/apache/http/conn/scheme/SocketFactoryAdaptor.java
index f8f0017..1716189 100644
--- a/httpclient/src/main/java/org/apache/http/conn/scheme/SocketFactoryAdaptor.java
+++ b/httpclient/src/main/java/org/apache/http/conn/scheme/SocketFactoryAdaptor.java
@@ -37,6 +37,9 @@ import org.apache.http.conn.ConnectTimeoutException;
 import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.HttpParams;
 
+/**
+ * @deprecated (4.1) do not use
+ */
 @Deprecated
 class SocketFactoryAdaptor implements SocketFactory {
 
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java b/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java
index 547204a..79a6a6c 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java
+++ b/httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java
@@ -197,13 +197,21 @@ public abstract class AbstractVerifier implements X509HostnameVerifier {
             // The CN better have at least two dots if it wants wildcard
             // action.  It also can't be [*.co.uk] or [*.co.jp] or
             // [*.org.uk], etc...
-            boolean doWildcard = cn.startsWith("*.") &&
-                                 cn.lastIndexOf('.') >= 0 &&
+            String parts[] = cn.split("\\.");
+            boolean doWildcard = parts.length >= 3 &&
+                                 parts[0].endsWith("*") &&
                                  acceptableCountryWildcard(cn) &&
                                  !isIPAddress(host);
 
             if(doWildcard) {
-                match = hostName.endsWith(cn.substring(1));
+                if (parts[0].length() > 1) { // e.g. server*
+                    String prefix = parts[0].substring(0, parts.length-2); // e.g. server
+                    String suffix = cn.substring(parts[0].length()); // skip wildcard part from cn
+                    String hostSuffix = hostName.substring(prefix.length()); // skip wildcard part from host
+                    match = hostName.startsWith(prefix) && hostSuffix.endsWith(suffix);
+                } else {
+                    match = hostName.endsWith(cn.substring(1));
+                }
                 if(match && strictWithSubDomains) {
                     // If we're in strict mode, then [*.foo.com] is not
                     // allowed to match [a.b.foo.com]
@@ -222,18 +230,11 @@ public abstract class AbstractVerifier implements X509HostnameVerifier {
     }
 
     public static boolean acceptableCountryWildcard(String cn) {
-        int cnLen = cn.length();
-        if(cnLen >= 7 && cnLen <= 9) {
-            // Look for the '.' in the 3rd-last position:
-            if(cn.charAt(cnLen - 3) == '.') {
-                // Trim off the [*.] and the [.XX].
-                String s = cn.substring(2, cnLen - 3);
-                // And test against the sorted array of bad 2lds:
-                int x = Arrays.binarySearch(BAD_COUNTRY_2LDS, s);
-                return x < 0;
-            }
+        String parts[] = cn.split("\\.");
+        if (parts.length != 3 || parts[2].length() != 2) {
+            return true; // it's not an attempt to wildcard a 2TLD within a country code
         }
-        return true;
+        return Arrays.binarySearch(BAD_COUNTRY_2LDS, parts[1]) < 0;
     }
 
     public static String[] getCNs(X509Certificate cert) {
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/SSLInitializationException.java b/httpclient/src/main/java/org/apache/http/conn/ssl/SSLInitializationException.java
new file mode 100644
index 0000000..defd7e1
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/conn/ssl/SSLInitializationException.java
@@ -0,0 +1,37 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.conn.ssl;
+
+public class SSLInitializationException extends IllegalStateException {
+
+    private static final long serialVersionUID = -8243587425648536702L;
+
+    public SSLInitializationException(final String message, final Throwable cause) {
+        super(message, cause);
+    }    
+    
+}
diff --git a/httpclient/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java b/httpclient/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java
index 38b6141..632a04f 100644
--- a/httpclient/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java
+++ b/httpclient/src/main/java/org/apache/http/conn/ssl/SSLSocketFactory.java
@@ -27,12 +27,15 @@
 
 package org.apache.http.conn.ssl;
 
+import org.apache.http.HttpHost;
 import org.apache.http.annotation.ThreadSafe;
 
 import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.conn.HttpInetSocketAddress;
 import org.apache.http.conn.scheme.HostNameResolver;
 import org.apache.http.conn.scheme.LayeredSchemeSocketFactory;
 import org.apache.http.conn.scheme.LayeredSocketFactory;
+import org.apache.http.conn.scheme.SchemeLayeredSocketFactory;
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
 
@@ -44,6 +47,8 @@ import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
 
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -54,8 +59,10 @@ import java.security.KeyManagementException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
 import java.security.SecureRandom;
 import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
 
 /**
  * Layered socket factory for TLS/SSL connections.
@@ -140,7 +147,8 @@ import java.security.UnrecoverableKeyException;
  */
 @SuppressWarnings("deprecation")
 @ThreadSafe
-public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSocketFactory {
+public class SSLSocketFactory implements SchemeLayeredSocketFactory,
+                                         LayeredSchemeSocketFactory, LayeredSocketFactory {
 
     public static final String TLS   = "TLS";
     public static final String SSL   = "SSL";
@@ -155,14 +163,45 @@ public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSock
     public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER
         = new StrictHostnameVerifier();
 
+    private final static char[] EMPTY_PASSWORD = "".toCharArray();
+
     /**
-     * Gets the default factory, which uses the default JVM settings for secure
-     * connections.
+     * Gets the default factory, which uses the default JSSE settings for initializing
+     * the SSL context.
      *
-     * @return the default factory
+     * @return the default SSL socket factory
      */
-    public static SSLSocketFactory getSocketFactory() {
-        return new SSLSocketFactory();
+    public static SSLSocketFactory getSocketFactory() throws SSLInitializationException {
+        return new SSLSocketFactory(createDefaultSSLContext());
+    }
+
+    /**
+     * Gets the default factory, which uses system properties for initializing the SSL context
+     * as described in
+     * <a href="http://download.oracle.com/javase/1,5.0/docs/guide/security/jsse/JSSERefGuide.html">
+     * "JavaTM Secure Socket Extension (JSSE) Reference Guide for the JavaTM 2 Platform
+     * Standard Edition 5</a>
+     * <p>
+     * The following system properties are taken into account by this method:
+     * <ul>
+     *  <li>ssl.TrustManagerFactory.algorithm</li>
+     *  <li>javax.net.ssl.trustStoreType</li>
+     *  <li>javax.net.ssl.trustStore</li>
+     *  <li>javax.net.ssl.trustStoreProvider</li>
+     *  <li>javax.net.ssl.trustStorePassword</li>
+     *  <li>java.home</li>
+     *  <li>ssl.KeyManagerFactory.algorithm</li>
+     *  <li>javax.net.ssl.keyStoreType</li>
+     *  <li>javax.net.ssl.keyStore</li>
+     *  <li>javax.net.ssl.keyStoreProvider</li>
+     *  <li>javax.net.ssl.keyStorePassword</li>
+     * </ul>
+     * <p>
+     *
+     * @return the system SSL socket factory
+     */
+    public static SSLSocketFactory getSystemSocketFactory() throws SSLInitializationException {
+        return new SSLSocketFactory(createSystemSSLContext());
     }
 
     private final javax.net.ssl.SSLSocketFactory socketfactory;
@@ -203,12 +242,130 @@ public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSock
         sslcontext.init(keymanagers, trustmanagers, random);
         return sslcontext;
     }
+    
+    private static SSLContext createSystemSSLContext(
+            String algorithm,
+            final SecureRandom random) throws IOException, NoSuchAlgorithmException, NoSuchProviderException,
+            KeyStoreException, CertificateException, UnrecoverableKeyException, KeyManagementException {
+        if (algorithm == null) {
+            algorithm = TLS;
+        }
+        TrustManagerFactory tmfactory = null;
+
+        String trustAlgorithm = System.getProperty("ssl.TrustManagerFactory.algorithm");
+        if (trustAlgorithm == null) {
+            trustAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
+        }
+        String trustStoreType = System.getProperty("javax.net.ssl.trustStoreType");
+        if (trustStoreType == null) {
+            trustStoreType = KeyStore.getDefaultType();
+        }
+        if ("none".equalsIgnoreCase(trustStoreType)) {
+            tmfactory = TrustManagerFactory.getInstance(trustAlgorithm);
+        } else {
+            File trustStoreFile = null;
+            String s = System.getProperty("javax.net.ssl.trustStore");
+            if (s != null) {
+                trustStoreFile = new File(s);
+                tmfactory = TrustManagerFactory.getInstance(trustAlgorithm);
+                String trustStoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider");
+                KeyStore trustStore;
+                if (trustStoreProvider != null) {
+                    trustStore = KeyStore.getInstance(trustStoreType, trustStoreProvider);
+                } else {
+                    trustStore = KeyStore.getInstance(trustStoreType);
+                }
+                String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
+                FileInputStream instream = new FileInputStream(trustStoreFile);
+                try {
+                    trustStore.load(instream, trustStorePassword != null ?
+                            trustStorePassword.toCharArray() : EMPTY_PASSWORD);
+                } finally {
+                    instream.close();
+                }
+                tmfactory.init(trustStore);
+            } else {
+                File javaHome = new File(System.getProperty("java.home"));
+                File file = new File(javaHome, "lib/security/jssecacerts");
+                if (!file.exists()) {
+                    file = new File(javaHome, "lib/security/cacerts");
+                    trustStoreFile = file;
+                } else {
+                    trustStoreFile = file;
+                }
+                tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+                KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
+                String trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword");
+                FileInputStream instream = new FileInputStream(trustStoreFile);
+                try {
+                    trustStore.load(instream, trustStorePassword != null ? trustStorePassword.toCharArray() : null);
+                } finally {
+                    instream.close();
+                }
+                tmfactory.init(trustStore);
+            }
+        }
+
+        KeyManagerFactory kmfactory = null;
+        String keyAlgorithm = System.getProperty("ssl.KeyManagerFactory.algorithm");
+        if (keyAlgorithm == null) {
+            keyAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
+        }
+        String keyStoreType = System.getProperty("javax.net.ssl.keyStoreType");
+        if (keyStoreType == null) {
+            keyStoreType = KeyStore.getDefaultType();
+        }
+        if ("none".equalsIgnoreCase(keyStoreType)) {
+            kmfactory = KeyManagerFactory.getInstance(keyAlgorithm);
+        } else {
+            File keyStoreFile = null;
+            String s = System.getProperty("javax.net.ssl.keyStore");
+            if (s != null) {
+                keyStoreFile = new File(s);
+            }
+            if (keyStoreFile != null) {
+                kmfactory = KeyManagerFactory.getInstance(keyAlgorithm);
+                String keyStoreProvider = System.getProperty("javax.net.ssl.keyStoreProvider");
+                KeyStore keyStore;
+                if (keyStoreProvider != null) {
+                    keyStore = KeyStore.getInstance(keyStoreType, keyStoreProvider);
+                } else {
+                    keyStore = KeyStore.getInstance(keyStoreType);
+                }
+                String keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
+                FileInputStream instream = new FileInputStream(keyStoreFile);
+                try {
+                    keyStore.load(instream, keyStorePassword != null ?
+                            keyStorePassword.toCharArray() : EMPTY_PASSWORD);
+                } finally {
+                    instream.close();
+                }
+                kmfactory.init(keyStore, keyStorePassword != null ?
+                        keyStorePassword.toCharArray() : EMPTY_PASSWORD);
+            }
+        }
+
+        SSLContext sslcontext = SSLContext.getInstance(algorithm);
+        sslcontext.init(
+                kmfactory != null ? kmfactory.getKeyManagers() : null,
+                tmfactory != null ? tmfactory.getTrustManagers() : null,
+                random);
+        return sslcontext;
+    }
 
-    private static SSLContext createDefaultSSLContext() {
+    private static SSLContext createDefaultSSLContext() throws SSLInitializationException {
         try {
             return createSSLContext(TLS, null, null, null, null, null);
         } catch (Exception ex) {
-            throw new IllegalStateException("Failure initializing default SSL context", ex);
+            throw new SSLInitializationException("Failure initializing default SSL context", ex);
+        }
+    }
+
+    private static SSLContext createSystemSSLContext() throws SSLInitializationException {
+        try {
+            return createSystemSSLContext(TLS, null);
+        } catch (Exception ex) {
+            throw new SSLInitializationException("Failure initializing default system SSL context", ex);
         }
     }
 
@@ -324,13 +481,26 @@ public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSock
     public SSLSocketFactory(
             final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) {
         super();
+        if (sslContext == null) {
+            throw new IllegalArgumentException("SSL context may not be null");
+        }
         this.socketfactory = sslContext.getSocketFactory();
         this.hostnameVerifier = hostnameVerifier;
         this.nameResolver = null;
     }
 
-    private SSLSocketFactory() {
-        this(createDefaultSSLContext());
+    /**
+     * @since 4.2
+     */
+    public SSLSocketFactory(
+            final javax.net.ssl.SSLSocketFactory socketfactory, 
+            final X509HostnameVerifier hostnameVerifier) {
+        if (socketfactory == null) {
+            throw new IllegalArgumentException("SSL socket factory may not be null");
+        }
+        this.socketfactory = socketfactory;
+        this.hostnameVerifier = hostnameVerifier;
+        this.nameResolver = null;
     }
 
     /**
@@ -339,12 +509,16 @@ public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSock
      * @since 4.1
      */
     public Socket createSocket(final HttpParams params) throws IOException {
-        return this.socketfactory.createSocket();
+        SSLSocket sock = (SSLSocket) this.socketfactory.createSocket();
+        prepareSocket(sock);
+        return sock;
     }
 
     @Deprecated
     public Socket createSocket() throws IOException {
-        return this.socketfactory.createSocket();
+        SSLSocket sock = (SSLSocket) this.socketfactory.createSocket();
+        prepareSocket(sock);
+        return sock;
     }
 
     /**
@@ -361,7 +535,7 @@ public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSock
         if (params == null) {
             throw new IllegalArgumentException("HTTP parameters may not be null");
         }
-        Socket sock = socket != null ? socket : new Socket();
+        Socket sock = socket != null ? socket : this.socketfactory.createSocket();
         if (localAddress != null) {
             sock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params));
             sock.bind(localAddress);
@@ -374,20 +548,28 @@ public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSock
             sock.setSoTimeout(soTimeout);
             sock.connect(remoteAddress, connTimeout);
         } catch (SocketTimeoutException ex) {
-            throw new ConnectTimeoutException("Connect to " + remoteAddress.getHostName() + "/"
-                    + remoteAddress.getAddress() + " timed out");
+            throw new ConnectTimeoutException("Connect to " + remoteAddress + " timed out");
         }
+
+        String hostname;
+        if (remoteAddress instanceof HttpInetSocketAddress) {
+            hostname = ((HttpInetSocketAddress) remoteAddress).getHttpHost().getHostName();
+        } else {
+            hostname = remoteAddress.getHostName();
+        }
+
         SSLSocket sslsock;
         // Setup SSL layering if necessary
         if (sock instanceof SSLSocket) {
             sslsock = (SSLSocket) sock;
         } else {
-            sslsock = (SSLSocket) this.socketfactory.createSocket(
-                    sock, remoteAddress.getHostName(), remoteAddress.getPort(), true);
+            int port = remoteAddress.getPort();
+            sslsock = (SSLSocket) this.socketfactory.createSocket(sock, hostname, port, true);
+            prepareSocket(sslsock);
         }
         if (this.hostnameVerifier != null) {
             try {
-                this.hostnameVerifier.verify(remoteAddress.getHostName(), sslsock);
+                this.hostnameVerifier.verify(hostname, sslsock);
                 // verifyHostName() didn't blowup - good!
             } catch (IOException iox) {
                 // close the socket before re-throwing the exception
@@ -429,7 +611,28 @@ public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSock
     }
 
     /**
-     * @since 4.1
+     * @since 4.2
+     */
+    public Socket createLayeredSocket(
+        final Socket socket,
+        final String host,
+        final int port,
+        final HttpParams params) throws IOException, UnknownHostException {
+        SSLSocket sslSocket = (SSLSocket) this.socketfactory.createSocket(
+              socket,
+              host,
+              port,
+              true);
+        prepareSocket(sslSocket);
+        if (this.hostnameVerifier != null) {
+            this.hostnameVerifier.verify(host, sslSocket);
+        }
+        // verifyHostName() didn't blowup - good!
+        return sslSocket;
+    }
+
+    /**
+     * @deprecated use {@link #createLayeredSocket(Socket, String, int, HttpParams)}
      */
     public Socket createLayeredSocket(
         final Socket socket,
@@ -442,6 +645,7 @@ public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSock
               port,
               autoClose
         );
+        prepareSocket(sslSocket);
         if (this.hostnameVerifier != null) {
             this.hostnameVerifier.verify(host, sslSocket);
         }
@@ -484,7 +688,7 @@ public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSock
         } else {
             remoteAddress = InetAddress.getByName(host);
         }
-        InetSocketAddress remote = new InetSocketAddress(remoteAddress, port);
+        InetSocketAddress remote = new HttpInetSocketAddress(new HttpHost(host, port), remoteAddress, port);
         return connectSocket(socket, remote, local, params);
     }
 
@@ -499,4 +703,15 @@ public class SSLSocketFactory implements LayeredSchemeSocketFactory, LayeredSock
         return createLayeredSocket(socket, host, port, autoClose);
     }
 
+    /**
+     * Performs any custom initialization for a newly created SSLSocket
+     * (before the SSL handshake happens).
+     *
+     * The default implementation is a no-op, but could be overriden to, e.g.,
+     * call {@link SSLSocket#setEnabledCipherSuites(java.lang.String[])}.
+     *
+     * @since 4.2
+     */
+    protected void prepareSocket(final SSLSocket socket) throws IOException {
+    }
 }
diff --git a/httpclient/src/main/java/org/apache/http/cookie/ClientCookie.java b/httpclient/src/main/java/org/apache/http/cookie/ClientCookie.java
index a2db6f8..164d302 100644
--- a/httpclient/src/main/java/org/apache/http/cookie/ClientCookie.java
+++ b/httpclient/src/main/java/org/apache/http/cookie/ClientCookie.java
@@ -59,4 +59,4 @@ public interface ClientCookie extends Cookie {
 
     boolean containsAttribute(String name);
 
-}
\ No newline at end of file
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java b/httpclient/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java
index c790f3e..bc72333 100644
--- a/httpclient/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java
@@ -26,6 +26,8 @@
 
 package org.apache.http.impl.auth;
 
+import java.util.Locale;
+
 import org.apache.http.annotation.NotThreadSafe;
 
 import org.apache.http.FormattedHeader;
@@ -33,6 +35,7 @@ import org.apache.http.Header;
 import org.apache.http.HttpRequest;
 import org.apache.http.auth.AUTH;
 import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.ChallengeState;
 import org.apache.http.auth.ContextAwareAuthScheme;
 import org.apache.http.auth.Credentials;
 import org.apache.http.auth.MalformedChallengeException;
@@ -50,16 +53,24 @@ import org.apache.http.util.CharArrayBuffer;
  *
  * @since 4.0
  */
- at NotThreadSafe // proxy
+ at NotThreadSafe
 public abstract class AuthSchemeBase implements ContextAwareAuthScheme {
 
+    private ChallengeState challengeState;
+
     /**
-     * Flag whether authenticating against a proxy.
+     * Creates an instance of <tt>AuthSchemeBase</tt> with the given challenge
+     * state.
+     *
+     * @since 4.2
      */
-    private boolean proxy;
+    public AuthSchemeBase(final ChallengeState challengeState) {
+        super();
+        this.challengeState = challengeState;
+    }
 
     public AuthSchemeBase() {
-        super();
+        this(null);
     }
 
     /**
@@ -78,9 +89,9 @@ public abstract class AuthSchemeBase implements ContextAwareAuthScheme {
         }
         String authheader = header.getName();
         if (authheader.equalsIgnoreCase(AUTH.WWW_AUTH)) {
-            this.proxy = false;
+            this.challengeState = ChallengeState.TARGET;
         } else if (authheader.equalsIgnoreCase(AUTH.PROXY_AUTH)) {
-            this.proxy = true;
+            this.challengeState = ChallengeState.PROXY;
         } else {
             throw new MalformedChallengeException("Unexpected header name: " + authheader);
         }
@@ -130,17 +141,28 @@ public abstract class AuthSchemeBase implements ContextAwareAuthScheme {
     /**
      * Returns <code>true</code> if authenticating against a proxy, <code>false</code>
      * otherwise.
-     *
-     * @return <code>true</code> if authenticating against a proxy, <code>false</code>
-     * otherwise
      */
     public boolean isProxy() {
-        return this.proxy;
+        return this.challengeState != null && this.challengeState == ChallengeState.PROXY;
+    }
+
+    /**
+     * Returns {@link ChallengeState} value or <code>null</code> if unchallenged.
+     *
+     * @since 4.2
+     */
+    public ChallengeState getChallengeState() {
+        return this.challengeState;
     }
 
     @Override
     public String toString() {
-        return getSchemeName();
+        String name = getSchemeName();
+        if (name != null) {
+            return name.toUpperCase(Locale.US);
+        } else {
+            return super.toString();
+        }
     }
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java
index 91e02cf..0534903 100644
--- a/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java
@@ -32,12 +32,16 @@ import org.apache.commons.codec.binary.Base64;
 import org.apache.http.Header;
 import org.apache.http.HttpRequest;
 import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.ChallengeState;
+import org.apache.http.auth.ContextAwareAuthScheme;
 import org.apache.http.auth.Credentials;
 import org.apache.http.auth.AUTH;
 import org.apache.http.auth.InvalidCredentialsException;
 import org.apache.http.auth.MalformedChallengeException;
 import org.apache.http.auth.params.AuthParams;
 import org.apache.http.message.BufferedHeader;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
 import org.apache.http.util.CharArrayBuffer;
 import org.apache.http.util.EncodingUtils;
 
@@ -59,13 +63,20 @@ public class BasicScheme extends RFC2617Scheme {
     private boolean complete;
 
     /**
-     * Default constructor for the basic authentication scheme.
+     * Creates an instance of <tt>BasicScheme</tt> with the given challenge
+     * state.
+     *
+     * @since 4.2
      */
-    public BasicScheme() {
-        super();
+    public BasicScheme(final ChallengeState challengeState) {
+        super(challengeState);
         this.complete = false;
     }
 
+    public BasicScheme() {
+        this(null);
+    }
+
     /**
      * Returns textual designation of the basic authentication scheme.
      *
@@ -110,6 +121,15 @@ public class BasicScheme extends RFC2617Scheme {
     }
 
     /**
+     * @deprecated (4.2) Use {@link ContextAwareAuthScheme#authenticate(Credentials, HttpRequest, org.apache.http.protocol.HttpContext)}
+     */
+    @Deprecated
+    public Header authenticate(
+            final Credentials credentials, final HttpRequest request) throws AuthenticationException {
+        return authenticate(credentials, request, new BasicHttpContext());
+    }
+
+    /**
      * Produces basic authorization header for the given set of {@link Credentials}.
      *
      * @param credentials The set of credentials to be used for authentication
@@ -121,9 +141,11 @@ public class BasicScheme extends RFC2617Scheme {
      *
      * @return a basic authorization string
      */
+    @Override
     public Header authenticate(
             final Credentials credentials,
-            final HttpRequest request) throws AuthenticationException {
+            final HttpRequest request,
+            final HttpContext context) throws AuthenticationException {
 
         if (credentials == null) {
             throw new IllegalArgumentException("Credentials may not be null");
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java
index 6b47350..f183cb5 100644
--- a/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java
@@ -26,18 +26,26 @@
 
 package org.apache.http.impl.auth;
 
+import java.io.IOException;
 import java.security.MessageDigest;
+import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Formatter;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 import java.util.StringTokenizer;
 
 import org.apache.http.annotation.NotThreadSafe;
 
 import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpRequest;
 import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.ChallengeState;
+import org.apache.http.auth.ContextAwareAuthScheme;
 import org.apache.http.auth.Credentials;
 import org.apache.http.auth.AUTH;
 import org.apache.http.auth.MalformedChallengeException;
@@ -45,6 +53,8 @@ import org.apache.http.auth.params.AuthParams;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.message.BasicHeaderValueFormatter;
 import org.apache.http.message.BufferedHeader;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
 import org.apache.http.util.CharArrayBuffer;
 import org.apache.http.util.EncodingUtils;
 
@@ -90,24 +100,32 @@ public class DigestScheme extends RFC2617Scheme {
     /** Whether the digest authentication process is complete */
     private boolean complete;
 
+    private static final int QOP_UNKNOWN = -1;
     private static final int QOP_MISSING = 0;
     private static final int QOP_AUTH_INT = 1;
     private static final int QOP_AUTH = 2;
 
-    private int qopVariant = QOP_MISSING;
     private String lastNonce;
     private long nounceCount;
     private String cnonce;
-    private String nc;
+    private String a1;
+    private String a2;
 
     /**
-     * Default constructor for the digest authetication scheme.
+     * Creates an instance of <tt>DigestScheme</tt> with the given challenge
+     * state.
+     *
+     * @since 4.2
      */
-    public DigestScheme() {
-        super();
+    public DigestScheme(final ChallengeState challengeState) {
+        super(challengeState);
         this.complete = false;
     }
 
+    public DigestScheme() {
+        this(null);
+    }
+
     /**
      * Processes the Digest challenge.
      *
@@ -120,35 +138,6 @@ public class DigestScheme extends RFC2617Scheme {
     public void processChallenge(
             final Header header) throws MalformedChallengeException {
         super.processChallenge(header);
-
-        if (getParameter("realm") == null) {
-            throw new MalformedChallengeException("missing realm in challange");
-        }
-        if (getParameter("nonce") == null) {
-            throw new MalformedChallengeException("missing nonce in challange");
-        }
-
-        boolean unsupportedQop = false;
-        // qop parsing
-        String qop = getParameter("qop");
-        if (qop != null) {
-            StringTokenizer tok = new StringTokenizer(qop,",");
-            while (tok.hasMoreTokens()) {
-                String variant = tok.nextToken().trim();
-                if (variant.equals("auth")) {
-                    qopVariant = QOP_AUTH;
-                    break; //that's our favourite, because auth-int is unsupported
-                } else if (variant.equals("auth-int")) {
-                    qopVariant = QOP_AUTH_INT;
-                } else {
-                    unsupportedQop = true;
-                }
-            }
-        }
-
-        if (unsupportedQop && (qopVariant == QOP_MISSING)) {
-            throw new MalformedChallengeException("None of the qop methods is supported");
-        }
         this.complete = true;
     }
 
@@ -189,21 +178,13 @@ public class DigestScheme extends RFC2617Scheme {
         getParameters().put(name, value);
     }
 
-    private String getCnonce() {
-        if (this.cnonce == null) {
-            this.cnonce = createCnonce();
-        }
-        return this.cnonce;
-    }
-
-    private String getNc() {
-        if (this.nc == null) {
-            StringBuilder sb = new StringBuilder();
-            Formatter formatter = new Formatter(sb, Locale.US);
-            formatter.format("%08x", this.nounceCount);
-            this.nc = sb.toString();
-        }
-        return this.nc;
+    /**
+     * @deprecated (4.2) Use {@link ContextAwareAuthScheme#authenticate(Credentials, HttpRequest, org.apache.http.protocol.HttpContext)}
+     */
+    @Deprecated 
+    public Header authenticate(
+            final Credentials credentials, final HttpRequest request) throws AuthenticationException {
+        return authenticate(credentials, request, new BasicHttpContext());
     }
 
     /**
@@ -220,9 +201,11 @@ public class DigestScheme extends RFC2617Scheme {
      *
      * @return a digest authorization string
      */
+    @Override
     public Header authenticate(
             final Credentials credentials,
-            final HttpRequest request) throws AuthenticationException {
+            final HttpRequest request,
+            final HttpContext context) throws AuthenticationException {
 
         if (credentials == null) {
             throw new IllegalArgumentException("Credentials may not be null");
@@ -230,7 +213,12 @@ public class DigestScheme extends RFC2617Scheme {
         if (request == null) {
             throw new IllegalArgumentException("HTTP request may not be null");
         }
-
+        if (getParameter("realm") == null) {
+            throw new AuthenticationException("missing realm in challenge");
+        }
+        if (getParameter("nonce") == null) {
+            throw new AuthenticationException("missing nonce in challenge");
+        }
         // Add method name and request-URI to the parameter map
         getParameters().put("methodname", request.getRequestLine().getMethod());
         getParameters().put("uri", request.getRequestLine().getUri());
@@ -239,8 +227,7 @@ public class DigestScheme extends RFC2617Scheme {
             charset = AuthParams.getCredentialCharset(request.getParams());
             getParameters().put("charset", charset);
         }
-        String digest = createDigest(credentials);
-        return createDigestHeader(credentials, digest);
+        return createDigestHeader(credentials, request);
     }
 
     private static MessageDigest createMessageDigest(
@@ -255,154 +242,160 @@ public class DigestScheme extends RFC2617Scheme {
     }
 
     /**
-     * Creates an MD5 response digest.
+     * Creates digest-response header as defined in RFC2617.
      *
-     * @return The created digest as string. This will be the response tag's
-     *         value in the Authentication HTTP header.
-     * @throws AuthenticationException when MD5 is an unsupported algorithm
+     * @param credentials User credentials
+     *
+     * @return The digest-response as String.
      */
-    private String createDigest(final Credentials credentials) throws AuthenticationException {
-        // Collecting required tokens
+    private Header createDigestHeader(
+            final Credentials credentials,
+            final HttpRequest request) throws AuthenticationException {
         String uri = getParameter("uri");
         String realm = getParameter("realm");
         String nonce = getParameter("nonce");
+        String opaque = getParameter("opaque");
         String method = getParameter("methodname");
         String algorithm = getParameter("algorithm");
-        if (uri == null) {
-            throw new IllegalStateException("URI may not be null");
-        }
-        if (realm == null) {
-            throw new IllegalStateException("Realm may not be null");
-        }
-        if (nonce == null) {
-            throw new IllegalStateException("Nonce may not be null");
+
+        Set<String> qopset = new HashSet<String>(8);
+        int qop = QOP_UNKNOWN;
+        String qoplist = getParameter("qop");
+        if (qoplist != null) {
+            StringTokenizer tok = new StringTokenizer(qoplist, ",");
+            while (tok.hasMoreTokens()) {
+                String variant = tok.nextToken().trim();
+                qopset.add(variant.toLowerCase(Locale.US));
+            }
+            if (request instanceof HttpEntityEnclosingRequest && qopset.contains("auth-int")) {
+                qop = QOP_AUTH_INT;
+            } else if (qopset.contains("auth")) {
+                qop = QOP_AUTH;
+            }
+        } else {
+            qop = QOP_MISSING;
         }
 
-        // Reset
-        this.cnonce = null;
-        this.nc = null;
+        if (qop == QOP_UNKNOWN) {
+            throw new AuthenticationException("None of the qop methods is supported: " + qoplist);
+        }
 
         // If an algorithm is not specified, default to MD5.
         if (algorithm == null) {
             algorithm = "MD5";
         }
-        // If an charset is not specified, default to ISO-8859-1.
         String charset = getParameter("charset");
         if (charset == null) {
             charset = "ISO-8859-1";
         }
 
-        if (qopVariant == QOP_AUTH_INT) {
-            throw new AuthenticationException(
-                "Unsupported qop in HTTP Digest authentication");
-        }
-
         String digAlg = algorithm;
         if (digAlg.equalsIgnoreCase("MD5-sess")) {
             digAlg = "MD5";
         }
 
-        if (nonce.equals(this.lastNonce)) {
-            this.nounceCount++;
-        } else {
-            this.nounceCount = 1;
-            this.lastNonce = nonce;
+        MessageDigest digester;
+        try {
+            digester = createMessageDigest(digAlg);
+        } catch (UnsupportedDigestAlgorithmException ex) {
+            throw new AuthenticationException("Unsuppported digest algorithm: " + digAlg);
         }
 
-        MessageDigest digester = createMessageDigest(digAlg);
-
         String uname = credentials.getUserPrincipal().getName();
         String pwd = credentials.getPassword();
 
+        if (nonce.equals(this.lastNonce)) {
+            nounceCount++;
+        } else {
+            nounceCount = 1;
+            cnonce = null;
+            lastNonce = nonce;
+        }
+        StringBuilder sb = new StringBuilder(256);
+        Formatter formatter = new Formatter(sb, Locale.US);
+        formatter.format("%08x", nounceCount);
+        String nc = sb.toString();
+
+        if (cnonce == null) {
+            cnonce = createCnonce();
+        }
+
+        a1 = null;
+        a2 = null;
         // 3.2.2.2: Calculating digest
-        StringBuilder tmp = new StringBuilder(uname.length() + realm.length() + pwd.length() + 2);
-        tmp.append(uname);
-        tmp.append(':');
-        tmp.append(realm);
-        tmp.append(':');
-        tmp.append(pwd);
-        // unq(username-value) ":" unq(realm-value) ":" passwd
-        String a1 = tmp.toString();
-
-        //a1 is suitable for MD5 algorithm
         if (algorithm.equalsIgnoreCase("MD5-sess")) {
             // H( unq(username-value) ":" unq(realm-value) ":" passwd )
             //      ":" unq(nonce-value)
             //      ":" unq(cnonce-value)
 
-            algorithm = "MD5";
-            String cnonce = getCnonce();
-
-            String tmp2 = encode(digester.digest(EncodingUtils.getBytes(a1, charset)));
-            StringBuilder tmp3 = new StringBuilder(
-                    tmp2.length() + nonce.length() + cnonce.length() + 2);
-            tmp3.append(tmp2);
-            tmp3.append(':');
-            tmp3.append(nonce);
-            tmp3.append(':');
-            tmp3.append(cnonce);
-            a1 = tmp3.toString();
+            // calculated one per session
+            sb.setLength(0);
+            sb.append(uname).append(':').append(realm).append(':').append(pwd);
+            String checksum = encode(digester.digest(EncodingUtils.getBytes(sb.toString(), charset)));
+            sb.setLength(0);
+            sb.append(checksum).append(':').append(nonce).append(':').append(cnonce);
+            a1 = sb.toString();
+        } else {
+            // unq(username-value) ":" unq(realm-value) ":" passwd
+            sb.setLength(0);
+            sb.append(uname).append(':').append(realm).append(':').append(pwd);
+            a1 = sb.toString();
         }
+
         String hasha1 = encode(digester.digest(EncodingUtils.getBytes(a1, charset)));
 
-        String a2 = null;
-        if (qopVariant == QOP_AUTH_INT) {
-            // Unhandled qop auth-int
-            //we do not have access to the entity-body or its hash
-            //TODO: add Method ":" digest-uri-value ":" H(entity-body)
+        if (qop == QOP_AUTH) {
+            // Method ":" digest-uri-value
+            a2 = method + ':' + uri;
+        } else if (qop == QOP_AUTH_INT) {
+            // Method ":" digest-uri-value ":" H(entity-body)
+            HttpEntity entity = null;
+            if (request instanceof HttpEntityEnclosingRequest) {
+                entity = ((HttpEntityEnclosingRequest) request).getEntity();
+            }
+            if (entity != null && !entity.isRepeatable()) {
+                // If the entity is not repeatable, try falling back onto QOP_AUTH
+                if (qopset.contains("auth")) {
+                    qop = QOP_AUTH;
+                    a2 = method + ':' + uri;
+                } else {
+                    throw new AuthenticationException("Qop auth-int cannot be used with " +
+                            "a non-repeatable entity");
+                }
+            } else {
+                HttpEntityDigester entityDigester = new HttpEntityDigester(digester);
+                try {
+                    if (entity != null) {
+                        entity.writeTo(entityDigester);
+                    }
+                    entityDigester.close();
+                } catch (IOException ex) {
+                    throw new AuthenticationException("I/O error reading entity content", ex);
+                }
+                a2 = method + ':' + uri + ':' + encode(entityDigester.getDigest());
+            }
         } else {
             a2 = method + ':' + uri;
         }
-        String hasha2 = encode(digester.digest(EncodingUtils.getAsciiBytes(a2)));
+
+        String hasha2 = encode(digester.digest(EncodingUtils.getBytes(a2, charset)));
 
         // 3.2.2.1
-        String serverDigestValue;
-        if (qopVariant == QOP_MISSING) {
-            StringBuilder tmp2 = new StringBuilder(
-                    hasha1.length() + nonce.length() + hasha1.length());
-            tmp2.append(hasha1);
-            tmp2.append(':');
-            tmp2.append(nonce);
-            tmp2.append(':');
-            tmp2.append(hasha2);
-            serverDigestValue = tmp2.toString();
+
+        String digestValue;
+        if (qop == QOP_MISSING) {
+            sb.setLength(0);
+            sb.append(hasha1).append(':').append(nonce).append(':').append(hasha2);
+            digestValue = sb.toString();
         } else {
-            String qopOption = getQopVariantString();
-            String cnonce = getCnonce();
-            String nc =  getNc();
-            StringBuilder tmp2 = new StringBuilder(hasha1.length() + nonce.length()
-                + nc.length() + cnonce.length() + qopOption.length() + hasha2.length() + 5);
-            tmp2.append(hasha1);
-            tmp2.append(':');
-            tmp2.append(nonce);
-            tmp2.append(':');
-            tmp2.append(nc);
-            tmp2.append(':');
-            tmp2.append(cnonce);
-            tmp2.append(':');
-            tmp2.append(qopOption);
-            tmp2.append(':');
-            tmp2.append(hasha2);
-            serverDigestValue = tmp2.toString();
+            sb.setLength(0);
+            sb.append(hasha1).append(':').append(nonce).append(':').append(nc).append(':')
+                .append(cnonce).append(':').append(qop == QOP_AUTH_INT ? "auth-int" : "auth")
+                .append(':').append(hasha2);
+            digestValue = sb.toString();
         }
 
-        String serverDigest =
-            encode(digester.digest(EncodingUtils.getAsciiBytes(serverDigestValue)));
-
-        return serverDigest;
-    }
-
-    /**
-     * Creates digest-response header as defined in RFC2617.
-     *
-     * @param credentials User credentials
-     * @param digest The response tag's value as String.
-     *
-     * @return The digest-response as String.
-     */
-    private Header createDigestHeader(
-            final Credentials credentials,
-            final String digest) {
+        String digest = encode(digester.digest(EncodingUtils.getAsciiBytes(digestValue)));
 
         CharArrayBuffer buffer = new CharArrayBuffer(128);
         if (isProxy()) {
@@ -412,26 +405,17 @@ public class DigestScheme extends RFC2617Scheme {
         }
         buffer.append(": Digest ");
 
-        String uri = getParameter("uri");
-        String realm = getParameter("realm");
-        String nonce = getParameter("nonce");
-        String opaque = getParameter("opaque");
-        String response = digest;
-        String algorithm = getParameter("algorithm");
-
-        String uname = credentials.getUserPrincipal().getName();
-
         List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>(20);
         params.add(new BasicNameValuePair("username", uname));
         params.add(new BasicNameValuePair("realm", realm));
         params.add(new BasicNameValuePair("nonce", nonce));
         params.add(new BasicNameValuePair("uri", uri));
-        params.add(new BasicNameValuePair("response", response));
+        params.add(new BasicNameValuePair("response", digest));
 
-        if (qopVariant != QOP_MISSING) {
-            params.add(new BasicNameValuePair("qop", getQopVariantString()));
-            params.add(new BasicNameValuePair("nc", getNc()));
-            params.add(new BasicNameValuePair("cnonce", getCnonce()));
+        if (qop != QOP_MISSING) {
+            params.add(new BasicNameValuePair("qop", qop == QOP_AUTH_INT ? "auth-int" : "auth"));
+            params.add(new BasicNameValuePair("nc", nc));
+            params.add(new BasicNameValuePair("cnonce", cnonce));
         }
         if (algorithm != null) {
             params.add(new BasicNameValuePair("algorithm", algorithm));
@@ -445,22 +429,22 @@ public class DigestScheme extends RFC2617Scheme {
             if (i > 0) {
                 buffer.append(", ");
             }
-            boolean noQuotes = "nc".equals(param.getName()) ||
-                               "qop".equals(param.getName());
-            BasicHeaderValueFormatter.DEFAULT
-                .formatNameValuePair(buffer, param, !noQuotes);
+            boolean noQuotes = "nc".equals(param.getName()) || "qop".equals(param.getName());
+            BasicHeaderValueFormatter.DEFAULT.formatNameValuePair(buffer, param, !noQuotes);
         }
         return new BufferedHeader(buffer);
     }
 
-    private String getQopVariantString() {
-        String qopOption;
-        if (qopVariant == QOP_AUTH_INT) {
-            qopOption = "auth-int";
-        } else {
-            qopOption = "auth";
-        }
-        return qopOption;
+    String getCnonce() {
+        return cnonce;
+    }
+
+    String getA1() {
+        return a1;
+    }
+
+    String getA2() {
+        return a2;
     }
 
     /**
@@ -470,7 +454,7 @@ public class DigestScheme extends RFC2617Scheme {
      * @param binaryData array containing the digest
      * @return encoded MD5, or <CODE>null</CODE> if encoding failed
      */
-    private static String encode(byte[] binaryData) {
+    static String encode(byte[] binaryData) {
         int n = binaryData.length;
         char[] buffer = new char[n * 2];
         for (int i = 0; i < n; i++) {
@@ -488,16 +472,12 @@ public class DigestScheme extends RFC2617Scheme {
      * Creates a random cnonce value based on the current time.
      *
      * @return The cnonce value as String.
-     * @throws UnsupportedDigestAlgorithmException if MD5 algorithm is not supported.
      */
     public static String createCnonce() {
-        String cnonce;
-
-        MessageDigest md5Helper = createMessageDigest("MD5");
-
-        cnonce = Long.toString(System.currentTimeMillis());
-        cnonce = encode(md5Helper.digest(EncodingUtils.getAsciiBytes(cnonce)));
-
-        return cnonce;
+        SecureRandom rnd = new SecureRandom();
+        byte[] tmp = new byte[8];
+        rnd.nextBytes(tmp);
+        return encode(tmp);
     }
+
 }
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java b/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java
new file mode 100644
index 0000000..075c0a1
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java
@@ -0,0 +1,199 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.ContextAwareAuthScheme;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.InvalidCredentialsException;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.CharArrayBuffer;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+
+/**
+ * @since 4.2
+ */
+public abstract class GGSSchemeBase extends AuthSchemeBase {
+
+    enum State {
+        UNINITIATED,
+        CHALLENGE_RECEIVED,
+        TOKEN_GENERATED,
+        FAILED,
+    }
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    private final boolean stripPort;
+    private final Base64 base64codec;
+
+    /** Authentication process state */
+    private State state;
+
+    /** base64 decoded challenge **/
+    private byte[] token;
+
+    GGSSchemeBase(boolean stripPort) {
+        super();
+        this.base64codec = new Base64();
+        this.state = State.UNINITIATED;
+        this.stripPort = stripPort;
+    }
+
+    GGSSchemeBase() {
+        this(false);
+    }
+
+    protected GSSManager getManager() {
+        return GSSManager.getInstance();
+    }
+
+    protected byte[] generateGSSToken(
+            final byte[] input, final Oid oid, final String authServer) throws GSSException {
+        byte[] token = input;
+        if (token == null) {
+            token = new byte[0];
+        }
+        GSSManager manager = getManager();
+        GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE);
+        GSSContext gssContext = manager.createContext(
+                serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME);
+        gssContext.requestMutualAuth(true);
+        gssContext.requestCredDeleg(true);
+        return gssContext.initSecContext(token, 0, token.length);
+    }
+
+    protected abstract byte[] generateToken(
+            byte[] input, final String authServer) throws GSSException;
+
+    public boolean isComplete() {
+        return this.state == State.TOKEN_GENERATED || this.state == State.FAILED;
+    }
+
+    /**
+     * @deprecated (4.2) Use {@link ContextAwareAuthScheme#authenticate(Credentials, HttpRequest, org.apache.http.protocol.HttpContext)}
+     */
+    @Deprecated 
+    public Header authenticate(
+            final Credentials credentials,
+            final HttpRequest request) throws AuthenticationException {
+        return authenticate(credentials, request, null);
+    }
+
+    @Override
+    public Header authenticate(
+            final Credentials credentials,
+            final HttpRequest request,
+            final HttpContext context) throws AuthenticationException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        switch (state) {
+        case UNINITIATED:
+            throw new AuthenticationException(getSchemeName() + " authentication has not been initiated");
+        case FAILED:
+            throw new AuthenticationException(getSchemeName() + " authentication has failed");
+        case CHALLENGE_RECEIVED:
+            try {
+                String key = null;
+                if (isProxy()) {
+                    key = ExecutionContext.HTTP_PROXY_HOST;
+                } else {
+                    key = ExecutionContext.HTTP_TARGET_HOST;
+                }
+                HttpHost host = (HttpHost) context.getAttribute(key);
+                if (host == null) {
+                    throw new AuthenticationException("Authentication host is not set " +
+                            "in the execution context");
+                }
+                String authServer;
+                if (!this.stripPort && host.getPort() > 0) {
+                    authServer = host.toHostString();
+                } else {
+                    authServer = host.getHostName();
+                }
+
+                if (log.isDebugEnabled()) {
+                    log.debug("init " + authServer);
+                }
+                token = generateToken(token, authServer);
+                state = State.TOKEN_GENERATED;
+            } catch (GSSException gsse) {
+                state = State.FAILED;
+                if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL
+                        || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED)
+                    throw new InvalidCredentialsException(gsse.getMessage(), gsse);
+                if (gsse.getMajor() == GSSException.NO_CRED )
+                    throw new InvalidCredentialsException(gsse.getMessage(), gsse);
+                if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
+                        || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
+                        || gsse.getMajor() == GSSException.OLD_TOKEN)
+                    throw new AuthenticationException(gsse.getMessage(), gsse);
+                // other error
+                throw new AuthenticationException(gsse.getMessage());
+            }
+        case TOKEN_GENERATED:
+            String tokenstr = new String(base64codec.encode(token));
+            if (log.isDebugEnabled()) {
+                log.debug("Sending response '" + tokenstr + "' back to the auth server");
+            }
+            return new BasicHeader("Authorization", "Negotiate " + tokenstr);
+        default:
+            throw new IllegalStateException("Illegal state: " + state);
+        }
+    }
+
+    @Override
+    protected void parseChallenge(
+            final CharArrayBuffer buffer,
+            int beginIndex, int endIndex) throws MalformedChallengeException {
+        String challenge = buffer.substringTrimmed(beginIndex, endIndex);
+        if (log.isDebugEnabled()) {
+            log.debug("Received challenge '" + challenge + "' from the auth server");
+        }
+        if (state == State.UNINITIATED) {
+            token = base64codec.decode(challenge.getBytes());
+            state = State.CHALLENGE_RECEIVED;
+        } else {
+            log.debug("Authentication already attempted");
+            state = State.FAILED;
+        }
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/HttpEntityDigester.java b/httpclient/src/main/java/org/apache/http/impl/auth/HttpEntityDigester.java
new file mode 100644
index 0000000..75fe561
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/HttpEntityDigester.java
@@ -0,0 +1,75 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.MessageDigest;
+
+class HttpEntityDigester extends OutputStream {
+
+    private final MessageDigest digester;
+    private boolean closed;
+    private byte[] digest;
+
+    HttpEntityDigester(final MessageDigest digester) {
+        super();
+        this.digester = digester;
+        this.digester.reset();
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        if (this.closed) {
+            throw new IOException("Stream has been already closed");
+        }
+        this.digester.update((byte) b);
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (this.closed) {
+            throw new IOException("Stream has been already closed");
+        }
+        this.digester.update(b, off, len);
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (this.closed) {
+            return;
+        }
+        this.closed = true;
+        this.digest = this.digester.digest();
+        super.close();
+    }
+
+    public byte[] getDigest() {
+        return this.digest;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java
new file mode 100644
index 0000000..d9971c1
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java
@@ -0,0 +1,114 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.protocol.HttpContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.Oid;
+
+/**
+ * KERBEROS authentication scheme.
+ *
+ * @since 4.2
+ */
+public class KerberosScheme extends GGSSchemeBase {
+
+    private static final String KERBEROS_OID = "1.2.840.113554.1.2.2";
+
+    public KerberosScheme(boolean stripPort) {
+        super(stripPort);
+    }
+
+    public KerberosScheme() {
+        super(false);
+    }
+
+    public String getSchemeName() {
+        return "Kerberos";
+    }
+
+    /**
+     * Produces KERBEROS authorization Header based on token created by
+     * processChallenge.
+     *
+     * @param credentials not used by the KERBEROS scheme.
+     * @param request The request being authenticated
+     *
+     * @throws AuthenticationException if authentication string cannot
+     *   be generated due to an authentication failure
+     *
+     * @return KERBEROS authentication Header
+     */
+    @Override
+    public Header authenticate(
+            final Credentials credentials,
+            final HttpRequest request,
+            final HttpContext context) throws AuthenticationException {
+        return super.authenticate(credentials, request, context);
+    }
+
+    @Override
+    protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException {
+        return generateGSSToken(input, new Oid(KERBEROS_OID), authServer);
+    }
+
+    /**
+     * There are no valid parameters for KERBEROS authentication so this
+     * method always returns <code>null</code>.
+     *
+     * @return <code>null</code>
+     */
+    public String getParameter(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Parameter name may not be null");
+        }
+        return null;
+    }
+
+    /**
+     * The concept of an authentication realm is not supported by the Negotiate
+     * authentication scheme. Always returns <code>null</code>.
+     *
+     * @return <code>null</code>
+     */
+    public String getRealm() {
+        return null;
+    }
+
+    /**
+     * Returns <tt>true</tt>. KERBEROS authentication scheme is connection based.
+     *
+     * @return <tt>true</tt>.
+     */
+    public boolean isConnectionBased() {
+        return true;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/KerberosSchemeFactory.java b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosSchemeFactory.java
new file mode 100644
index 0000000..6bb8a64
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosSchemeFactory.java
@@ -0,0 +1,61 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.annotation.Immutable;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeFactory;
+import org.apache.http.params.HttpParams;
+
+/**
+ * Kerberos authentication scheme factory.
+ *
+ * @since 4.2
+ */
+ at Immutable
+public class KerberosSchemeFactory implements AuthSchemeFactory {
+
+    private final boolean stripPort;
+
+    public KerberosSchemeFactory(boolean stripPort) {
+        super();
+        this.stripPort = stripPort;
+    }
+
+    public KerberosSchemeFactory() {
+        this(false);
+    }
+
+    public AuthScheme newInstance(final HttpParams params) {
+        return new KerberosScheme(this.stripPort);
+    }
+
+    public boolean isStripPort() {
+        return stripPort;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java
index 3d38527..463e961 100644
--- a/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateScheme.java
@@ -27,24 +27,14 @@ package org.apache.http.impl.auth;
 
 import java.io.IOException;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.http.Header;
-import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.auth.AuthenticationException;
 import org.apache.http.auth.Credentials;
-import org.apache.http.auth.InvalidCredentialsException;
-import org.apache.http.auth.MalformedChallengeException;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.protocol.ExecutionContext;
 import org.apache.http.protocol.HttpContext;
-import org.apache.http.util.CharArrayBuffer;
-import org.ietf.jgss.GSSContext;
 import org.ietf.jgss.GSSException;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.GSSName;
 import org.ietf.jgss.Oid;
 
 /**
@@ -52,44 +42,26 @@ import org.ietf.jgss.Oid;
  * scheme.
  *
  * @since 4.1
+ *
+ * @deprecated (4.2)  use {@link SPNegoScheme} or {@link KerberosScheme}.
  */
-public class NegotiateScheme extends AuthSchemeBase {
+ at Deprecated
+public class NegotiateScheme extends GGSSchemeBase {
 
-    enum State {
-        UNINITIATED,
-        CHALLENGE_RECEIVED,
-        TOKEN_GENERATED,
-        FAILED,
-    }
+    private final Log log = LogFactory.getLog(getClass());
 
     private static final String SPNEGO_OID       = "1.3.6.1.5.5.2";
     private static final String KERBEROS_OID     = "1.2.840.113554.1.2.2";
 
-    private final Log log = LogFactory.getLog(getClass());
-
     private final SpnegoTokenGenerator spengoGenerator;
 
-    private final boolean stripPort;
-
-    private GSSContext gssContext = null;
-
-    /** Authentication process state */
-    private State state;
-
-    /** base64 decoded challenge **/
-    private byte[] token;
-
-    private Oid negotiationOid = null;
-
     /**
      * Default constructor for the Negotiate authentication scheme.
      *
      */
     public NegotiateScheme(final SpnegoTokenGenerator spengoGenerator, boolean stripPort) {
-        super();
-        this.state = State.UNINITIATED;
+        super(stripPort);
         this.spengoGenerator = spengoGenerator;
-        this.stripPort = stripPort;
     }
 
     public NegotiateScheme(final SpnegoTokenGenerator spengoGenerator) {
@@ -101,17 +73,6 @@ public class NegotiateScheme extends AuthSchemeBase {
     }
 
     /**
-     * Tests if the Negotiate authentication process has been completed.
-     *
-     * @return <tt>true</tt> if authorization has been processed,
-     *   <tt>false</tt> otherwise.
-     *
-     */
-    public boolean isComplete() {
-        return this.state == State.TOKEN_GENERATED || this.state == State.FAILED;
-    }
-
-    /**
      * Returns textual designation of the Negotiate authentication scheme.
      *
      * @return <code>Negotiate</code>
@@ -120,17 +81,12 @@ public class NegotiateScheme extends AuthSchemeBase {
         return "Negotiate";
     }
 
-    @Deprecated
     public Header authenticate(
             final Credentials credentials,
             final HttpRequest request) throws AuthenticationException {
         return authenticate(credentials, request, null);
     }
 
-    protected GSSManager getManager() {
-        return GSSManager.getInstance();
-    }
-
     /**
      * Produces Negotiate authorization Header based on token created by
      * processChallenge.
@@ -149,127 +105,64 @@ public class NegotiateScheme extends AuthSchemeBase {
             final Credentials credentials,
             final HttpRequest request,
             final HttpContext context) throws AuthenticationException {
-        if (request == null) {
-            throw new IllegalArgumentException("HTTP request may not be null");
-        }
-        if (state != State.CHALLENGE_RECEIVED) {
-            throw new IllegalStateException(
-                    "Negotiation authentication process has not been initiated");
-        }
+        return super.authenticate(credentials, request, context);
+    }
+
+    @Override
+    protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException {
+        /* Using the SPNEGO OID is the correct method.
+         * Kerberos v5 works for IIS but not JBoss. Unwrapping
+         * the initial token when using SPNEGO OID looks like what is
+         * described here...
+         *
+         * http://msdn.microsoft.com/en-us/library/ms995330.aspx
+         *
+         * Another helpful URL...
+         *
+         * http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/tsec_SPNEGO_token.html
+         *
+         * Unfortunately SPNEGO is JRE >=1.6.
+         */
+
+        /** Try SPNEGO by default, fall back to Kerberos later if error */
+        Oid negotiationOid  = new Oid(SPNEGO_OID);
+
+        byte[] token = input;
+        boolean tryKerberos = false;
         try {
-            String key = null;
-            if (isProxy()) {
-                key = ExecutionContext.HTTP_PROXY_HOST;
+            token = generateGSSToken(token, negotiationOid, authServer);
+        } catch (GSSException ex){
+            // BAD MECH means we are likely to be using 1.5, fall back to Kerberos MECH.
+            // Rethrow any other exception.
+            if (ex.getMajor() == GSSException.BAD_MECH ){
+                log.debug("GSSException BAD_MECH, retry with Kerberos MECH");
+                tryKerberos = true;
             } else {
-                key = ExecutionContext.HTTP_TARGET_HOST;
+                throw ex;
             }
-            HttpHost host = (HttpHost) context.getAttribute(key);
-            if (host == null) {
-                throw new AuthenticationException("Authentication host is not set " +
-                        "in the execution context");
-            }
-            String authServer;
-            if (!this.stripPort && host.getPort() > 0) {
-                authServer = host.toHostString();
-            } else {
-                authServer = host.getHostName();
-            }
-
-            if (log.isDebugEnabled()) {
-                log.debug("init " + authServer);
-            }
-            /* Using the SPNEGO OID is the correct method.
-             * Kerberos v5 works for IIS but not JBoss. Unwrapping
-             * the initial token when using SPNEGO OID looks like what is
-             * described here...
-             *
-             * http://msdn.microsoft.com/en-us/library/ms995330.aspx
-             *
-             * Another helpful URL...
-             *
-             * http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/tsec_SPNEGO_token.html
-             *
-             * Unfortunately SPNEGO is JRE >=1.6.
-             */
 
-            /** Try SPNEGO by default, fall back to Kerberos later if error */
-            negotiationOid  = new Oid(SPNEGO_OID);
-
-            boolean tryKerberos = false;
-            try {
-                GSSManager manager = getManager();
-                GSSName serverName = manager.createName("HTTP/" + authServer, null);
-                gssContext = manager.createContext(
-                        serverName.canonicalize(negotiationOid), negotiationOid, null,
-                        GSSContext.DEFAULT_LIFETIME);
-                gssContext.requestMutualAuth(true);
-                gssContext.requestCredDeleg(true);
-            } catch (GSSException ex){
-                // BAD MECH means we are likely to be using 1.5, fall back to Kerberos MECH.
-                // Rethrow any other exception.
-                if (ex.getMajor() == GSSException.BAD_MECH ){
-                    log.debug("GSSException BAD_MECH, retry with Kerberos MECH");
-                    tryKerberos = true;
-                } else {
-                    throw ex;
-                }
-
-            }
-            if (tryKerberos){
-                /* Kerberos v5 GSS-API mechanism defined in RFC 1964.*/
-                log.debug("Using Kerberos MECH " + KERBEROS_OID);
-                negotiationOid  = new Oid(KERBEROS_OID);
-                GSSManager manager = getManager();
-                GSSName serverName = manager.createName("HTTP/" + authServer, null);
-                gssContext = manager.createContext(
-                        serverName.canonicalize(negotiationOid), negotiationOid, null,
-                        GSSContext.DEFAULT_LIFETIME);
-                gssContext.requestMutualAuth(true);
-                gssContext.requestCredDeleg(true);
-            }
-            if (token == null) {
-                token = new byte[0];
-            }
-            token = gssContext.initSecContext(token, 0, token.length);
-            if (token == null) {
-                state = State.FAILED;
-                throw new AuthenticationException("GSS security context initialization failed");
-            }
+        }
+        if (tryKerberos){
+            /* Kerberos v5 GSS-API mechanism defined in RFC 1964.*/
+            log.debug("Using Kerberos MECH " + KERBEROS_OID);
+            negotiationOid  = new Oid(KERBEROS_OID);
+            token = generateGSSToken(token, negotiationOid, authServer);
 
             /*
              * IIS accepts Kerberos and SPNEGO tokens. Some other servers Jboss, Glassfish?
              * seem to only accept SPNEGO. Below wraps Kerberos into SPNEGO token.
              */
-            if (spengoGenerator != null && negotiationOid.toString().equals(KERBEROS_OID)) {
-                token = spengoGenerator.generateSpnegoDERObject(token);
-            }
-
-            state = State.TOKEN_GENERATED;
-            String tokenstr = new String(Base64.encodeBase64(token, false));
-            if (log.isDebugEnabled()) {
-                log.debug("Sending response '" + tokenstr + "' back to the auth server");
+            if (token != null && spengoGenerator != null) {
+                try {
+                    token = spengoGenerator.generateSpnegoDERObject(token);
+                } catch (IOException ex) {
+                    log.error(ex.getMessage(), ex);
+                }
             }
-            return new BasicHeader("Authorization", "Negotiate " + tokenstr);
-        } catch (GSSException gsse) {
-            state = State.FAILED;
-            if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL
-                    || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED)
-                throw new InvalidCredentialsException(gsse.getMessage(), gsse);
-            if (gsse.getMajor() == GSSException.NO_CRED )
-                throw new InvalidCredentialsException(gsse.getMessage(), gsse);
-            if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
-                    || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
-                    || gsse.getMajor() == GSSException.OLD_TOKEN)
-                throw new AuthenticationException(gsse.getMessage(), gsse);
-            // other error
-            throw new AuthenticationException(gsse.getMessage());
-        } catch (IOException ex){
-            state = State.FAILED;
-            throw new AuthenticationException(ex.getMessage());
         }
+        return token;
     }
 
-
     /**
      * Returns the authentication parameter with the given name, if available.
      *
@@ -307,21 +200,4 @@ public class NegotiateScheme extends AuthSchemeBase {
         return true;
     }
 
-    @Override
-    protected void parseChallenge(
-            final CharArrayBuffer buffer,
-            int beginIndex, int endIndex) throws MalformedChallengeException {
-        String challenge = buffer.substringTrimmed(beginIndex, endIndex);
-        if (log.isDebugEnabled()) {
-            log.debug("Received challenge '" + challenge + "' from the auth server");
-        }
-        if (state == State.UNINITIATED) {
-            token = new Base64().decode(challenge.getBytes());
-            state = State.CHALLENGE_RECEIVED;
-        } else {
-            log.debug("Authentication already attempted");
-            state = State.FAILED;
-        }
-    }
-
 }
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateSchemeFactory.java b/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateSchemeFactory.java
index 4ad002d..520e14d 100644
--- a/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateSchemeFactory.java
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/NegotiateSchemeFactory.java
@@ -26,7 +26,6 @@
 
 package org.apache.http.impl.auth;
 
-import org.apache.http.annotation.Immutable;
 import org.apache.http.auth.AuthScheme;
 import org.apache.http.auth.AuthSchemeFactory;
 import org.apache.http.params.HttpParams;
@@ -36,8 +35,10 @@ import org.apache.http.params.HttpParams;
  * scheme factory.
  *
  * @since 4.1
+ *
+ * @deprecated (4.2)  use {@link SPNegoSchemeFactory} or {@link KerberosSchemeFactory}.
  */
- at Immutable
+ at Deprecated 
 public class NegotiateSchemeFactory implements AuthSchemeFactory {
 
     private final SpnegoTokenGenerator spengoGenerator;
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java
index 021c39d..b8fd45f 100644
--- a/httpclient/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java
@@ -33,6 +33,7 @@ import java.util.Map;
 import org.apache.http.annotation.NotThreadSafe;
 
 import org.apache.http.HeaderElement;
+import org.apache.http.auth.ChallengeState;
 import org.apache.http.auth.MalformedChallengeException;
 import org.apache.http.message.BasicHeaderValueParser;
 import org.apache.http.message.HeaderValueParser;
@@ -52,13 +53,21 @@ public abstract class RFC2617Scheme extends AuthSchemeBase {
     /**
      * Authentication parameter map.
      */
-    private Map<String, String> params;
+    private final Map<String, String> params;
 
     /**
-     * Default constructor for RFC2617 compliant authentication schemes.
+     * Creates an instance of <tt>RFC2617Scheme</tt> with the given challenge
+     * state.
+     *
+     * @since 4.2
      */
+    public RFC2617Scheme(final ChallengeState challengeState) {
+        super(challengeState);
+        this.params = new HashMap<String, String>();
+    }
+
     public RFC2617Scheme() {
-        super();
+        this(null);
     }
 
     @Override
@@ -70,8 +79,7 @@ public abstract class RFC2617Scheme extends AuthSchemeBase {
         if (elements.length == 0) {
             throw new MalformedChallengeException("Authentication challenge is empty");
         }
-
-        this.params = new HashMap<String, String>(elements.length);
+        this.params.clear();
         for (HeaderElement element : elements) {
             this.params.put(element.getName(), element.getValue());
         }
@@ -83,9 +91,6 @@ public abstract class RFC2617Scheme extends AuthSchemeBase {
      * @return the map of authentication parameters
      */
     protected Map<String, String> getParameters() {
-        if (this.params == null) {
-            this.params = new HashMap<String, String>();
-        }
         return this.params;
     }
 
@@ -98,9 +103,6 @@ public abstract class RFC2617Scheme extends AuthSchemeBase {
      */
     public String getParameter(final String name) {
         if (name == null) {
-            throw new IllegalArgumentException("Parameter name may not be null");
-        }
-        if (this.params == null) {
             return null;
         }
         return this.params.get(name.toLowerCase(Locale.ENGLISH));
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java
new file mode 100644
index 0000000..66ee8db
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java
@@ -0,0 +1,115 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.protocol.HttpContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.Oid;
+
+/**
+ * SPNEGO (Simple and Protected GSSAPI Negotiation Mechanism) authentication
+ * scheme.
+ *
+ * @since 4.2
+ */
+public class SPNegoScheme extends GGSSchemeBase {
+
+    private static final String SPNEGO_OID = "1.3.6.1.5.5.2";
+
+    public SPNegoScheme(boolean stripPort) {
+        super(stripPort);
+    }
+
+    public SPNegoScheme() {
+        super(false);
+    }
+
+    public String getSchemeName() {
+        return "Negotiate";
+    }
+
+    /**
+     * Produces SPNEGO authorization Header based on token created by
+     * processChallenge.
+     *
+     * @param credentials not used by the SPNEGO scheme.
+     * @param request The request being authenticated
+     *
+     * @throws AuthenticationException if authentication string cannot
+     *   be generated due to an authentication failure
+     *
+     * @return SPNEGO authentication Header
+     */
+    @Override
+    public Header authenticate(
+            final Credentials credentials,
+            final HttpRequest request,
+            final HttpContext context) throws AuthenticationException {
+        return super.authenticate(credentials, request, context);
+    }
+
+    @Override
+    protected byte[] generateToken(final byte[] input, final String authServer) throws GSSException {
+        return generateGSSToken(input, new Oid(SPNEGO_OID), authServer);
+    }
+
+    /**
+     * There are no valid parameters for SPNEGO authentication so this
+     * method always returns <code>null</code>.
+     *
+     * @return <code>null</code>
+     */
+    public String getParameter(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Parameter name may not be null");
+        }
+        return null;
+    }
+
+    /**
+     * The concept of an authentication realm is not supported by the Negotiate
+     * authentication scheme. Always returns <code>null</code>.
+     *
+     * @return <code>null</code>
+     */
+    public String getRealm() {
+        return null;
+    }
+
+    /**
+     * Returns <tt>true</tt>. SPNEGO authentication scheme is connection based.
+     *
+     * @return <tt>true</tt>.
+     */
+    public boolean isConnectionBased() {
+        return true;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoSchemeFactory.java b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoSchemeFactory.java
new file mode 100644
index 0000000..6ee3ac6
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoSchemeFactory.java
@@ -0,0 +1,62 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.auth;
+
+import org.apache.http.annotation.Immutable;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeFactory;
+import org.apache.http.params.HttpParams;
+
+/**
+ * SPNEGO (Simple and Protected GSSAPI Negotiation Mechanism) authentication
+ * scheme factory.
+ *
+ * @since 4.2
+ */
+ at Immutable
+public class SPNegoSchemeFactory implements AuthSchemeFactory {
+
+    private final boolean stripPort;
+
+    public SPNegoSchemeFactory(boolean stripPort) {
+        super();
+        this.stripPort = stripPort;
+    }
+
+    public SPNegoSchemeFactory() {
+        this(false);
+    }
+
+    public AuthScheme newInstance(final HttpParams params) {
+        return new SPNegoScheme(this.stripPort);
+    }
+
+    public boolean isStripPort() {
+        return stripPort;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/SpnegoTokenGenerator.java b/httpclient/src/main/java/org/apache/http/impl/auth/SpnegoTokenGenerator.java
index 3e474bb..154e584 100644
--- a/httpclient/src/main/java/org/apache/http/impl/auth/SpnegoTokenGenerator.java
+++ b/httpclient/src/main/java/org/apache/http/impl/auth/SpnegoTokenGenerator.java
@@ -35,7 +35,11 @@ import java.io.IOException;
  * Implementations of this interface are expected to be thread-safe.
  *
  * @since 4.1
+ *
+ * @deprecated (4.2)  subclass {@link KerberosScheme} and override
+ *   {@link KerberosScheme#generateGSSToken(byte[], org.ietf.jgss.Oid, String)}
  */
+ at Deprecated
 public interface SpnegoTokenGenerator {
 
     byte [] generateSpnegoDERObject(byte [] kerberosTicket) throws IOException;
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AIMDBackoffManager.java b/httpclient/src/main/java/org/apache/http/impl/client/AIMDBackoffManager.java
new file mode 100644
index 0000000..b372fe4
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/AIMDBackoffManager.java
@@ -0,0 +1,161 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.http.client.BackoffManager;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.pool.ConnPoolControl;
+
+/**
+ * <p>The <code>AIMDBackoffManager</code> applies an additive increase,
+ * multiplicative decrease (AIMD) to managing a dynamic limit to
+ * the number of connections allowed to a given host. You may want
+ * to experiment with the settings for the cooldown periods and the
+ * backoff factor to get the adaptive behavior you want.</p>
+ *
+ * <p>Generally speaking, shorter cooldowns will lead to more steady-state
+ * variability but faster reaction times, while longer cooldowns
+ * will lead to more stable equilibrium behavior but slower reaction
+ * times.</p>
+ *
+ * <p>Similarly, higher backoff factors promote greater
+ * utilization of available capacity at the expense of fairness
+ * among clients. Lower backoff factors allow equal distribution of
+ * capacity among clients (fairness) to happen faster, at the
+ * expense of having more server capacity unused in the short term.</p>
+ *
+ * @since 4.2
+ */
+public class AIMDBackoffManager implements BackoffManager {
+
+    private final ConnPoolControl<HttpRoute> connPerRoute;
+    private final Clock clock;
+    private final Map<HttpRoute,Long> lastRouteProbes;
+    private final Map<HttpRoute,Long> lastRouteBackoffs;
+    private long coolDown = 5 * 1000L;
+    private double backoffFactor = 0.5;
+    private int cap = 2; // Per RFC 2616 sec 8.1.4
+
+    /**
+     * Creates an <code>AIMDBackoffManager</code> to manage
+     * per-host connection pool sizes represented by the
+     * given {@link ConnPoolControl}.
+     * @param connPerRoute per-host routing maximums to
+     *   be managed
+     */
+    public AIMDBackoffManager(ConnPoolControl<HttpRoute> connPerRoute) {
+        this(connPerRoute, new SystemClock());
+    }
+
+    AIMDBackoffManager(ConnPoolControl<HttpRoute> connPerRoute, Clock clock) {
+        this.clock = clock;
+        this.connPerRoute = connPerRoute;
+        this.lastRouteProbes = new HashMap<HttpRoute,Long>();
+        this.lastRouteBackoffs = new HashMap<HttpRoute,Long>();
+    }
+
+    public void backOff(HttpRoute route) {
+        synchronized(connPerRoute) {
+            int curr = connPerRoute.getMaxPerRoute(route);
+            Long lastUpdate = getLastUpdate(lastRouteBackoffs, route);
+            long now = clock.getCurrentTime();
+            if (now - lastUpdate.longValue() < coolDown) return;
+            connPerRoute.setMaxPerRoute(route, getBackedOffPoolSize(curr));
+            lastRouteBackoffs.put(route, Long.valueOf(now));
+        }
+    }
+
+    private int getBackedOffPoolSize(int curr) {
+        if (curr <= 1) return 1;
+        return (int)(Math.floor(backoffFactor * curr));
+    }
+
+    public void probe(HttpRoute route) {
+        synchronized(connPerRoute) {
+            int curr = connPerRoute.getMaxPerRoute(route);
+            int max = (curr >= cap) ? cap : curr + 1;
+            Long lastProbe = getLastUpdate(lastRouteProbes, route);
+            Long lastBackoff = getLastUpdate(lastRouteBackoffs, route);
+            long now = clock.getCurrentTime();
+            if (now - lastProbe.longValue() < coolDown || now - lastBackoff.longValue() < coolDown)
+                return;
+            connPerRoute.setMaxPerRoute(route, max);
+            lastRouteProbes.put(route, Long.valueOf(now));
+        }
+    }
+
+    private Long getLastUpdate(Map<HttpRoute,Long> updates, HttpRoute route) {
+        Long lastUpdate = updates.get(route);
+        if (lastUpdate == null) lastUpdate = Long.valueOf(0L);
+        return lastUpdate;
+    }
+
+    /**
+     * Sets the factor to use when backing off; the new
+     * per-host limit will be roughly the current max times
+     * this factor. <code>Math.floor</code> is applied in the
+     * case of non-integer outcomes to ensure we actually
+     * decrease the pool size. Pool sizes are never decreased
+     * below 1, however. Defaults to 0.5.
+     * @param d must be between 0.0 and 1.0, exclusive.
+     */
+    public void setBackoffFactor(double d) {
+        if (d <= 0.0 || d >= 1.0) {
+            throw new IllegalArgumentException("backoffFactor must be 0.0 < f < 1.0");
+        }
+        backoffFactor = d;
+    }
+
+    /**
+     * Sets the amount of time, in milliseconds, to wait between
+     * adjustments in pool sizes for a given host, to allow
+     * enough time for the adjustments to take effect. Defaults
+     * to 5000L (5 seconds).
+     * @param l must be positive
+     */
+    public void setCooldownMillis(long l) {
+        if (coolDown <= 0) {
+            throw new IllegalArgumentException("cooldownMillis must be positive");
+        }
+        coolDown = l;
+    }
+
+    /**
+     * Sets the absolute maximum per-host connection pool size to
+     * probe up to; defaults to 2 (the default per-host max).
+     * @param cap must be >= 1
+     */
+    public void setPerHostConnectionCap(int cap) {
+        if (cap < 1) {
+            throw new IllegalArgumentException("perHostConnectionCap must be >= 1");
+        }
+        this.cap = cap;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java
index 3a8dbcb..2976f4d 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java
@@ -46,6 +46,7 @@ import org.apache.http.auth.AuthSchemeRegistry;
 import org.apache.http.auth.AuthenticationException;
 import org.apache.http.auth.MalformedChallengeException;
 import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.AuthenticationStrategy;
 import org.apache.http.client.params.AuthPolicy;
 import org.apache.http.client.protocol.ClientContext;
 import org.apache.http.protocol.HTTP;
@@ -56,7 +57,10 @@ import org.apache.http.util.CharArrayBuffer;
  * Base class for {@link AuthenticationHandler} implementations.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link AuthenticationStrategy}
  */
+ at Deprecated 
 @Immutable
 public abstract class AbstractAuthenticationHandler implements AuthenticationHandler {
 
@@ -102,7 +106,7 @@ public abstract class AbstractAuthenticationHandler implements AuthenticationHan
             }
             int endIndex = pos;
             String s = buffer.substring(beginIndex, endIndex);
-            map.put(s.toLowerCase(Locale.ENGLISH), header);
+            map.put(s.toLowerCase(Locale.US), header);
         }
         return map;
     }
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java
index 5069e25..0f597d6 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/AbstractHttpClient.java
@@ -45,7 +45,10 @@ import org.apache.http.annotation.GuardedBy;
 import org.apache.http.annotation.ThreadSafe;
 import org.apache.http.auth.AuthSchemeRegistry;
 import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.AuthenticationStrategy;
+import org.apache.http.client.BackoffManager;
 import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.ConnectionBackoffStrategy;
 import org.apache.http.client.CookieStore;
 import org.apache.http.client.CredentialsProvider;
 import org.apache.http.client.HttpClient;
@@ -64,17 +67,19 @@ import org.apache.http.client.utils.URIUtils;
 import org.apache.http.conn.ClientConnectionManager;
 import org.apache.http.conn.ClientConnectionManagerFactory;
 import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoute;
 import org.apache.http.conn.routing.HttpRoutePlanner;
 import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.cookie.CookieSpecRegistry;
 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.auth.KerberosSchemeFactory;
 import org.apache.http.impl.auth.NTLMSchemeFactory;
-import org.apache.http.impl.auth.NegotiateSchemeFactory;
+import org.apache.http.impl.auth.SPNegoSchemeFactory;
+import org.apache.http.impl.conn.BasicClientConnectionManager;
 import org.apache.http.impl.conn.DefaultHttpRoutePlanner;
 import org.apache.http.impl.conn.SchemeRegistryFactory;
-import org.apache.http.impl.conn.SingleClientConnManager;
 import org.apache.http.impl.cookie.BestMatchSpecFactory;
 import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
 import org.apache.http.impl.cookie.IgnoreSpecFactory;
@@ -137,13 +142,13 @@ import org.apache.http.util.EntityUtils;
  *    a collection user credentials. The {@link #createCredentialsProvider()}
  *    must be implemented by concrete super classes to instantiate
  *    this object.
- *   <li>{@link AuthenticationHandler}</li> object used to authenticate
+ *   <li>{@link AuthenticationStrategy}</li> object used to authenticate
  *    against the target host.
- *    The {@link #createTargetAuthenticationHandler()} must be implemented
+ *    The {@link #createTargetAuthenticationStrategy()} must be implemented
  *    by concrete super classes to instantiate this object.
- *   <li>{@link AuthenticationHandler}</li> object used to authenticate
+ *   <li>{@link AuthenticationStrategy}</li> object used to authenticate
  *    against the proxy host.
- *    The {@link #createProxyAuthenticationHandler()} must be implemented
+ *    The {@link #createProxyAuthenticationStrategy()} must be implemented
  *    by concrete super classes to instantiate this object.
  *   <li>{@link HttpRoutePlanner}</li> object used to calculate a route
  *    for establishing a connection to the target host. The route
@@ -175,8 +180,8 @@ import org.apache.http.util.EntityUtils;
  *
  * @since 4.0
  */
- at ThreadSafe
 @SuppressWarnings("deprecation")
+ at ThreadSafe
 public abstract class AbstractHttpClient implements HttpClient {
 
     private final Log log = LogFactory.getLog(getClass());
@@ -226,11 +231,11 @@ public abstract class AbstractHttpClient implements HttpClient {
 
     /** The target authentication handler. */
     @GuardedBy("this")
-    private AuthenticationHandler targetAuthHandler;
+    private AuthenticationStrategy targetAuthStrategy;
 
     /** The proxy authentication handler. */
     @GuardedBy("this")
-    private AuthenticationHandler proxyAuthHandler;
+    private AuthenticationStrategy proxyAuthStrategy;
 
     /** The cookie store. */
     @GuardedBy("this")
@@ -248,6 +253,13 @@ public abstract class AbstractHttpClient implements HttpClient {
     @GuardedBy("this")
     private UserTokenHandler userTokenHandler;
 
+    /** The connection backoff strategy. */
+    @GuardedBy("this")
+    private ConnectionBackoffStrategy connectionBackoffStrategy;
+
+    /** The backoff manager. */
+    @GuardedBy("this")
+    private BackoffManager backoffManager;
 
     /**
      * Creates a new HTTP client.
@@ -315,7 +327,7 @@ public abstract class AbstractHttpClient implements HttpClient {
         if (factory != null) {
             connManager = factory.newInstance(params, registry);
         } else {
-            connManager = new SingleClientConnManager(registry);
+            connManager = new BasicClientConnectionManager(registry);
         }
 
         return connManager;
@@ -335,7 +347,10 @@ public abstract class AbstractHttpClient implements HttpClient {
                 new NTLMSchemeFactory());
         registry.register(
                 AuthPolicy.SPNEGO,
-                new NegotiateSchemeFactory());
+                new SPNegoSchemeFactory());
+        registry.register(
+                AuthPolicy.KERBEROS,
+                new KerberosSchemeFactory());
         return registry;
     }
 
@@ -363,63 +378,70 @@ public abstract class AbstractHttpClient implements HttpClient {
         return registry;
     }
 
-
     protected HttpRequestExecutor createRequestExecutor() {
         return new HttpRequestExecutor();
     }
 
-
     protected ConnectionReuseStrategy createConnectionReuseStrategy() {
         return new DefaultConnectionReuseStrategy();
     }
 
-
     protected ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy() {
         return new DefaultConnectionKeepAliveStrategy();
     }
 
-
     protected HttpRequestRetryHandler createHttpRequestRetryHandler() {
         return new DefaultHttpRequestRetryHandler();
     }
 
-
-    @Deprecated
+    /**
+     * @deprecated (4.1) do not use
+     */
+    @Deprecated 
     protected RedirectHandler createRedirectHandler() {
         return new DefaultRedirectHandler();
     }
 
+    protected AuthenticationStrategy createTargetAuthenticationStrategy() {
+        return new TargetAuthenticationStrategy();
+    }
 
+    /**
+     * @deprecated (4.2) do not use
+     */
+    @Deprecated 
     protected AuthenticationHandler createTargetAuthenticationHandler() {
         return new DefaultTargetAuthenticationHandler();
     }
 
+    protected AuthenticationStrategy createProxyAuthenticationStrategy() {
+        return new ProxyAuthenticationStrategy();
+    }
 
+    /**
+     * @deprecated (4.2) do not use
+     */
+    @Deprecated 
     protected AuthenticationHandler createProxyAuthenticationHandler() {
         return new DefaultProxyAuthenticationHandler();
     }
 
-
     protected CookieStore createCookieStore() {
         return new BasicCookieStore();
     }
 
-
     protected CredentialsProvider createCredentialsProvider() {
         return new BasicCredentialsProvider();
     }
 
-
     protected HttpRoutePlanner createHttpRoutePlanner() {
         return new DefaultHttpRoutePlanner(getConnectionManager().getSchemeRegistry());
     }
 
-
     protected UserTokenHandler createUserTokenHandler() {
         return new DefaultUserTokenHandler();
     }
 
-
     // non-javadoc, see interface HttpClient
     public synchronized final HttpParams getParams() {
         if (defaultParams == null) {
@@ -428,7 +450,6 @@ public abstract class AbstractHttpClient implements HttpClient {
         return defaultParams;
     }
 
-
     /**
      * Replaces the parameters.
      * The implementation here does not update parameters of dependent objects.
@@ -463,11 +484,17 @@ public abstract class AbstractHttpClient implements HttpClient {
         return supportedAuthSchemes;
     }
 
+    public synchronized void setAuthSchemes(final AuthSchemeRegistry registry) {
+        supportedAuthSchemes = registry;
+    }
 
-    public synchronized void setAuthSchemes(final AuthSchemeRegistry authSchemeRegistry) {
-        supportedAuthSchemes = authSchemeRegistry;
+    public synchronized final ConnectionBackoffStrategy getConnectionBackoffStrategy() {
+        return connectionBackoffStrategy;
     }
 
+    public synchronized void setConnectionBackoffStrategy(final ConnectionBackoffStrategy strategy) {
+        connectionBackoffStrategy = strategy;
+    }
 
     public synchronized final CookieSpecRegistry getCookieSpecs() {
         if (supportedCookieSpecs == null) {
@@ -476,11 +503,17 @@ public abstract class AbstractHttpClient implements HttpClient {
         return supportedCookieSpecs;
     }
 
+    public synchronized final BackoffManager getBackoffManager() {
+        return backoffManager;
+    }
 
-    public synchronized void setCookieSpecs(final CookieSpecRegistry cookieSpecRegistry) {
-        supportedCookieSpecs = cookieSpecRegistry;
+    public synchronized void setBackoffManager(final BackoffManager manager) {
+        backoffManager = manager;
     }
 
+    public synchronized void setCookieSpecs(final CookieSpecRegistry registry) {
+        supportedCookieSpecs = registry;
+    }
 
     public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() {
         if (reuseStrategy == null) {
@@ -490,8 +523,8 @@ public abstract class AbstractHttpClient implements HttpClient {
     }
 
 
-    public synchronized void setReuseStrategy(final ConnectionReuseStrategy reuseStrategy) {
-        this.reuseStrategy = reuseStrategy;
+    public synchronized void setReuseStrategy(final ConnectionReuseStrategy strategy) {
+        this.reuseStrategy = strategy;
     }
 
 
@@ -503,8 +536,8 @@ public abstract class AbstractHttpClient implements HttpClient {
     }
 
 
-    public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy keepAliveStrategy) {
-        this.keepAliveStrategy = keepAliveStrategy;
+    public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy strategy) {
+        this.keepAliveStrategy = strategy;
     }
 
 
@@ -515,21 +548,24 @@ public abstract class AbstractHttpClient implements HttpClient {
         return retryHandler;
     }
 
-
-    public synchronized void setHttpRequestRetryHandler(final HttpRequestRetryHandler retryHandler) {
-        this.retryHandler = retryHandler;
+    public synchronized void setHttpRequestRetryHandler(final HttpRequestRetryHandler handler) {
+        this.retryHandler = handler;
     }
 
-
-    @Deprecated
+    /**
+     * @deprecated (4.1) do not use
+     */
+    @Deprecated 
     public synchronized final RedirectHandler getRedirectHandler() {
         return createRedirectHandler();
     }
 
-
-    @Deprecated
-    public synchronized void setRedirectHandler(final RedirectHandler redirectHandler) {
-        this.redirectStrategy = new DefaultRedirectStrategyAdaptor(redirectHandler);
+    /**
+     * @deprecated (4.1) do not use
+     */
+    @Deprecated 
+    public synchronized void setRedirectHandler(final RedirectHandler handler) {
+        this.redirectStrategy = new DefaultRedirectStrategyAdaptor(handler);
     }
 
     /**
@@ -545,38 +581,75 @@ public abstract class AbstractHttpClient implements HttpClient {
     /**
      * @since 4.1
      */
-    public synchronized void setRedirectStrategy(final RedirectStrategy redirectStrategy) {
-        this.redirectStrategy = redirectStrategy;
+    public synchronized void setRedirectStrategy(final RedirectStrategy strategy) {
+        this.redirectStrategy = strategy;
     }
 
-
+    /**
+     * @deprecated (4.2) do not use
+     */
+    @Deprecated 
     public synchronized final AuthenticationHandler getTargetAuthenticationHandler() {
-        if (targetAuthHandler == null) {
-            targetAuthHandler = createTargetAuthenticationHandler();
-        }
-        return targetAuthHandler;
+        return createTargetAuthenticationHandler();
     }
 
+    /**
+     * @deprecated (4.2) do not use
+     */
+    @Deprecated 
+    public synchronized void setTargetAuthenticationHandler(final AuthenticationHandler handler) {
+        this.targetAuthStrategy = new AuthenticationStrategyAdaptor(handler);
+    }
 
-    public synchronized void setTargetAuthenticationHandler(
-            final AuthenticationHandler targetAuthHandler) {
-        this.targetAuthHandler = targetAuthHandler;
+    /**
+     * @since 4.2
+     */
+    public synchronized final AuthenticationStrategy getTargetAuthenticationStrategy() {
+        if (targetAuthStrategy == null) {
+            targetAuthStrategy = createTargetAuthenticationStrategy();
+        }
+        return targetAuthStrategy;
     }
 
+    /**
+     * @since 4.2
+     */
+    public synchronized void setTargetAuthenticationStrategy(final AuthenticationStrategy strategy) {
+        this.targetAuthStrategy = strategy;
+    }
 
+    /**
+     * @deprecated (4.2) do not use
+     */
+    @Deprecated 
     public synchronized final AuthenticationHandler getProxyAuthenticationHandler() {
-        if (proxyAuthHandler == null) {
-            proxyAuthHandler = createProxyAuthenticationHandler();
-        }
-        return proxyAuthHandler;
+        return createProxyAuthenticationHandler();
     }
 
+    /**
+     * @deprecated (4.2) do not use
+     */
+    @Deprecated 
+    public synchronized void setProxyAuthenticationHandler(final AuthenticationHandler handler) {
+        this.proxyAuthStrategy = new AuthenticationStrategyAdaptor(handler);
+    }
 
-    public synchronized void setProxyAuthenticationHandler(
-            final AuthenticationHandler proxyAuthHandler) {
-        this.proxyAuthHandler = proxyAuthHandler;
+    /**
+     * @since 4.2
+     */
+    public synchronized final AuthenticationStrategy getProxyAuthenticationStrategy() {
+        if (proxyAuthStrategy == null) {
+            proxyAuthStrategy = createProxyAuthenticationStrategy();
+        }
+        return proxyAuthStrategy;
     }
 
+    /**
+     * @since 4.2
+     */
+    public synchronized void setProxyAuthenticationStrategy(final AuthenticationStrategy strategy) {
+        this.proxyAuthStrategy = strategy;
+    }
 
     public synchronized final CookieStore getCookieStore() {
         if (cookieStore == null) {
@@ -585,12 +658,10 @@ public abstract class AbstractHttpClient implements HttpClient {
         return cookieStore;
     }
 
-
     public synchronized void setCookieStore(final CookieStore cookieStore) {
         this.cookieStore = cookieStore;
     }
 
-
     public synchronized final CredentialsProvider getCredentialsProvider() {
         if (credsProvider == null) {
             credsProvider = createCredentialsProvider();
@@ -598,12 +669,10 @@ public abstract class AbstractHttpClient implements HttpClient {
         return credsProvider;
     }
 
-
     public synchronized void setCredentialsProvider(final CredentialsProvider credsProvider) {
         this.credsProvider = credsProvider;
     }
 
-
     public synchronized final HttpRoutePlanner getRoutePlanner() {
         if (this.routePlanner == null) {
             this.routePlanner = createHttpRoutePlanner();
@@ -611,12 +680,10 @@ public abstract class AbstractHttpClient implements HttpClient {
         return this.routePlanner;
     }
 
-
     public synchronized void setRoutePlanner(final HttpRoutePlanner routePlanner) {
         this.routePlanner = routePlanner;
     }
 
-
     public synchronized final UserTokenHandler getUserTokenHandler() {
         if (this.userTokenHandler == null) {
             this.userTokenHandler = createUserTokenHandler();
@@ -624,12 +691,10 @@ public abstract class AbstractHttpClient implements HttpClient {
         return this.userTokenHandler;
     }
 
-
-    public synchronized void setUserTokenHandler(final UserTokenHandler userTokenHandler) {
-        this.userTokenHandler = userTokenHandler;
+    public synchronized void setUserTokenHandler(final UserTokenHandler handler) {
+        this.userTokenHandler = handler;
     }
 
-
     protected synchronized final BasicHttpProcessor getHttpProcessor() {
         if (mutableProcessor == null) {
             mutableProcessor = createHttpProcessor();
@@ -637,7 +702,6 @@ public abstract class AbstractHttpClient implements HttpClient {
         return mutableProcessor;
     }
 
-
     private synchronized final HttpProcessor getProtocolProcessor() {
         if (protocolProcessor == null) {
             // Get mutable HTTP processor
@@ -658,69 +722,57 @@ public abstract class AbstractHttpClient implements HttpClient {
         return protocolProcessor;
     }
 
-
     public synchronized int getResponseInterceptorCount() {
         return getHttpProcessor().getResponseInterceptorCount();
     }
 
-
     public synchronized HttpResponseInterceptor getResponseInterceptor(int index) {
         return getHttpProcessor().getResponseInterceptor(index);
     }
 
-
     public synchronized HttpRequestInterceptor getRequestInterceptor(int index) {
         return getHttpProcessor().getRequestInterceptor(index);
     }
 
-
     public synchronized int getRequestInterceptorCount() {
         return getHttpProcessor().getRequestInterceptorCount();
     }
 
-
     public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp) {
         getHttpProcessor().addInterceptor(itcp);
         protocolProcessor = null;
     }
 
-
     public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp, int index) {
         getHttpProcessor().addInterceptor(itcp, index);
         protocolProcessor = null;
     }
 
-
     public synchronized void clearResponseInterceptors() {
         getHttpProcessor().clearResponseInterceptors();
         protocolProcessor = null;
     }
 
-
     public synchronized void removeResponseInterceptorByClass(Class<? extends HttpResponseInterceptor> clazz) {
         getHttpProcessor().removeResponseInterceptorByClass(clazz);
         protocolProcessor = null;
     }
 
-
     public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp) {
         getHttpProcessor().addInterceptor(itcp);
         protocolProcessor = null;
     }
 
-
     public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp, int index) {
         getHttpProcessor().addInterceptor(itcp, index);
         protocolProcessor = null;
     }
 
-
     public synchronized void clearRequestInterceptors() {
         getHttpProcessor().clearRequestInterceptors();
         protocolProcessor = null;
     }
 
-
     public synchronized void removeRequestInterceptorByClass(Class<? extends HttpRequestInterceptor> clazz) {
         getHttpProcessor().removeRequestInterceptorByClass(clazz);
         protocolProcessor = null;
@@ -732,7 +784,6 @@ public abstract class AbstractHttpClient implements HttpClient {
         return execute(request, (HttpContext) null);
     }
 
-
     /**
      * Maps to {@link HttpClient#execute(HttpHost,HttpRequest,HttpContext)
      *                           execute(target, request, context)}.
@@ -789,6 +840,9 @@ public abstract class AbstractHttpClient implements HttpClient {
 
         HttpContext execContext = null;
         RequestDirector director = null;
+        HttpRoutePlanner routePlanner = null;
+        ConnectionBackoffStrategy connectionBackoffStrategy = null;
+        BackoffManager backoffManager = null;
 
         // Initialize the request execution context making copies of
         // all shared objects that are potentially threading unsafe.
@@ -810,19 +864,55 @@ public abstract class AbstractHttpClient implements HttpClient {
                     getProtocolProcessor(),
                     getHttpRequestRetryHandler(),
                     getRedirectStrategy(),
-                    getTargetAuthenticationHandler(),
-                    getProxyAuthenticationHandler(),
+                    getTargetAuthenticationStrategy(),
+                    getProxyAuthenticationStrategy(),
                     getUserTokenHandler(),
                     determineParams(request));
+            routePlanner = getRoutePlanner();
+            connectionBackoffStrategy = getConnectionBackoffStrategy();
+            backoffManager = getBackoffManager();
         }
 
         try {
-            return director.execute(target, request, execContext);
+            if (connectionBackoffStrategy != null && backoffManager != null) {
+                HttpHost targetForRoute = (target != null) ? target
+                        : (HttpHost) determineParams(request).getParameter(
+                                ClientPNames.DEFAULT_HOST);
+                HttpRoute route = routePlanner.determineRoute(targetForRoute, request, execContext);
+
+                HttpResponse out;
+                try {
+                    out = director.execute(target, request, execContext);
+                } catch (RuntimeException re) {
+                    if (connectionBackoffStrategy.shouldBackoff(re)) {
+                        backoffManager.backOff(route);
+                    }
+                    throw re;
+                } catch (Exception e) {
+                    if (connectionBackoffStrategy.shouldBackoff(e)) {
+                        backoffManager.backOff(route);
+                    }
+                    if (e instanceof HttpException) throw (HttpException)e;
+                    if (e instanceof IOException) throw (IOException)e;
+                    throw new UndeclaredThrowableException(e);
+                }
+                if (connectionBackoffStrategy.shouldBackoff(out)) {
+                    backoffManager.backOff(route);
+                } else {
+                    backoffManager.probe(route);
+                }
+                return out;
+            } else {
+                return director.execute(target, request, execContext);
+            }
         } catch(HttpException httpException) {
             throw new ClientProtocolException(httpException);
         }
     }
 
+    /**
+     * @deprecated (4.1) do not use
+     */
     @Deprecated
     protected RequestDirector createClientRequestDirector(
             final HttpRequestExecutor requestExec,
@@ -832,10 +922,10 @@ public abstract class AbstractHttpClient implements HttpClient {
             final HttpRoutePlanner rouplan,
             final HttpProcessor httpProcessor,
             final HttpRequestRetryHandler retryHandler,
-            final org.apache.http.client.RedirectHandler redirectHandler,
+            final RedirectHandler redirectHandler,
             final AuthenticationHandler targetAuthHandler,
             final AuthenticationHandler proxyAuthHandler,
-            final UserTokenHandler stateHandler,
+            final UserTokenHandler userTokenHandler,
             final HttpParams params) {
         return new DefaultRequestDirector(
                 requestExec,
@@ -848,13 +938,14 @@ public abstract class AbstractHttpClient implements HttpClient {
                 redirectHandler,
                 targetAuthHandler,
                 proxyAuthHandler,
-                stateHandler,
+                userTokenHandler,
                 params);
     }
 
     /**
-     * @since 4.1
+     * @deprecated (4.2) do not use
      */
+    @Deprecated 
     protected RequestDirector createClientRequestDirector(
             final HttpRequestExecutor requestExec,
             final ClientConnectionManager conman,
@@ -866,7 +957,7 @@ public abstract class AbstractHttpClient implements HttpClient {
             final RedirectStrategy redirectStrategy,
             final AuthenticationHandler targetAuthHandler,
             final AuthenticationHandler proxyAuthHandler,
-            final UserTokenHandler stateHandler,
+            final UserTokenHandler userTokenHandler,
             final HttpParams params) {
         return new DefaultRequestDirector(
                 log,
@@ -880,9 +971,43 @@ public abstract class AbstractHttpClient implements HttpClient {
                 redirectStrategy,
                 targetAuthHandler,
                 proxyAuthHandler,
-                stateHandler,
+                userTokenHandler,
                 params);
     }
+
+
+    /**
+     * @since 4.2
+     */
+    protected RequestDirector createClientRequestDirector(
+            final HttpRequestExecutor requestExec,
+            final ClientConnectionManager conman,
+            final ConnectionReuseStrategy reustrat,
+            final ConnectionKeepAliveStrategy kastrat,
+            final HttpRoutePlanner rouplan,
+            final HttpProcessor httpProcessor,
+            final HttpRequestRetryHandler retryHandler,
+            final RedirectStrategy redirectStrategy,
+            final AuthenticationStrategy targetAuthStrategy,
+            final AuthenticationStrategy proxyAuthStrategy,
+            final UserTokenHandler userTokenHandler,
+            final HttpParams params) {
+        return new DefaultRequestDirector(
+                log,
+                requestExec,
+                conman,
+                reustrat,
+                kastrat,
+                rouplan,
+                httpProcessor,
+                retryHandler,
+                redirectStrategy,
+                targetAuthStrategy,
+                proxyAuthStrategy,
+                userTokenHandler,
+                params);
+    }
+
     /**
      * Obtains parameters for executing a request.
      * The default implementation in this class creates a new
@@ -943,7 +1068,7 @@ public abstract class AbstractHttpClient implements HttpClient {
         T result;
         try {
             result = responseHandler.handleResponse(response);
-        } catch (Throwable t) {
+        } catch (Exception t) {
             HttpEntity entity = response.getEntity();
             try {
                 EntityUtils.consume(entity);
@@ -952,19 +1077,12 @@ public abstract class AbstractHttpClient implements HttpClient {
                 // important and will be thrown to the caller.
                 this.log.warn("Error consuming content after an exception.", t2);
             }
-
-            if (t instanceof Error) {
-                throw (Error) t;
-            }
-
             if (t instanceof RuntimeException) {
                 throw (RuntimeException) t;
             }
-
             if (t instanceof IOException) {
                 throw (IOException) t;
             }
-
             throw new UndeclaredThrowableException(t);
         }
 
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyAdaptor.java b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyAdaptor.java
new file mode 100644
index 0000000..c7d5346
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyAdaptor.java
@@ -0,0 +1,179 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.LinkedList;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Queue;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.annotation.Immutable;
+import org.apache.http.auth.AuthOption;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.AuthenticationHandler;
+import org.apache.http.client.AuthenticationStrategy;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * @deprecated (4.2) do not use
+ */
+ at Immutable
+ at Deprecated
+class AuthenticationStrategyAdaptor implements AuthenticationStrategy {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    private final AuthenticationHandler handler;
+
+    public AuthenticationStrategyAdaptor(final AuthenticationHandler handler) {
+        super();
+        this.handler = handler;
+    }
+
+    public boolean isAuthenticationRequested(
+            final HttpHost authhost,
+            final HttpResponse response,
+            final HttpContext context) {
+        return this.handler.isAuthenticationRequested(response, context);
+    }
+
+    public Map<String, Header> getChallenges(
+            final HttpHost authhost,
+            final HttpResponse response,
+            final HttpContext context) throws MalformedChallengeException {
+        return this.handler.getChallenges(response, context);
+    }
+
+    public Queue<AuthOption> select(
+            final Map<String, Header> challenges,
+            final HttpHost authhost,
+            final HttpResponse response,
+            final HttpContext context) throws MalformedChallengeException {
+        if (challenges == null) {
+            throw new IllegalArgumentException("Map of auth challenges may not be null");
+        }
+        if (authhost == null) {
+            throw new IllegalArgumentException("Host may not be null");
+        }
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+
+        Queue<AuthOption> options = new LinkedList<AuthOption>();
+        CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
+                ClientContext.CREDS_PROVIDER);
+        if (credsProvider == null) {
+            this.log.debug("Credentials provider not set in the context");
+            return options;
+        }
+
+        AuthScheme authScheme;
+        try {
+            authScheme = this.handler.selectScheme(challenges, response, context);
+        } catch (AuthenticationException ex) {
+            if (this.log.isWarnEnabled()) {
+                this.log.warn(ex.getMessage(), ex);
+            }
+            return options;
+        }
+        String id = authScheme.getSchemeName();
+        Header challenge = challenges.get(id.toLowerCase(Locale.US));
+        authScheme.processChallenge(challenge);
+
+        AuthScope authScope = new AuthScope(
+                authhost.getHostName(),
+                authhost.getPort(),
+                authScheme.getRealm(),
+                authScheme.getSchemeName());
+
+        Credentials credentials = credsProvider.getCredentials(authScope);
+        if (credentials != null) {
+            options.add(new AuthOption(authScheme, credentials));
+        }
+        return options;
+    }
+
+    public void authSucceeded(
+            final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) {
+        AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
+        if (isCachable(authScheme)) {
+            if (authCache == null) {
+                authCache = new BasicAuthCache();
+                context.setAttribute(ClientContext.AUTH_CACHE, authCache);
+            }
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Caching '" + authScheme.getSchemeName() +
+                        "' auth scheme for " + authhost);
+            }
+            authCache.put(authhost, authScheme);
+        }
+    }
+
+    public void authFailed(
+            final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) {
+        AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
+        if (authCache == null) {
+            return;
+        }
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Removing from cache '" + authScheme.getSchemeName() +
+                    "' auth scheme for " + authhost);
+        }
+        authCache.remove(authhost);
+    }
+
+    private boolean isCachable(final AuthScheme authScheme) {
+        if (authScheme == null || !authScheme.isComplete()) {
+            return false;
+        }
+        String schemeName = authScheme.getSchemeName();
+        return schemeName.equalsIgnoreCase(AuthPolicy.BASIC) ||
+                schemeName.equalsIgnoreCase(AuthPolicy.DIGEST);
+    }
+
+    public AuthenticationHandler getHandler() {
+        return this.handler;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java
new file mode 100644
index 0000000..3d1ff95
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java
@@ -0,0 +1,260 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Queue;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.FormattedHeader;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.annotation.Immutable;
+import org.apache.http.auth.AuthOption;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.AuthenticationStrategy;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.CharArrayBuffer;
+
+ at Immutable
+class AuthenticationStrategyImpl implements AuthenticationStrategy {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    private static final List<String> DEFAULT_SCHEME_PRIORITY =
+        Collections.unmodifiableList(Arrays.asList(new String[] {
+                AuthPolicy.SPNEGO,
+                AuthPolicy.KERBEROS,
+                AuthPolicy.NTLM,
+                AuthPolicy.DIGEST,
+                AuthPolicy.BASIC
+    }));
+
+    private final int challengeCode;
+    private final String headerName;
+    private final String prefParamName;
+
+    AuthenticationStrategyImpl(int challengeCode, final String headerName, final String prefParamName) {
+        super();
+        this.challengeCode = challengeCode;
+        this.headerName = headerName;
+        this.prefParamName = prefParamName;
+    }
+
+    public boolean isAuthenticationRequested(
+            final HttpHost authhost,
+            final HttpResponse response,
+            final HttpContext context) {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        int status = response.getStatusLine().getStatusCode();
+        return status == this.challengeCode;
+    }
+
+    public Map<String, Header> getChallenges(
+            final HttpHost authhost,
+            final HttpResponse response,
+            final HttpContext context) throws MalformedChallengeException {
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        Header[] headers = response.getHeaders(this.headerName);
+        Map<String, Header> map = new HashMap<String, Header>(headers.length);
+        for (Header header : headers) {
+            CharArrayBuffer buffer;
+            int pos;
+            if (header instanceof FormattedHeader) {
+                buffer = ((FormattedHeader) header).getBuffer();
+                pos = ((FormattedHeader) header).getValuePos();
+            } else {
+                String s = header.getValue();
+                if (s == null) {
+                    throw new MalformedChallengeException("Header value is null");
+                }
+                buffer = new CharArrayBuffer(s.length());
+                buffer.append(s);
+                pos = 0;
+            }
+            while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) {
+                pos++;
+            }
+            int beginIndex = pos;
+            while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
+                pos++;
+            }
+            int endIndex = pos;
+            String s = buffer.substring(beginIndex, endIndex);
+            map.put(s.toLowerCase(Locale.US), header);
+        }
+        return map;
+    }
+
+    public Queue<AuthOption> select(
+            final Map<String, Header> challenges,
+            final HttpHost authhost,
+            final HttpResponse response,
+            final HttpContext context) throws MalformedChallengeException {
+        if (challenges == null) {
+            throw new IllegalArgumentException("Map of auth challenges may not be null");
+        }
+        if (authhost == null) {
+            throw new IllegalArgumentException("Host may not be null");
+        }
+        if (response == null) {
+            throw new IllegalArgumentException("HTTP response may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+
+        Queue<AuthOption> options = new LinkedList<AuthOption>();
+        AuthSchemeRegistry registry = (AuthSchemeRegistry) context.getAttribute(
+                ClientContext.AUTHSCHEME_REGISTRY);
+        if (registry == null) {
+            this.log.debug("Auth scheme registry not set in the context");
+            return options;
+        }
+        CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
+                ClientContext.CREDS_PROVIDER);
+        if (credsProvider == null) {
+            this.log.debug("Credentials provider not set in the context");
+            return options;
+        }
+
+        @SuppressWarnings("unchecked")
+        List<String> authPrefs = (List<String>) response.getParams().getParameter(this.prefParamName);
+        if (authPrefs == null) {
+            authPrefs = DEFAULT_SCHEME_PRIORITY;
+        }
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Authentication schemes in the order of preference: " + authPrefs);
+        }
+
+        for (String id: authPrefs) {
+            Header challenge = challenges.get(id.toLowerCase(Locale.US));
+            if (challenge != null) {
+                try {
+                    AuthScheme authScheme = registry.getAuthScheme(id, response.getParams());
+                    authScheme.processChallenge(challenge);
+
+                    AuthScope authScope = new AuthScope(
+                            authhost.getHostName(),
+                            authhost.getPort(),
+                            authScheme.getRealm(),
+                            authScheme.getSchemeName());
+
+                    Credentials credentials = credsProvider.getCredentials(authScope);
+                    if (credentials != null) {
+                        options.add(new AuthOption(authScheme, credentials));
+                    }
+                } catch (IllegalStateException e) {
+                    if (this.log.isWarnEnabled()) {
+                        this.log.warn("Authentication scheme " + id + " not supported");
+                        // Try again
+                    }
+                }
+            } else {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("Challenge for " + id + " authentication scheme not available");
+                    // Try again
+                }
+            }
+        }
+        return options;
+    }
+
+    public void authSucceeded(
+            final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) {
+        if (authhost == null) {
+            throw new IllegalArgumentException("Host may not be null");
+        }
+        if (authScheme == null) {
+            throw new IllegalArgumentException("Auth scheme may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        if (isCachable(authScheme)) {
+            AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
+            if (authCache == null) {
+                authCache = new BasicAuthCache();
+                context.setAttribute(ClientContext.AUTH_CACHE, authCache);
+            }
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Caching '" + authScheme.getSchemeName() +
+                        "' auth scheme for " + authhost);
+            }
+            authCache.put(authhost, authScheme);
+        }
+    }
+
+    protected boolean isCachable(final AuthScheme authScheme) {
+        if (authScheme == null || !authScheme.isComplete()) {
+            return false;
+        }
+        String schemeName = authScheme.getSchemeName();
+        return schemeName.equalsIgnoreCase(AuthPolicy.BASIC) ||
+                schemeName.equalsIgnoreCase(AuthPolicy.DIGEST);
+    }
+
+    public void authFailed(
+            final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) {
+        if (authhost == null) {
+            throw new IllegalArgumentException("Host may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
+        AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
+        if (authCache != null) {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Clearing cached auth scheme for " + authhost);
+            }
+            authCache.remove(authhost);
+        }
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AutoRetryHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/AutoRetryHttpClient.java
new file mode 100644
index 0000000..7eb72c1
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/AutoRetryHttpClient.java
@@ -0,0 +1,179 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.URI;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.annotation.ThreadSafe;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.ServiceUnavailableRetryStrategy;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * {@link HttpClient} implementation that can automatically retry the request in case of
+ * a non-2xx response using the {@link ServiceUnavailableRetryStrategy} interface.
+ *
+ * @since 4.2
+ */
+ at ThreadSafe
+public class AutoRetryHttpClient implements HttpClient {
+
+    private final HttpClient backend;
+
+    private final ServiceUnavailableRetryStrategy retryStrategy;
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    public AutoRetryHttpClient(
+            final HttpClient client, final ServiceUnavailableRetryStrategy retryStrategy) {
+        super();
+        if (client == null) {
+            throw new IllegalArgumentException("HttpClient may not be null");
+        }
+        if (retryStrategy == null) {
+            throw new IllegalArgumentException(
+                    "ServiceUnavailableRetryStrategy may not be null");
+        }
+        this.backend = client;
+        this.retryStrategy = retryStrategy;
+    }
+
+    /**
+     * Constructs a {@code AutoRetryHttpClient} with default caching settings that
+     * stores cache entries in memory and uses a vanilla
+     * {@link DefaultHttpClient} for backend requests.
+     */
+    public AutoRetryHttpClient() {
+        this(new DefaultHttpClient(), new DefaultServiceUnavailableRetryStrategy());
+    }
+
+    /**
+     * Constructs a {@code AutoRetryHttpClient} with the given caching options that
+     * stores cache entries in memory and uses a vanilla
+     * {@link DefaultHttpClient} for backend requests.
+     *
+     * @param config
+     *            retry configuration module options
+     */
+    public AutoRetryHttpClient(ServiceUnavailableRetryStrategy config) {
+        this(new DefaultHttpClient(), config);
+    }
+
+    /**
+     * Constructs a {@code AutoRetryHttpClient} with default caching settings that
+     * stores cache entries in memory and uses the given {@link HttpClient} for
+     * backend requests.
+     *
+     * @param client
+     *            used to make origin requests
+     */
+    public AutoRetryHttpClient(HttpClient client) {
+        this(client, new DefaultServiceUnavailableRetryStrategy());
+    }
+
+    public HttpResponse execute(HttpHost target, HttpRequest request)
+            throws IOException {
+        HttpContext defaultContext = null;
+        return execute(target, request, defaultContext);
+    }
+
+    public <T> T execute(HttpHost target, HttpRequest request,
+            ResponseHandler<? extends T> responseHandler) throws IOException {
+        return execute(target, request, responseHandler, null);
+    }
+
+    public <T> T execute(HttpHost target, HttpRequest request,
+            ResponseHandler<? extends T> responseHandler, HttpContext context)
+            throws IOException {
+        HttpResponse resp = execute(target, request, context);
+        return responseHandler.handleResponse(resp);
+    }
+
+    public HttpResponse execute(HttpUriRequest request) throws IOException {
+        HttpContext context = null;
+        return execute(request, context);
+    }
+
+    public HttpResponse execute(HttpUriRequest request, HttpContext context)
+            throws IOException {
+        URI uri = request.getURI();
+        HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort(),
+                uri.getScheme());
+        return execute(httpHost, request, context);
+    }
+
+    public <T> T execute(HttpUriRequest request,
+            ResponseHandler<? extends T> responseHandler) throws IOException {
+        return execute(request, responseHandler, null);
+    }
+
+    public <T> T execute(HttpUriRequest request,
+            ResponseHandler<? extends T> responseHandler, HttpContext context)
+            throws IOException {
+        HttpResponse resp = execute(request, context);
+        return responseHandler.handleResponse(resp);
+    }
+
+    public HttpResponse execute(HttpHost target, HttpRequest request,
+            HttpContext context) throws IOException {
+        for (int c = 1;; c++) {
+            HttpResponse response = backend.execute(target, request, context);
+            if (retryStrategy.retryRequest(response, c, context)) {
+                long nextInterval = retryStrategy.getRetryInterval();
+                try {
+                    log.trace("Wait for " + nextInterval);
+                    Thread.sleep(nextInterval);
+                } catch (InterruptedException e) {
+                    throw new InterruptedIOException(e.getMessage());
+                }
+            } else {
+                return response;
+            }
+        }
+    }
+
+    public ClientConnectionManager getConnectionManager() {
+        return backend.getConnectionManager();
+    }
+
+    public HttpParams getParams() {
+        return backend.getParams();
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/BasicCookieStore.java b/httpclient/src/main/java/org/apache/http/impl/client/BasicCookieStore.java
index 09fc020..7c67aa4 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/BasicCookieStore.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/BasicCookieStore.java
@@ -27,7 +27,11 @@
 package org.apache.http.impl.client;
 
 import java.io.Serializable;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeSet;
 
 import org.apache.http.annotation.GuardedBy;
 import org.apache.http.annotation.ThreadSafe;
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/BasicResponseHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/BasicResponseHandler.java
index 5ad5427..54df962 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/BasicResponseHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/BasicResponseHandler.java
@@ -42,13 +42,12 @@ import org.apache.http.util.EntityUtils;
  * A {@link ResponseHandler} that returns the response body as a String
  * for successful (2xx) responses. If the response code was >= 300, the response
  * body is consumed and an {@link HttpResponseException} is thrown.
- *
+ * <p/>
  * If this is used with
  * {@link org.apache.http.client.HttpClient#execute(
  *  org.apache.http.client.methods.HttpUriRequest, ResponseHandler)},
  * HttpClient may handle redirects (3xx responses) internally.
  *
- *
  * @since 4.0
  */
 @Immutable
@@ -63,12 +62,12 @@ public class BasicResponseHandler implements ResponseHandler<String> {
     public String handleResponse(final HttpResponse response)
             throws HttpResponseException, IOException {
         StatusLine statusLine = response.getStatusLine();
+        HttpEntity entity = response.getEntity();
         if (statusLine.getStatusCode() >= 300) {
+            EntityUtils.consume(entity);
             throw new HttpResponseException(statusLine.getStatusCode(),
                     statusLine.getReasonPhrase());
         }
-
-        HttpEntity entity = response.getEntity();
         return entity == null ? null : EntityUtils.toString(entity);
     }
 
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/Clock.java b/httpclient/src/main/java/org/apache/http/impl/client/Clock.java
new file mode 100644
index 0000000..e3ef4bc
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/Clock.java
@@ -0,0 +1,42 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+/**
+ * Interface used to enable easier testing of time-related behavior.
+ *
+ * @since 4.2
+ *
+ */
+interface Clock {
+
+    /**
+     * Returns the current time, expressed as the number of
+     * milliseconds since the epoch.
+     * @return current time
+     */
+    long getCurrentTime();
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/ContentEncodingHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/ContentEncodingHttpClient.java
index 82e3a68..2a08d99 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/ContentEncodingHttpClient.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/ContentEncodingHttpClient.java
@@ -27,6 +27,7 @@
 package org.apache.http.impl.client;
 
 import org.apache.http.annotation.ThreadSafe;
+import org.apache.http.client.HttpClient;
 import org.apache.http.client.protocol.RequestAcceptEncoding;
 import org.apache.http.client.protocol.ResponseContentEncoding;
 import org.apache.http.conn.ClientConnectionManager;
@@ -36,9 +37,18 @@ import org.apache.http.protocol.BasicHttpProcessor;
 /**
  * {@link DefaultHttpClient} sub-class which includes a {@link RequestAcceptEncoding}
  * for the request and response.
+ * 
+ * <b>Deprecation note:</b> due to the way this class modifies a response body
+ * without changing the response headers to reflect the entity changes, it cannot
+ * be used as the "backend" for a caching {@link HttpClient} and still
+ * have uncompressed responses be cached. Users are encouraged to use the
+ * {@link DecompressingHttpClient} instead of this class, which can be wired in
+ * either before or after caching, depending on whether you want to cache
+ * responses in compressed or uncompressed form.
  *
  * @since 4.1
  */
+ at Deprecated
 @ThreadSafe // since DefaultHttpClient is
 public class ContentEncodingHttpClient extends DefaultHttpClient {
 
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DecompressingHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/DecompressingHttpClient.java
new file mode 100644
index 0000000..26307a1
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DecompressingHttpClient.java
@@ -0,0 +1,180 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+import java.net.URI;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.RequestAcceptEncoding;
+import org.apache.http.client.protocol.ResponseContentEncoding;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * <p>Decorator adding support for compressed responses. This class sets
+ * the <code>Accept-Encoding</code> header on requests to indicate
+ * support for the <code>gzip</code> and <code>deflate</code>
+ * compression schemes; it then checks the <code>Content-Encoding</code>
+ * header on the response to uncompress any compressed response bodies.
+ * The {@link java.io.InputStream} of the entity will contain the uncompressed
+ * content.</p>
+ * 
+ * <p><b>N.B.</b> Any upstream clients of this class need to be aware that
+ * this effectively obscures visibility into the length of a server
+ * response body, since the <code>Content-Length</code> header will
+ * correspond to the compressed entity length received from the server,
+ * but the content length experienced by reading the response body may
+ * be different (hopefully higher!).</p>
+ * 
+ * <p>That said, this decorator is compatible with the 
+ * <code>CachingHttpClient</code> in that the two decorators can be added 
+ * in either order and still have cacheable responses be cached.</p> 
+ * 
+ * @since 4.2
+ */
+public class DecompressingHttpClient implements HttpClient {
+
+    private HttpClient backend;
+    private HttpRequestInterceptor acceptEncodingInterceptor;
+    private HttpResponseInterceptor contentEncodingInterceptor;
+    
+    /**
+     * Constructs a decorator to ask for and handle compressed
+     * entities on the fly.
+     * @param backend the {@link HttpClient} to use for actually
+     *   issuing requests
+     */
+    public DecompressingHttpClient(HttpClient backend) {
+        this(backend, new RequestAcceptEncoding(), new ResponseContentEncoding());
+    }
+    
+    DecompressingHttpClient(HttpClient backend, 
+            HttpRequestInterceptor requestInterceptor, 
+            HttpResponseInterceptor responseInterceptor) {
+        this.backend = backend;
+        this.acceptEncodingInterceptor = requestInterceptor;
+        this.contentEncodingInterceptor = responseInterceptor;
+    }
+
+    public HttpParams getParams() {
+        return backend.getParams();
+    }
+
+    public ClientConnectionManager getConnectionManager() {
+        return backend.getConnectionManager();
+    }
+
+    public HttpResponse execute(HttpUriRequest request) throws IOException,
+            ClientProtocolException {
+        return execute(getHttpHost(request), request, (HttpContext)null);
+    }
+
+    HttpHost getHttpHost(HttpUriRequest request) {
+        URI uri = request.getURI();
+        return URIUtils.extractHost(uri);
+    }
+
+    public HttpResponse execute(HttpUriRequest request, HttpContext context)
+            throws IOException, ClientProtocolException {
+        return execute(getHttpHost(request), request, context);
+    }
+
+    public HttpResponse execute(HttpHost target, HttpRequest request)
+            throws IOException, ClientProtocolException {
+        return execute(target, request, (HttpContext)null);
+    }
+
+    public HttpResponse execute(HttpHost target, HttpRequest request,
+            HttpContext context) throws IOException, ClientProtocolException {
+        try {
+            if (context == null) context = new BasicHttpContext();
+            HttpRequest wrapped;
+            if (request instanceof HttpEntityEnclosingRequest) {
+                wrapped = new EntityEnclosingRequestWrapper((HttpEntityEnclosingRequest) request);
+            } else {
+                wrapped = new RequestWrapper(request);
+            }
+            acceptEncodingInterceptor.process(wrapped, context);
+            HttpResponse response = backend.execute(target, wrapped, context);
+            contentEncodingInterceptor.process(response, context);
+            if (Boolean.TRUE.equals(context.getAttribute(ResponseContentEncoding.UNCOMPRESSED))) {
+                response.removeHeaders("Content-Length");
+                response.removeHeaders("Content-Encoding");
+                response.removeHeaders("Content-MD5");
+            }
+            return response;
+        } catch (HttpException e) {
+            throw new ClientProtocolException(e);
+        }
+    }
+
+    public <T> T execute(HttpUriRequest request,
+            ResponseHandler<? extends T> responseHandler) throws IOException,
+            ClientProtocolException {
+        return execute(getHttpHost(request), request, responseHandler);
+    }
+
+    public <T> T execute(HttpUriRequest request,
+            ResponseHandler<? extends T> responseHandler, HttpContext context)
+            throws IOException, ClientProtocolException {
+        return execute(getHttpHost(request), request, responseHandler, context);
+    }
+
+    public <T> T execute(HttpHost target, HttpRequest request,
+            ResponseHandler<? extends T> responseHandler) throws IOException,
+            ClientProtocolException {
+        return execute(target, request, responseHandler, null);
+    }
+
+    public <T> T execute(HttpHost target, HttpRequest request,
+            ResponseHandler<? extends T> responseHandler, HttpContext context)
+            throws IOException, ClientProtocolException {
+        HttpResponse response = execute(target, request, context);
+        try {
+            return responseHandler.handleResponse(response);
+        } finally {
+            HttpEntity entity = response.getEntity();
+            if (entity != null) EntityUtils.consume(entity);
+        }
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultBackoffStrategy.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultBackoffStrategy.java
new file mode 100644
index 0000000..7d45ca4
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultBackoffStrategy.java
@@ -0,0 +1,53 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ConnectionBackoffStrategy;
+
+/**
+ * This {@link ConnectionBackoffStrategy} backs off either for a raw
+ * network socket or connection timeout or if the server explicitly
+ * sends a 503 (Service Unavailable) response.
+ *
+ * @since 4.2
+ */
+public class DefaultBackoffStrategy implements ConnectionBackoffStrategy {
+
+    public boolean shouldBackoff(Throwable t) {
+        return (t instanceof SocketTimeoutException
+                || t instanceof ConnectException);
+    }
+
+    public boolean shouldBackoff(HttpResponse resp) {
+        return (resp.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE);
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java
index 872eac8..e4eb54f 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java
@@ -36,7 +36,6 @@ import org.apache.http.client.protocol.RequestClientConnControl;
 import org.apache.http.client.protocol.RequestDefaultHeaders;
 import org.apache.http.client.protocol.RequestProxyAuthentication;
 import org.apache.http.client.protocol.RequestTargetAuthentication;
-import org.apache.http.client.protocol.ResponseAuthCache;
 import org.apache.http.client.protocol.ResponseProcessCookies;
 import org.apache.http.conn.ClientConnectionManager;
 import org.apache.http.params.CoreConnectionPNames;
@@ -56,19 +55,16 @@ import org.apache.http.util.VersionInfo;
 /**
  * Default implementation of {@link HttpClient} pre-configured for most common use scenarios.
  * <p>
- * This class creates the following chain of protocol interceptors per default:
- * <ul>
- * <li>{@link RequestDefaultHeaders}</li>
- * <li>{@link RequestContent}</li>
- * <li>{@link RequestTargetHost}</li>
- * <li>{@link RequestClientConnControl}</li>
- * <li>{@link RequestUserAgent}</li>
- * <li>{@link RequestExpectContinue}</li>
- * <li>{@link RequestAddCookies}</li>
- * <li>{@link ResponseProcessCookies}</li>
- * <li>{@link RequestTargetAuthentication}</li>
- * <li>{@link RequestProxyAuthentication}</li>
- * </ul>
+ * Please see the Javadoc for {@link #createHttpProcessor()} for the details of the interceptors
+ * that are set up by default.
+ * <p>
+ * Additional interceptors can be added as follows, but
+ * take care not to add the same interceptor more than once.
+ * <pre>
+ * DefaultHttpClient httpclient = new DefaultHttpClient();
+ * httpclient.addRequestInterceptor(new RequestAcceptEncoding());
+ * httpclient.addResponseInterceptor(new ResponseContentEncoding());
+ * </pre>
  * <p>
  * This class sets up the following parameters if not explicitly set:
  * <ul>
@@ -111,7 +107,7 @@ import org.apache.http.util.VersionInfo;
  *  <li>{@link org.apache.http.client.params.ClientPNames#VIRTUAL_HOST}</li>
  *  <li>{@link org.apache.http.client.params.ClientPNames#DEFAULT_HOST}</li>
  *  <li>{@link org.apache.http.client.params.ClientPNames#DEFAULT_HEADERS}</li>
- *  <li>{@link org.apache.http.client.params.ClientPNames#CONNECTION_MANAGER_FACTORY_CLASS_NAME}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#CONN_MANAGER_TIMEOUT}</li>
  * </ul>
  *
  * @since 4.0
@@ -176,7 +172,7 @@ public class DefaultHttpClient extends AbstractHttpClient {
      */
     public static void setDefaultHttpParams(HttpParams params) {
         HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
-        HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
+        HttpProtocolParams.setContentCharset(params, HTTP.DEF_CONTENT_CHARSET.name());
         HttpConnectionParams.setTcpNoDelay(params, true);
         HttpConnectionParams.setSocketBufferSize(params, 8192);
 
@@ -189,7 +185,24 @@ public class DefaultHttpClient extends AbstractHttpClient {
                 "Apache-HttpClient/" + release + " (java 1.5)");
     }
 
-
+    /**
+    * Create the processor with the following interceptors:
+    * <ul>
+    * <li>{@link RequestDefaultHeaders}</li>
+    * <li>{@link RequestContent}</li>
+    * <li>{@link RequestTargetHost}</li>
+    * <li>{@link RequestClientConnControl}</li>
+    * <li>{@link RequestUserAgent}</li>
+    * <li>{@link RequestExpectContinue}</li>
+    * <li>{@link RequestAddCookies}</li>
+    * <li>{@link ResponseProcessCookies}</li>
+    * <li>{@link RequestAuthCache}</li>
+    * <li>{@link RequestTargetAuthentication}</li>
+    * <li>{@link RequestProxyAuthentication}</li>
+    * </ul>
+    * <p>
+    * @return the processor with the added interceptors.
+    */
     @Override
     protected BasicHttpProcessor createHttpProcessor() {
         BasicHttpProcessor httpproc = new BasicHttpProcessor();
@@ -206,7 +219,6 @@ public class DefaultHttpClient extends AbstractHttpClient {
         httpproc.addInterceptor(new ResponseProcessCookies());
         // HTTP authentication interceptors
         httpproc.addInterceptor(new RequestAuthCache());
-        httpproc.addInterceptor(new ResponseAuthCache());
         httpproc.addInterceptor(new RequestTargetAuthentication());
         httpproc.addInterceptor(new RequestProxyAuthentication());
         return httpproc;
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java
index 9cfecc7..d50e5f6 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.java
@@ -41,6 +41,7 @@ import org.apache.http.HttpRequest;
 import org.apache.http.client.HttpRequestRetryHandler;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.client.methods.HttpUriRequest;
 
 /**
  * The default {@link HttpRequestRetryHandler} used by request executors.
@@ -109,6 +110,11 @@ public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler {
 
         HttpRequest request = (HttpRequest)
             context.getAttribute(ExecutionContext.HTTP_REQUEST);
+
+        if(requestIsAborted(request)){
+            return false;
+        }
+
         if (handleAsIdempotent(request)) {
             // Retry if the request is considered idempotent
             return true;
@@ -142,8 +148,22 @@ public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler {
         return retryCount;
     }
 
-    private boolean handleAsIdempotent(final HttpRequest request) {
+    /**
+     * @since 4.2
+     */
+    protected boolean handleAsIdempotent(final HttpRequest request) {
         return !(request instanceof HttpEntityEnclosingRequest);
     }
 
+    /**
+     * @since 4.2
+     */
+    protected boolean requestIsAborted(final HttpRequest request) {
+        HttpRequest req = request;
+        if (request instanceof RequestWrapper) { // does not forward request to original
+            req = ((RequestWrapper) request).getOriginal();
+        }
+        return (req instanceof HttpUriRequest && ((HttpUriRequest)req).isAborted());
+    }
+
 }
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java
index e3a96d1..345c347 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java
@@ -46,7 +46,10 @@ import org.apache.http.protocol.HttpContext;
  * authentication.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link ProxyAuthenticationStrategy}
  */
+ at Deprecated 
 @Immutable
 public class DefaultProxyAuthenticationHandler extends AbstractAuthenticationHandler {
 
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectHandler.java
index 850a33b..4b1a3f3 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectHandler.java
@@ -55,7 +55,7 @@ import org.apache.http.protocol.ExecutionContext;
  *
  * @since 4.0
  *
- * @deprecated use {@link DefaultRedirectStrategy}.
+ * @deprecated (4.1)  use {@link DefaultRedirectStrategy}.
  */
 @Immutable
 @Deprecated
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategy.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategy.java
index c41ee38..bdec33f 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategy.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategy.java
@@ -52,8 +52,17 @@ import org.apache.http.protocol.HttpContext;
 import org.apache.http.protocol.ExecutionContext;
 
 /**
- * Default implementation of {@link RedirectStrategy}.
+ * Default implementation of {@link RedirectStrategy}. This strategy honors the restrictions
+ * on automatic redirection of entity enclosing methods such as POST and PUT imposed by the
+ * HTTP specification. <tt>302 Moved Temporarily</tt>, <tt>301 Moved Permanently</tt> and
+ * <tt>307 Temporary Redirect</tt> status codes will result in an automatic redirect of
+ * HEAD and GET methods only. POST and PUT methods will not be automatically redirected
+ * as requiring user confirmation.
+ * <p/>
+ * The restriction on automatic redirection of POST methods can be relaxed by using
+ * {@link LaxRedirectStrategy} instead of {@link DefaultRedirectStrategy}.
  *
+ * @see LaxRedirectStrategy
  * @since 4.1
  */
 @Immutable
@@ -63,6 +72,14 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
 
     public static final String REDIRECT_LOCATIONS = "http.protocol.redirect-locations";
 
+    /**
+     * Redirectable methods.
+     */
+    private static final String[] REDIRECT_METHODS = new String[] {
+        HttpGet.METHOD_NAME,
+        HttpHead.METHOD_NAME
+    };
+
     public DefaultRedirectStrategy() {
         super();
     }
@@ -71,6 +88,9 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
             final HttpRequest request,
             final HttpResponse response,
             final HttpContext context) throws ProtocolException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
         if (response == null) {
             throw new IllegalArgumentException("HTTP response may not be null");
         }
@@ -80,12 +100,10 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
         Header locationHeader = response.getFirstHeader("location");
         switch (statusCode) {
         case HttpStatus.SC_MOVED_TEMPORARILY:
-            return (method.equalsIgnoreCase(HttpGet.METHOD_NAME)
-                || method.equalsIgnoreCase(HttpHead.METHOD_NAME)) && locationHeader != null;
+            return isRedirectable(method) && locationHeader != null;
         case HttpStatus.SC_MOVED_PERMANENTLY:
         case HttpStatus.SC_TEMPORARY_REDIRECT:
-            return method.equalsIgnoreCase(HttpGet.METHOD_NAME)
-                || method.equalsIgnoreCase(HttpHead.METHOD_NAME);
+            return isRedirectable(method);
         case HttpStatus.SC_SEE_OTHER:
             return true;
         default:
@@ -97,9 +115,15 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
             final HttpRequest request,
             final HttpResponse response,
             final HttpContext context) throws ProtocolException {
+        if (request == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
         if (response == null) {
             throw new IllegalArgumentException("HTTP response may not be null");
         }
+        if (context == null) {
+            throw new IllegalArgumentException("HTTP context may not be null");
+        }
         //get the location header to find out where to redirect to
         Header locationHeader = response.getFirstHeader("location");
         if (locationHeader == null) {
@@ -115,63 +139,43 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
 
         URI uri = createLocationURI(location);
 
-        HttpParams params = response.getParams();
+        HttpParams params = request.getParams();
         // rfc2616 demands the location value be a complete URI
         // Location       = "Location" ":" absoluteURI
-        if (!uri.isAbsolute()) {
-            if (params.isParameterTrue(ClientPNames.REJECT_RELATIVE_REDIRECT)) {
-                throw new ProtocolException("Relative redirect location '"
-                        + uri + "' not allowed");
-            }
-            // Adjust location URI
-            HttpHost target = (HttpHost) context.getAttribute(
-                    ExecutionContext.HTTP_TARGET_HOST);
-            if (target == null) {
-                throw new IllegalStateException("Target host not available " +
-                        "in the HTTP context");
-            }
-            try {
+        try {
+            // Drop fragment
+            uri = URIUtils.rewriteURI(uri);
+            if (!uri.isAbsolute()) {
+                if (params.isParameterTrue(ClientPNames.REJECT_RELATIVE_REDIRECT)) {
+                    throw new ProtocolException("Relative redirect location '"
+                            + uri + "' not allowed");
+                }
+                // Adjust location URI
+                HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
+                if (target == null) {
+                    throw new IllegalStateException("Target host not available " +
+                            "in the HTTP context");
+                }
                 URI requestURI = new URI(request.getRequestLine().getUri());
                 URI absoluteRequestURI = URIUtils.rewriteURI(requestURI, target, true);
                 uri = URIUtils.resolve(absoluteRequestURI, uri);
-            } catch (URISyntaxException ex) {
-                throw new ProtocolException(ex.getMessage(), ex);
             }
+        } catch (URISyntaxException ex) {
+            throw new ProtocolException(ex.getMessage(), ex);
         }
 
+        RedirectLocations redirectLocations = (RedirectLocations) context.getAttribute(
+                REDIRECT_LOCATIONS);
+        if (redirectLocations == null) {
+            redirectLocations = new RedirectLocations();
+            context.setAttribute(REDIRECT_LOCATIONS, redirectLocations);
+        }
         if (params.isParameterFalse(ClientPNames.ALLOW_CIRCULAR_REDIRECTS)) {
-
-            RedirectLocations redirectLocations = (RedirectLocations) context.getAttribute(
-                    REDIRECT_LOCATIONS);
-
-            if (redirectLocations == null) {
-                redirectLocations = new RedirectLocations();
-                context.setAttribute(REDIRECT_LOCATIONS, redirectLocations);
-            }
-
-            URI redirectURI;
-            if (uri.getFragment() != null) {
-                try {
-                    HttpHost target = new HttpHost(
-                            uri.getHost(),
-                            uri.getPort(),
-                            uri.getScheme());
-                    redirectURI = URIUtils.rewriteURI(uri, target, true);
-                } catch (URISyntaxException ex) {
-                    throw new ProtocolException(ex.getMessage(), ex);
-                }
-            } else {
-                redirectURI = uri;
-            }
-
-            if (redirectLocations.contains(redirectURI)) {
-                throw new CircularRedirectException("Circular redirect to '" +
-                        redirectURI + "'");
-            } else {
-                redirectLocations.add(redirectURI);
+            if (redirectLocations.contains(uri)) {
+                throw new CircularRedirectException("Circular redirect to '" + uri + "'");
             }
         }
-
+        redirectLocations.add(uri);
         return uri;
     }
 
@@ -180,12 +184,24 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
      */
     protected URI createLocationURI(final String location) throws ProtocolException {
         try {
-            return new URI(location);
+            return new URI(location).normalize();
         } catch (URISyntaxException ex) {
             throw new ProtocolException("Invalid redirect URI: " + location, ex);
         }
     }
 
+    /**
+     * @since 4.2
+     */
+    protected boolean isRedirectable(final String method) {
+        for (String m: REDIRECT_METHODS) {
+            if (m.equalsIgnoreCase(method)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public HttpUriRequest getRedirect(
             final HttpRequest request,
             final HttpResponse response,
@@ -199,4 +215,4 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
         }
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategyAdaptor.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategyAdaptor.java
index a4da7fd..7a592aa 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategyAdaptor.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategyAdaptor.java
@@ -33,6 +33,7 @@ import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.ProtocolException;
 import org.apache.http.annotation.Immutable;
+import org.apache.http.client.RedirectHandler;
 import org.apache.http.client.RedirectStrategy;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpHead;
@@ -40,16 +41,15 @@ import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.protocol.HttpContext;
 
 /**
- * @since 4.1
+ * @deprecated (4.1) do not use
  */
 @Immutable
- at Deprecated
+ at Deprecated 
 class DefaultRedirectStrategyAdaptor implements RedirectStrategy {
 
-    private final org.apache.http.client.RedirectHandler handler;
+    private final RedirectHandler handler;
 
-    @Deprecated
-    public DefaultRedirectStrategyAdaptor(final org.apache.http.client.RedirectHandler handler) {
+    public DefaultRedirectStrategyAdaptor(final RedirectHandler handler) {
         super();
         this.handler = handler;
     }
@@ -74,4 +74,8 @@ class DefaultRedirectStrategyAdaptor implements RedirectStrategy {
         }
     }
 
+    public RedirectHandler getHandler() {
+        return this.handler;
+    }
+
 }
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRequestDirector.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRequestDirector.java
index d9818f1..c2e9558 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRequestDirector.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRequestDirector.java
@@ -31,16 +31,11 @@ import java.io.IOException;
 import java.io.InterruptedIOException;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.Locale;
-import java.util.Map;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.http.annotation.NotThreadSafe;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.http.ConnectionReuseStrategy;
-import org.apache.http.Header;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpException;
@@ -49,19 +44,19 @@ import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.ProtocolException;
 import org.apache.http.ProtocolVersion;
+import org.apache.http.annotation.NotThreadSafe;
+import org.apache.http.auth.AuthProtocolState;
 import org.apache.http.auth.AuthScheme;
-import org.apache.http.auth.AuthScope;
 import org.apache.http.auth.AuthState;
-import org.apache.http.auth.AuthenticationException;
-import org.apache.http.auth.Credentials;
-import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.auth.UsernamePasswordCredentials;
 import org.apache.http.client.AuthenticationHandler;
-import org.apache.http.client.RedirectStrategy;
-import org.apache.http.client.RequestDirector;
-import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.AuthenticationStrategy;
 import org.apache.http.client.HttpRequestRetryHandler;
 import org.apache.http.client.NonRepeatableRequestException;
 import org.apache.http.client.RedirectException;
+import org.apache.http.client.RedirectHandler;
+import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.RequestDirector;
 import org.apache.http.client.UserTokenHandler;
 import org.apache.http.client.methods.AbortableHttpRequest;
 import org.apache.http.client.methods.HttpUriRequest;
@@ -80,6 +75,7 @@ import org.apache.http.conn.routing.HttpRouteDirector;
 import org.apache.http.conn.routing.HttpRoutePlanner;
 import org.apache.http.conn.scheme.Scheme;
 import org.apache.http.entity.BufferedHttpEntity;
+import org.apache.http.impl.auth.BasicScheme;
 import org.apache.http.impl.conn.ConnectionShutdownException;
 import org.apache.http.message.BasicHttpRequest;
 import org.apache.http.params.HttpConnectionParams;
@@ -126,10 +122,12 @@ import org.apache.http.util.EntityUtils;
  *  <li>{@link org.apache.http.client.params.ClientPNames#VIRTUAL_HOST}</li>
  *  <li>{@link org.apache.http.client.params.ClientPNames#DEFAULT_HOST}</li>
  *  <li>{@link org.apache.http.client.params.ClientPNames#DEFAULT_HEADERS}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#CONN_MANAGER_TIMEOUT}</li>
  * </ul>
  *
  * @since 4.0
  */
+ at SuppressWarnings("deprecation")
 @NotThreadSafe // e.g. managedConn
 public class DefaultRequestDirector implements RequestDirector {
 
@@ -158,17 +156,25 @@ public class DefaultRequestDirector implements RequestDirector {
 
     /** The redirect handler. */
     @Deprecated
-    protected final org.apache.http.client.RedirectHandler redirectHandler = null;
+    protected final RedirectHandler redirectHandler;
 
     /** The redirect strategy. */
     protected final RedirectStrategy redirectStrategy;
 
     /** The target authentication handler. */
+    @Deprecated
     protected final AuthenticationHandler targetAuthHandler;
 
+    /** The target authentication handler. */
+    protected final AuthenticationStrategy targetAuthStrategy;
+
     /** The proxy authentication handler. */
+    @Deprecated
     protected final AuthenticationHandler proxyAuthHandler;
 
+    /** The proxy authentication handler. */
+    protected final AuthenticationStrategy proxyAuthStrategy;
+
     /** The user token handler. */
     protected final UserTokenHandler userTokenHandler;
 
@@ -182,6 +188,8 @@ public class DefaultRequestDirector implements RequestDirector {
 
     protected final AuthState proxyAuthState;
 
+    private final HttpAuthenticator authenticator;
+
     private int execCount;
 
     private int redirectCount;
@@ -199,7 +207,7 @@ public class DefaultRequestDirector implements RequestDirector {
             final HttpRoutePlanner rouplan,
             final HttpProcessor httpProcessor,
             final HttpRequestRetryHandler retryHandler,
-            final org.apache.http.client.RedirectHandler redirectHandler,
+            final RedirectHandler redirectHandler,
             final AuthenticationHandler targetAuthHandler,
             final AuthenticationHandler proxyAuthHandler,
             final UserTokenHandler userTokenHandler,
@@ -207,13 +215,14 @@ public class DefaultRequestDirector implements RequestDirector {
         this(LogFactory.getLog(DefaultRequestDirector.class),
                 requestExec, conman, reustrat, kastrat, rouplan, httpProcessor, retryHandler,
                 new DefaultRedirectStrategyAdaptor(redirectHandler),
-                targetAuthHandler, proxyAuthHandler, userTokenHandler, params);
+                new AuthenticationStrategyAdaptor(targetAuthHandler),
+                new AuthenticationStrategyAdaptor(proxyAuthHandler),
+                userTokenHandler,
+                params);
     }
 
 
-    /**
-     * @since 4.1
-     */
+    @Deprecated
     public DefaultRequestDirector(
             final Log log,
             final HttpRequestExecutor requestExec,
@@ -228,6 +237,32 @@ public class DefaultRequestDirector implements RequestDirector {
             final AuthenticationHandler proxyAuthHandler,
             final UserTokenHandler userTokenHandler,
             final HttpParams params) {
+        this(LogFactory.getLog(DefaultRequestDirector.class),
+                requestExec, conman, reustrat, kastrat, rouplan, httpProcessor, retryHandler,
+                redirectStrategy,
+                new AuthenticationStrategyAdaptor(targetAuthHandler),
+                new AuthenticationStrategyAdaptor(proxyAuthHandler),
+                userTokenHandler,
+                params);
+    }
+
+    /**
+     * @since 4.2
+     */
+    public DefaultRequestDirector(
+            final Log log,
+            final HttpRequestExecutor requestExec,
+            final ClientConnectionManager conman,
+            final ConnectionReuseStrategy reustrat,
+            final ConnectionKeepAliveStrategy kastrat,
+            final HttpRoutePlanner rouplan,
+            final HttpProcessor httpProcessor,
+            final HttpRequestRetryHandler retryHandler,
+            final RedirectStrategy redirectStrategy,
+            final AuthenticationStrategy targetAuthStrategy,
+            final AuthenticationStrategy proxyAuthStrategy,
+            final UserTokenHandler userTokenHandler,
+            final HttpParams params) {
 
         if (log == null) {
             throw new IllegalArgumentException
@@ -265,13 +300,13 @@ public class DefaultRequestDirector implements RequestDirector {
             throw new IllegalArgumentException
                 ("Redirect strategy may not be null.");
         }
-        if (targetAuthHandler == null) {
+        if (targetAuthStrategy == null) {
             throw new IllegalArgumentException
-                ("Target authentication handler may not be null.");
+                ("Target authentication strategy may not be null.");
         }
-        if (proxyAuthHandler == null) {
+        if (proxyAuthStrategy == null) {
             throw new IllegalArgumentException
-                ("Proxy authentication handler may not be null.");
+                ("Proxy authentication strategy may not be null.");
         }
         if (userTokenHandler == null) {
             throw new IllegalArgumentException
@@ -282,27 +317,44 @@ public class DefaultRequestDirector implements RequestDirector {
                 ("HTTP parameters may not be null");
         }
         this.log               = log;
-        this.requestExec       = requestExec;
-        this.connManager       = conman;
-        this.reuseStrategy     = reustrat;
-        this.keepAliveStrategy = kastrat;
-        this.routePlanner      = rouplan;
-        this.httpProcessor     = httpProcessor;
-        this.retryHandler      = retryHandler;
-        this.redirectStrategy  = redirectStrategy;
-        this.targetAuthHandler = targetAuthHandler;
-        this.proxyAuthHandler  = proxyAuthHandler;
-        this.userTokenHandler  = userTokenHandler;
-        this.params            = params;
-
-        this.managedConn       = null;
+        this.authenticator     = new HttpAuthenticator(log);
+        this.requestExec        = requestExec;
+        this.connManager        = conman;
+        this.reuseStrategy      = reustrat;
+        this.keepAliveStrategy  = kastrat;
+        this.routePlanner       = rouplan;
+        this.httpProcessor      = httpProcessor;
+        this.retryHandler       = retryHandler;
+        this.redirectStrategy   = redirectStrategy;
+        this.targetAuthStrategy = targetAuthStrategy;
+        this.proxyAuthStrategy  = proxyAuthStrategy;
+        this.userTokenHandler   = userTokenHandler;
+        this.params             = params;
+
+        if (redirectStrategy instanceof DefaultRedirectStrategyAdaptor) {
+            this.redirectHandler = ((DefaultRedirectStrategyAdaptor) redirectStrategy).getHandler();
+        } else {
+            this.redirectHandler = null;
+        }
+        if (targetAuthStrategy instanceof AuthenticationStrategyAdaptor) {
+            this.targetAuthHandler = ((AuthenticationStrategyAdaptor) targetAuthStrategy).getHandler();
+        } else {
+            this.targetAuthHandler = null;
+        }
+        if (proxyAuthStrategy instanceof AuthenticationStrategyAdaptor) {
+            this.proxyAuthHandler = ((AuthenticationStrategyAdaptor) proxyAuthStrategy).getHandler();
+        } else {
+            this.proxyAuthHandler = null;
+        }
+
+        this.managedConn = null;
 
         this.execCount = 0;
         this.redirectCount = 0;
-        this.maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100);
         this.targetAuthState = new AuthState();
         this.proxyAuthState = new AuthState();
-    } // constructor
+        this.maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100);
+    }
 
 
     private RequestWrapper wrapRequest(
@@ -327,16 +379,19 @@ public class DefaultRequestDirector implements RequestDirector {
                 // Make sure the request URI is absolute
                 if (!uri.isAbsolute()) {
                     HttpHost target = route.getTargetHost();
-                    uri = URIUtils.rewriteURI(uri, target);
-                    request.setURI(uri);
+                    uri = URIUtils.rewriteURI(uri, target, true);
+                } else {
+                    uri = URIUtils.rewriteURI(uri);
                 }
             } else {
                 // Make sure the request URI is relative
                 if (uri.isAbsolute()) {
                     uri = URIUtils.rewriteURI(uri, null);
-                    request.setURI(uri);
+                } else {
+                    uri = URIUtils.rewriteURI(uri);
                 }
             }
+            request.setURI(uri);
 
         } catch (URISyntaxException ex) {
             throw new ProtocolException("Invalid URI: " +
@@ -350,17 +405,25 @@ public class DefaultRequestDirector implements RequestDirector {
                                 HttpContext context)
         throws HttpException, IOException {
 
+        context.setAttribute(ClientContext.TARGET_AUTH_STATE, targetAuthState);
+        context.setAttribute(ClientContext.PROXY_AUTH_STATE, proxyAuthState);
+
         HttpRequest orig = request;
         RequestWrapper origWrapper = wrapRequest(orig);
         origWrapper.setParams(params);
         HttpRoute origRoute = determineRoute(target, origWrapper, context);
 
-        virtualHost = (HttpHost) orig.getParams().getParameter(
-                ClientPNames.VIRTUAL_HOST);
+        virtualHost = (HttpHost) origWrapper.getParams().getParameter(ClientPNames.VIRTUAL_HOST);
 
-        RoutedRequest roureq = new RoutedRequest(origWrapper, origRoute);
+        // HTTPCLIENT-1092 - add the port if necessary
+        if (virtualHost != null && virtualHost.getPort() == -1) {
+            int port = target.getPort();
+            if (port != -1){
+                virtualHost = new HttpHost(virtualHost.getHostName(), port, virtualHost.getSchemeName());
+            }
+        }
 
-        long timeout = HttpConnectionParams.getConnectionTimeout(params);
+        RoutedRequest roureq = new RoutedRequest(origWrapper, origRoute);
 
         boolean reuse = false;
         boolean done = false;
@@ -387,6 +450,7 @@ public class DefaultRequestDirector implements RequestDirector {
                         ((AbortableHttpRequest) orig).setConnectionRequest(connRequest);
                     }
 
+                    long timeout = HttpClientParams.getConnectionManagerTimeout(params);
                     try {
                         managedConn = connRequest.getConnection(timeout, TimeUnit.MILLISECONDS);
                     } catch(InterruptedException interrupted) {
@@ -421,6 +485,12 @@ public class DefaultRequestDirector implements RequestDirector {
                     break;
                 }
 
+                String userinfo = wrapper.getURI().getUserInfo();
+                if (userinfo != null) {
+                    targetAuthState.update(
+                            new BasicScheme(), new UsernamePasswordCredentials(userinfo));
+                }
+
                 // Reset headers on the request wrapper
                 wrapper.resetHeaders();
 
@@ -437,16 +507,9 @@ public class DefaultRequestDirector implements RequestDirector {
                 HttpHost proxy = route.getProxyHost();
 
                 // Populate the execution context
-                context.setAttribute(ExecutionContext.HTTP_TARGET_HOST,
-                        target);
-                context.setAttribute(ExecutionContext.HTTP_PROXY_HOST,
-                        proxy);
-                context.setAttribute(ExecutionContext.HTTP_CONNECTION,
-                        managedConn);
-                context.setAttribute(ClientContext.TARGET_AUTH_STATE,
-                        targetAuthState);
-                context.setAttribute(ClientContext.PROXY_AUTH_STATE,
-                        proxyAuthState);
+                context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
+                context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy);
+                context.setAttribute(ExecutionContext.HTTP_CONNECTION, managedConn);
 
                 // Run request protocol interceptors
                 requestExec.preProcess(wrapper, httpProcessor, context);
@@ -492,6 +555,18 @@ public class DefaultRequestDirector implements RequestDirector {
                         managedConn.markReusable();
                     } else {
                         managedConn.close();
+                        if (proxyAuthState.getState() == AuthProtocolState.SUCCESS
+                                && proxyAuthState.getAuthScheme() != null
+                                && proxyAuthState.getAuthScheme().isConnectionBased()) {
+                            this.log.debug("Resetting proxy auth state");
+                            proxyAuthState.reset();
+                        }
+                        if (targetAuthState.getState() == AuthProtocolState.SUCCESS
+                                && targetAuthState.getAuthScheme() != null
+                                && targetAuthState.getAuthScheme().isConnectionBased()) {
+                            this.log.debug("Resetting target auth state");
+                            targetAuthState.reset();
+                        }
                     }
                     // check if we can use the same connection for the followup
                     if (!followup.getRoute().equals(roureq.getRoute())) {
@@ -500,9 +575,11 @@ public class DefaultRequestDirector implements RequestDirector {
                     roureq = followup;
                 }
 
-                if (managedConn != null && userToken == null) {
-                    userToken = userTokenHandler.getUserToken(context);
-                    context.setAttribute(ClientContext.USER_TOKEN, userToken);
+                if (managedConn != null) {
+                    if (userToken == null) {
+                        userToken = userTokenHandler.getUserToken(context);
+                        context.setAttribute(ClientContext.USER_TOKEN, userToken);
+                    }
                     if (userToken != null) {
                         managedConn.setState(userToken);
                     }
@@ -551,9 +628,11 @@ public class DefaultRequestDirector implements RequestDirector {
     private void tryConnect(
             final RoutedRequest req, final HttpContext context) throws HttpException, IOException {
         HttpRoute route = req.getRoute();
+        HttpRequest wrapper = req.getRequest();
 
         int connectCount = 0;
         for (;;) {
+            context.setAttribute(ExecutionContext.HTTP_REQUEST, wrapper);
             // Increment connect count
             connectCount++;
             try {
@@ -574,11 +653,11 @@ public class DefaultRequestDirector implements RequestDirector {
                         this.log.info("I/O exception ("+ ex.getClass().getName() +
                                 ") caught when connecting to the target host: "
                                 + ex.getMessage());
+                        if (this.log.isDebugEnabled()) {
+                            this.log.debug(ex.getMessage(), ex);
+                        }
+                        this.log.info("Retrying connect");
                     }
-                    if (this.log.isDebugEnabled()) {
-                        this.log.debug(ex.getMessage(), ex);
-                    }
-                    this.log.info("Retrying connect");
                 } else {
                     throw ex;
                 }
@@ -801,11 +880,7 @@ public class DefaultRequestDirector implements RequestDirector {
         HttpHost target = route.getTargetHost();
         HttpResponse response = null;
 
-        boolean done = false;
-        while (!done) {
-
-            done = true;
-
+        for (;;) {
             if (!this.managedConn.isOpen()) {
                 this.managedConn.open(route, context, this.params);
             }
@@ -814,18 +889,10 @@ public class DefaultRequestDirector implements RequestDirector {
             connect.setParams(this.params);
 
             // Populate the execution context
-            context.setAttribute(ExecutionContext.HTTP_TARGET_HOST,
-                    target);
-            context.setAttribute(ExecutionContext.HTTP_PROXY_HOST,
-                    proxy);
-            context.setAttribute(ExecutionContext.HTTP_CONNECTION,
-                    managedConn);
-            context.setAttribute(ClientContext.TARGET_AUTH_STATE,
-                    targetAuthState);
-            context.setAttribute(ClientContext.PROXY_AUTH_STATE,
-                    proxyAuthState);
-            context.setAttribute(ExecutionContext.HTTP_REQUEST,
-                    connect);
+            context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
+            context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy);
+            context.setAttribute(ExecutionContext.HTTP_CONNECTION, managedConn);
+            context.setAttribute(ExecutionContext.HTTP_REQUEST, connect);
 
             this.requestExec.preProcess(connect, this.httpProcessor, context);
 
@@ -840,30 +907,11 @@ public class DefaultRequestDirector implements RequestDirector {
                         response.getStatusLine());
             }
 
-            CredentialsProvider credsProvider = (CredentialsProvider)
-                context.getAttribute(ClientContext.CREDS_PROVIDER);
-
-            if (credsProvider != null && HttpClientParams.isAuthenticating(params)) {
-                if (this.proxyAuthHandler.isAuthenticationRequested(response, context)) {
-
-                    this.log.debug("Proxy requested authentication");
-                    Map<String, Header> challenges = this.proxyAuthHandler.getChallenges(
-                            response, context);
-                    try {
-                        processChallenges(
-                                challenges, this.proxyAuthState, this.proxyAuthHandler,
-                                response, context);
-                    } catch (AuthenticationException ex) {
-                        if (this.log.isWarnEnabled()) {
-                            this.log.warn("Authentication error: " +  ex.getMessage());
-                            break;
-                        }
-                    }
-                    updateAuthState(this.proxyAuthState, proxy, credsProvider);
-
-                    if (this.proxyAuthState.getCredentials() != null) {
-                        done = false;
-
+            if (HttpClientParams.isAuthenticating(this.params)) {
+                if (this.authenticator.isAuthenticationRequested(proxy, response,
+                        this.proxyAuthStrategy, this.proxyAuthState, context)) {
+                    if (this.authenticator.authenticate(proxy, response,
+                            this.proxyAuthStrategy, this.proxyAuthState, context)) {
                         // Retry request
                         if (this.reuseStrategy.keepAlive(response, context)) {
                             this.log.debug("Connection kept alive");
@@ -873,17 +921,16 @@ public class DefaultRequestDirector implements RequestDirector {
                         } else {
                             this.managedConn.close();
                         }
-
+                    } else {
+                        break;
                     }
-
                 } else {
-                    // Reset proxy auth scope
-                    this.proxyAuthState.setAuthScope(null);
+                    break;
                 }
             }
         }
 
-        int status = response.getStatusLine().getStatusCode(); // can't be null
+        int status = response.getStatusLine().getStatusCode();
 
         if (status > 299) {
 
@@ -1032,16 +1079,14 @@ public class DefaultRequestDirector implements RequestDirector {
                     uri.getPort(),
                     uri.getScheme());
 
-            // Unset auth scope
-            targetAuthState.setAuthScope(null);
-            proxyAuthState.setAuthScope(null);
-
-            // Invalidate auth states if redirecting to another host
+            // Reset auth states if redirecting to another host
             if (!route.getTargetHost().equals(newTarget)) {
-                targetAuthState.invalidate();
+                this.log.debug("Resetting target auth state");
+                targetAuthState.reset();
                 AuthScheme authScheme = proxyAuthState.getAuthScheme();
                 if (authScheme != null && authScheme.isConnectionBased()) {
-                    proxyAuthState.invalidate();
+                    this.log.debug("Resetting proxy auth state");
+                    proxyAuthState.reset();
                 }
             }
 
@@ -1058,73 +1103,36 @@ public class DefaultRequestDirector implements RequestDirector {
             return newRequest;
         }
 
-        CredentialsProvider credsProvider = (CredentialsProvider)
-            context.getAttribute(ClientContext.CREDS_PROVIDER);
-
-        if (credsProvider != null && HttpClientParams.isAuthenticating(params)) {
-
-            if (this.targetAuthHandler.isAuthenticationRequested(response, context)) {
-
-                HttpHost target = (HttpHost)
-                    context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
-                if (target == null) {
-                    target = route.getTargetHost();
-                }
-
-                this.log.debug("Target requested authentication");
-                Map<String, Header> challenges = this.targetAuthHandler.getChallenges(
-                        response, context);
-                try {
-                    processChallenges(challenges,
-                            this.targetAuthState, this.targetAuthHandler,
-                            response, context);
-                } catch (AuthenticationException ex) {
-                    if (this.log.isWarnEnabled()) {
-                        this.log.warn("Authentication error: " +  ex.getMessage());
-                        return null;
-                    }
-                }
-                updateAuthState(this.targetAuthState, target, credsProvider);
-
-                if (this.targetAuthState.getCredentials() != null) {
+        if (HttpClientParams.isAuthenticating(params)) {
+            HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
+            if (target == null) {
+                target = route.getTargetHost();
+            }
+            if (target.getPort() < 0) {
+                Scheme scheme = connManager.getSchemeRegistry().getScheme(target);
+                target = new HttpHost(target.getHostName(), scheme.getDefaultPort(), target.getSchemeName());
+            }
+            if (this.authenticator.isAuthenticationRequested(target, response,
+                    this.targetAuthStrategy, this.targetAuthState, context)) {
+                if (this.authenticator.authenticate(target, response,
+                        this.targetAuthStrategy, this.targetAuthState, context)) {
                     // Re-try the same request via the same route
                     return roureq;
                 } else {
                     return null;
                 }
-            } else {
-                // Reset target auth scope
-                this.targetAuthState.setAuthScope(null);
             }
 
-            if (this.proxyAuthHandler.isAuthenticationRequested(response, context)) {
-
-                HttpHost proxy = route.getProxyHost();
-
-                this.log.debug("Proxy requested authentication");
-                Map<String, Header> challenges = this.proxyAuthHandler.getChallenges(
-                        response, context);
-                try {
-                    processChallenges(challenges,
-                            this.proxyAuthState, this.proxyAuthHandler,
-                            response, context);
-                } catch (AuthenticationException ex) {
-                    if (this.log.isWarnEnabled()) {
-                        this.log.warn("Authentication error: " +  ex.getMessage());
-                        return null;
-                    }
-                }
-                updateAuthState(this.proxyAuthState, proxy, credsProvider);
-
-                if (this.proxyAuthState.getCredentials() != null) {
+            HttpHost proxy = route.getProxyHost();
+            if (this.authenticator.isAuthenticationRequested(proxy, response,
+                    this.proxyAuthStrategy, this.proxyAuthState, context)) {
+                if (this.authenticator.authenticate(proxy, response,
+                        this.proxyAuthStrategy, this.proxyAuthState, context)) {
                     // Re-try the same request via the same route
                     return roureq;
                 } else {
                     return null;
                 }
-            } else {
-                // Reset proxy auth scope
-                this.proxyAuthState.setAuthScope(null);
             }
         }
         return null;
@@ -1159,76 +1167,4 @@ public class DefaultRequestDirector implements RequestDirector {
     } // abortConnection
 
 
-    private void processChallenges(
-            final Map<String, Header> challenges,
-            final AuthState authState,
-            final AuthenticationHandler authHandler,
-            final HttpResponse response,
-            final HttpContext context)
-                throws MalformedChallengeException, AuthenticationException {
-
-        AuthScheme authScheme = authState.getAuthScheme();
-        if (authScheme == null) {
-            // Authentication not attempted before
-            authScheme = authHandler.selectScheme(challenges, response, context);
-            authState.setAuthScheme(authScheme);
-        }
-        String id = authScheme.getSchemeName();
-
-        Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH));
-        if (challenge == null) {
-            throw new AuthenticationException(id +
-                " authorization challenge expected, but not found");
-        }
-        authScheme.processChallenge(challenge);
-        this.log.debug("Authorization challenge processed");
-    }
-
-
-    private void updateAuthState(
-            final AuthState authState,
-            final HttpHost host,
-            final CredentialsProvider credsProvider) {
-
-        if (!authState.isValid()) {
-            return;
-        }
-
-        String hostname = host.getHostName();
-        int port = host.getPort();
-        if (port < 0) {
-            Scheme scheme = connManager.getSchemeRegistry().getScheme(host);
-            port = scheme.getDefaultPort();
-        }
-
-        AuthScheme authScheme = authState.getAuthScheme();
-        AuthScope authScope = new AuthScope(
-                hostname,
-                port,
-                authScheme.getRealm(),
-                authScheme.getSchemeName());
-
-        if (this.log.isDebugEnabled()) {
-            this.log.debug("Authentication scope: " + authScope);
-        }
-        Credentials creds = authState.getCredentials();
-        if (creds == null) {
-            creds = credsProvider.getCredentials(authScope);
-            if (this.log.isDebugEnabled()) {
-                if (creds != null) {
-                    this.log.debug("Found credentials");
-                } else {
-                    this.log.debug("Credentials not found");
-                }
-            }
-        } else {
-            if (authScheme.isComplete()) {
-                this.log.debug("Authentication failed");
-                creds = null;
-            }
-        }
-        authState.setAuthScope(authScope);
-        authState.setCredentials(creds);
-    }
-
 } // class DefaultClientRequestDirector
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultServiceUnavailableRetryStrategy.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultServiceUnavailableRetryStrategy.java
new file mode 100644
index 0000000..7906788
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultServiceUnavailableRetryStrategy.java
@@ -0,0 +1,83 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.annotation.Immutable;
+import org.apache.http.client.ServiceUnavailableRetryStrategy;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Default implementation of the {@link ServiceUnavailableRetryStrategy} interface.
+ * that retries <code>503</code> (Service Unavailable) responses for a fixed number of times
+ * at a fixed interval.
+ *
+ * @since 4.2
+ */
+ at Immutable
+public class DefaultServiceUnavailableRetryStrategy implements ServiceUnavailableRetryStrategy {
+
+    /**
+     * Maximum number of allowed retries if the server responds with a HTTP code
+     * in our retry code list. Default value is 1.
+     */
+    private final int maxRetries;
+
+    /**
+     * Retry interval between subsequent requests, in milliseconds. Default
+     * value is 1 second.
+     */
+    private final long retryInterval;
+
+    public DefaultServiceUnavailableRetryStrategy(int maxRetries, int retryInterval) {
+        super();
+        if (maxRetries < 1) {
+            throw new IllegalArgumentException("MaxRetries must be greater than 1");
+        }
+        if (retryInterval < 1) {
+            throw new IllegalArgumentException("Retry interval must be greater than 1");
+        }
+        this.maxRetries = maxRetries;
+        this.retryInterval = retryInterval;
+    }
+
+    public DefaultServiceUnavailableRetryStrategy() {
+        this(1, 1000);
+    }
+
+    public boolean retryRequest(final HttpResponse response, int executionCount, final HttpContext context) {
+        return executionCount <= maxRetries &&
+            response.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE;
+    }
+
+    public long getRetryInterval() {
+        return retryInterval;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java
index 1850c15..17cfc5c 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java
@@ -46,7 +46,10 @@ import org.apache.http.protocol.HttpContext;
  * authentication.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link TargetAuthenticationStrategy}
  */
+ at Deprecated
 @Immutable
 public class DefaultTargetAuthenticationHandler extends AbstractAuthenticationHandler {
 
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java b/httpclient/src/main/java/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java
index 3c393c6..c256c10 100644
--- a/httpclient/src/main/java/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java
+++ b/httpclient/src/main/java/org/apache/http/impl/client/EntityEnclosingRequestWrapper.java
@@ -89,7 +89,7 @@ public class EntityEnclosingRequestWrapper extends RequestWrapper
             super(entity);
         }
 
-        @Deprecated
+        @SuppressWarnings("deprecation")
         @Override
         public void consumeContent() throws IOException {
             consumed = true;
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/HttpAuthenticator.java b/httpclient/src/main/java/org/apache/http/impl/client/HttpAuthenticator.java
new file mode 100644
index 0000000..034de00
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/HttpAuthenticator.java
@@ -0,0 +1,159 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.Queue;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthProtocolState;
+import org.apache.http.auth.AuthOption;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.client.AuthenticationStrategy;
+import org.apache.http.protocol.HttpContext;
+
+public class HttpAuthenticator {
+
+    private final Log log;
+
+    public HttpAuthenticator(final Log log) {
+        super();
+        this.log = log != null ? log : LogFactory.getLog(getClass());
+    }
+
+    public HttpAuthenticator() {
+        this(null);
+    }
+
+    public boolean isAuthenticationRequested(
+            final HttpHost host,
+            final HttpResponse response,
+            final AuthenticationStrategy authStrategy,
+            final AuthState authState,
+            final HttpContext context) {
+        if (authStrategy.isAuthenticationRequested(host, response, context)) {
+            return true;
+        } else {
+            switch (authState.getState()) {
+            case CHALLENGED:
+            case HANDSHAKE:
+                authState.setState(AuthProtocolState.SUCCESS);
+                authStrategy.authSucceeded(host, authState.getAuthScheme(), context);
+                break;
+            case SUCCESS:
+                break;
+            default:
+                authState.setState(AuthProtocolState.UNCHALLENGED);
+            }
+            return false;
+        }
+    }
+
+    public boolean authenticate(
+            final HttpHost host,
+            final HttpResponse response,
+            final AuthenticationStrategy authStrategy,
+            final AuthState authState,
+            final HttpContext context) {
+        try {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug(host.toHostString() + " requested authentication");
+            }
+            Map<String, Header> challenges = authStrategy.getChallenges(host, response, context);
+            if (challenges.isEmpty()) {
+                this.log.debug("Response contains no authentication challenges");
+                return false;
+            }
+
+            AuthScheme authScheme = authState.getAuthScheme();
+            switch (authState.getState()) {
+            case FAILURE:
+                return false;
+            case SUCCESS:
+                authState.reset();
+                break;
+            case CHALLENGED:
+            case HANDSHAKE:
+                if (authScheme == null) {
+                    this.log.debug("Auth scheme is null");
+                    authStrategy.authFailed(host, null, context);
+                    authState.reset();
+                    authState.setState(AuthProtocolState.FAILURE);
+                    return false;
+                }
+            case UNCHALLENGED:
+                if (authScheme != null) {
+                    String id = authScheme.getSchemeName();
+                    Header challenge = challenges.get(id.toLowerCase(Locale.US));
+                    if (challenge != null) {
+                        this.log.debug("Authorization challenge processed");
+                        authScheme.processChallenge(challenge);
+                        if (authScheme.isComplete()) {
+                            this.log.debug("Authentication failed");
+                            authStrategy.authFailed(host, authState.getAuthScheme(), context);
+                            authState.reset();
+                            authState.setState(AuthProtocolState.FAILURE);
+                            return false;
+                        } else {
+                            authState.setState(AuthProtocolState.HANDSHAKE);
+                            return true;
+                        }
+                    } else {
+                        authState.reset();
+                        // Retry authentication with a different scheme
+                    }
+                }
+            }
+            Queue<AuthOption> authOptions = authStrategy.select(challenges, host, response, context);
+            if (authOptions != null && !authOptions.isEmpty()) {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("Selected authentication options: " + authOptions);
+                }
+                authState.setState(AuthProtocolState.CHALLENGED);
+                authState.update(authOptions);
+                return true;
+            } else {
+                return false;
+            }
+        } catch (MalformedChallengeException ex) {
+            if (this.log.isWarnEnabled()) {
+                this.log.warn("Malformed challenge: " +  ex.getMessage());
+            }
+            authState.reset();
+            return false;
+        }
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/LaxRedirectStrategy.java b/httpclient/src/main/java/org/apache/http/impl/client/LaxRedirectStrategy.java
new file mode 100644
index 0000000..add0be3
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/LaxRedirectStrategy.java
@@ -0,0 +1,65 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.annotation.Immutable;
+import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpPost;
+
+/**
+ * Lax {@link RedirectStrategy} implementation that automatically redirects all HEAD, GET and POST
+ * requests. This strategy relaxes restrictions on automatic redirection of POST methods imposed
+ * by the HTTP specification.
+ *
+ * @since 4.2
+ */
+ at Immutable
+public class LaxRedirectStrategy extends DefaultRedirectStrategy {
+
+    /**
+     * Redirectable methods.
+     */
+    private static final String[] REDIRECT_METHODS = new String[] {
+        HttpGet.METHOD_NAME,
+        HttpPost.METHOD_NAME,
+        HttpHead.METHOD_NAME
+    };
+
+    @Override
+    protected boolean isRedirectable(String method) {
+        for (String m: REDIRECT_METHODS) {
+            if (m.equalsIgnoreCase(method)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/NullBackoffStrategy.java b/httpclient/src/main/java/org/apache/http/impl/client/NullBackoffStrategy.java
new file mode 100644
index 0000000..e7ff0b2
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/NullBackoffStrategy.java
@@ -0,0 +1,46 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.ConnectionBackoffStrategy;
+
+/**
+ * This is a {@link ConnectionBackoffStrategy} that never backs off,
+ * for compatibility with existing behavior.
+ *
+ * @since 4.2
+ */
+public class NullBackoffStrategy implements ConnectionBackoffStrategy {
+
+    public boolean shouldBackoff(Throwable t) {
+        return false;
+    }
+
+    public boolean shouldBackoff(HttpResponse resp) {
+        return false;
+    }
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java b/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java
new file mode 100644
index 0000000..ff74c39
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java
@@ -0,0 +1,48 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.annotation.Immutable;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.params.AuthPNames;
+import org.apache.http.client.AuthenticationStrategy;
+
+/**
+ * Default {@link AuthenticationStrategy} implementation for proxy host authentication.
+ *
+ * @since 4.2
+ */
+ at Immutable
+public class ProxyAuthenticationStrategy extends AuthenticationStrategyImpl {
+
+    public ProxyAuthenticationStrategy() {
+        super(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, AUTH.PROXY_AUTH, AuthPNames.PROXY_AUTH_PREF);
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java b/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java
new file mode 100644
index 0000000..19771a4
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java
@@ -0,0 +1,247 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.Credentials;
+import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.client.params.HttpClientParams;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.client.protocol.RequestClientConnControl;
+import org.apache.http.client.protocol.RequestProxyAuthentication;
+import org.apache.http.conn.HttpRoutedConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.entity.BufferedHttpEntity;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.DefaultHttpClientConnection;
+import org.apache.http.impl.auth.BasicSchemeFactory;
+import org.apache.http.impl.auth.DigestSchemeFactory;
+import org.apache.http.impl.auth.KerberosSchemeFactory;
+import org.apache.http.impl.auth.NTLMSchemeFactory;
+import org.apache.http.impl.auth.SPNegoSchemeFactory;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpRequestExecutor;
+import org.apache.http.protocol.ImmutableHttpProcessor;
+import org.apache.http.protocol.RequestContent;
+import org.apache.http.protocol.RequestTargetHost;
+import org.apache.http.protocol.RequestUserAgent;
+import org.apache.http.util.EntityUtils;
+
+public class ProxyClient {
+
+    private final HttpProcessor httpProcessor;
+    private final HttpRequestExecutor requestExec;
+    private final ProxyAuthenticationStrategy proxyAuthStrategy;
+    private final HttpAuthenticator authenticator;
+    private final AuthState proxyAuthState;
+    private final AuthSchemeRegistry authSchemeRegistry;
+    private final ConnectionReuseStrategy reuseStrategy;
+    private final HttpParams params;
+
+    public ProxyClient(final HttpParams params) {
+        super();
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        this.httpProcessor = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {
+                new RequestContent(),
+                new RequestTargetHost(),
+                new RequestClientConnControl(),
+                new RequestUserAgent(),
+                new RequestProxyAuthentication()
+        } );
+        this.requestExec = new HttpRequestExecutor();
+        this.proxyAuthStrategy = new ProxyAuthenticationStrategy();
+        this.authenticator = new HttpAuthenticator();
+        this.proxyAuthState = new AuthState();
+        this.authSchemeRegistry = new AuthSchemeRegistry();
+        this.authSchemeRegistry.register(AuthPolicy.BASIC, new BasicSchemeFactory());
+        this.authSchemeRegistry.register(AuthPolicy.DIGEST, new DigestSchemeFactory());
+        this.authSchemeRegistry.register(AuthPolicy.NTLM, new NTLMSchemeFactory());
+        this.authSchemeRegistry.register(AuthPolicy.SPNEGO, new SPNegoSchemeFactory());
+        this.authSchemeRegistry.register(AuthPolicy.KERBEROS, new KerberosSchemeFactory());
+        this.reuseStrategy = new DefaultConnectionReuseStrategy();
+        this.params = params;
+    }
+
+    public ProxyClient() {
+        this(new BasicHttpParams());
+    }
+
+    public HttpParams getParams() {
+        return this.params;
+    }
+
+    public AuthSchemeRegistry getAuthSchemeRegistry() {
+        return this.authSchemeRegistry;
+    }
+
+    public Socket tunnel(
+            final HttpHost proxy,
+            final HttpHost target,
+            final Credentials credentials) throws IOException, HttpException {
+        ProxyConnection conn = new ProxyConnection(new HttpRoute(proxy));
+        HttpContext context = new BasicHttpContext();
+        HttpResponse response = null;
+
+        for (;;) {
+            if (!conn.isOpen()) {
+                Socket socket = new Socket(proxy.getHostName(), proxy.getPort());
+                conn.bind(socket, this.params);
+            }
+            String host = target.getHostName();
+            int port = target.getPort();
+            if (port < 0) {
+                port = 80;
+            }
+
+            StringBuilder buffer = new StringBuilder(host.length() + 6);
+            buffer.append(host);
+            buffer.append(':');
+            buffer.append(Integer.toString(port));
+
+            String authority = buffer.toString();
+            ProtocolVersion ver = HttpProtocolParams.getVersion(this.params);
+            HttpRequest connect = new BasicHttpRequest("CONNECT", authority, ver);
+            connect.setParams(this.params);
+
+            BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
+            credsProvider.setCredentials(new AuthScope(proxy), credentials);
+
+            // Populate the execution context
+            context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
+            context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy);
+            context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+            context.setAttribute(ExecutionContext.HTTP_REQUEST, connect);
+            context.setAttribute(ClientContext.PROXY_AUTH_STATE, this.proxyAuthState);
+            context.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider);
+            context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry);
+
+            this.requestExec.preProcess(connect, this.httpProcessor, context);
+
+            response = this.requestExec.execute(connect, conn, context);
+
+            response.setParams(this.params);
+            this.requestExec.postProcess(response, this.httpProcessor, context);
+
+            int status = response.getStatusLine().getStatusCode();
+            if (status < 200) {
+                throw new HttpException("Unexpected response to CONNECT request: " +
+                        response.getStatusLine());
+            }
+
+            if (HttpClientParams.isAuthenticating(this.params)) {
+                if (this.authenticator.isAuthenticationRequested(proxy, response,
+                        this.proxyAuthStrategy, this.proxyAuthState, context)) {
+                    if (this.authenticator.authenticate(proxy, response,
+                            this.proxyAuthStrategy, this.proxyAuthState, context)) {
+                        // Retry request
+                        if (this.reuseStrategy.keepAlive(response, context)) {
+                            // Consume response content
+                            HttpEntity entity = response.getEntity();
+                            EntityUtils.consume(entity);
+                        } else {
+                            conn.close();
+                        }
+                    } else {
+                        break;
+                    }
+                } else {
+                    break;
+                }
+            }
+        }
+
+        int status = response.getStatusLine().getStatusCode();
+
+        if (status > 299) {
+
+            // Buffer response content
+            HttpEntity entity = response.getEntity();
+            if (entity != null) {
+                response.setEntity(new BufferedHttpEntity(entity));
+            }
+
+            conn.close();
+            throw new TunnelRefusedException("CONNECT refused by proxy: " +
+                    response.getStatusLine(), response);
+        }
+        return conn.getSocket();
+    }
+
+    static class ProxyConnection extends DefaultHttpClientConnection implements HttpRoutedConnection {
+
+        private final HttpRoute route;
+
+        ProxyConnection(final HttpRoute route) {
+            super();
+            this.route = route;
+        }
+
+        public HttpRoute getRoute() {
+            return this.route;
+        }
+
+        public boolean isSecure() {
+            return false;
+        }
+
+        public SSLSession getSSLSession() {
+            return null;
+        }
+
+        @Override
+        public Socket getSocket() {
+            return super.getSocket();
+        }
+
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/StandardHttpRequestRetryHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/StandardHttpRequestRetryHandler.java
new file mode 100644
index 0000000..0ebac0d
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/StandardHttpRequestRetryHandler.java
@@ -0,0 +1,81 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.http.HttpRequest;
+import org.apache.http.annotation.Immutable;
+import org.apache.http.client.HttpRequestRetryHandler;
+
+/**
+ * A {@link HttpRequestRetryHandler} which assumes that all requested
+ * HTTP methods which should be idempotent according to RFC-2616 are
+ * in fact idempotent and can be retried.
+ *
+ * According to RFC-2616 section 9.1.2 the idempotent HTTP methods are:
+ * GET, HEAD, PUT, DELETE, OPTIONS, and TRACE
+ *
+ * @since 4.2
+ */
+ at Immutable
+public class StandardHttpRequestRetryHandler extends DefaultHttpRequestRetryHandler {
+
+    private final Map<String, Boolean> idempotentMethods;
+
+    /**
+     * Default constructor
+     */
+    public StandardHttpRequestRetryHandler(int retryCount, boolean requestSentRetryEnabled) {
+        super(retryCount, requestSentRetryEnabled);
+        this.idempotentMethods = new ConcurrentHashMap<String, Boolean>();
+        this.idempotentMethods.put("GET", Boolean.TRUE);
+        this.idempotentMethods.put("HEAD", Boolean.TRUE);
+        this.idempotentMethods.put("PUT", Boolean.TRUE);
+        this.idempotentMethods.put("DELETE", Boolean.TRUE);
+        this.idempotentMethods.put("OPTIONS", Boolean.TRUE);
+        this.idempotentMethods.put("TRACE", Boolean.TRUE);
+    }
+
+    /**
+     * Default constructor
+     */
+    public StandardHttpRequestRetryHandler() {
+        this(3, false);
+    }
+
+    @Override
+    protected boolean handleAsIdempotent(final HttpRequest request) {
+        String method = request.getRequestLine().getMethod().toUpperCase(Locale.US);
+        Boolean b = this.idempotentMethods.get(method);
+        return b != null && b.booleanValue();
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/SystemClock.java b/httpclient/src/main/java/org/apache/http/impl/client/SystemClock.java
new file mode 100644
index 0000000..18f0992
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/SystemClock.java
@@ -0,0 +1,39 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+/**
+ * The actual system clock.
+ *
+ * @since 4.2
+ */
+class SystemClock implements Clock {
+
+    public long getCurrentTime() {
+        return System.currentTimeMillis();
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/SystemDefaultHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/SystemDefaultHttpClient.java
new file mode 100644
index 0000000..43b7855
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/SystemDefaultHttpClient.java
@@ -0,0 +1,146 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.net.ProxySelector;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.annotation.ThreadSafe;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.NoConnectionReuseStrategy;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
+import org.apache.http.impl.conn.ProxySelectorRoutePlanner;
+import org.apache.http.impl.conn.SchemeRegistryFactory;
+import org.apache.http.params.HttpParams;
+
+/**
+ * An extension of {@link DefaultHttpClient} pre-configured using system properties.
+ * <p>
+ * The following system properties are taken into account by this class:
+ * <ul>
+ *  <li>ssl.TrustManagerFactory.algorithm</li>
+ *  <li>javax.net.ssl.trustStoreType</li>
+ *  <li>javax.net.ssl.trustStore</li>
+ *  <li>javax.net.ssl.trustStoreProvider</li>
+ *  <li>javax.net.ssl.trustStorePassword</li>
+ *  <li>java.home</li>
+ *  <li>ssl.KeyManagerFactory.algorithm</li>
+ *  <li>javax.net.ssl.keyStoreType</li>
+ *  <li>javax.net.ssl.keyStore</li>
+ *  <li>javax.net.ssl.keyStoreProvider</li>
+ *  <li>javax.net.ssl.keyStorePassword</li>
+ *  <li>http.proxyHost</li>
+ *  <li>http.proxyPort</li>
+ *  <li>http.nonProxyHosts</li>
+ *  <li>http.keepAlive</li>
+ *  <li>http.maxConnections</li>
+ * </ul>
+ * <p>
+ * <p>
+ * The following parameters can be used to customize the behavior of this
+ * class:
+ * <ul>
+ *  <li>{@link org.apache.http.params.CoreProtocolPNames#PROTOCOL_VERSION}</li>
+ *  <li>{@link org.apache.http.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}</li>
+ *  <li>{@link org.apache.http.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}</li>
+ *  <li>{@link org.apache.http.params.CoreProtocolPNames#USE_EXPECT_CONTINUE}</li>
+ *  <li>{@link org.apache.http.params.CoreProtocolPNames#WAIT_FOR_CONTINUE}</li>
+ *  <li>{@link org.apache.http.params.CoreProtocolPNames#USER_AGENT}</li>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#TCP_NODELAY}</li>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#SO_TIMEOUT}</li>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#SO_LINGER}</li>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#SO_REUSEADDR}</li>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}</li>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#CONNECTION_TIMEOUT}</li>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#STALE_CONNECTION_CHECK}</li>
+ *  <li>{@link org.apache.http.conn.params.ConnRoutePNames#FORCED_ROUTE}</li>
+ *  <li>{@link org.apache.http.conn.params.ConnRoutePNames#LOCAL_ADDRESS}</li>
+ *  <li>{@link org.apache.http.conn.params.ConnRoutePNames#DEFAULT_PROXY}</li>
+ *  <li>{@link org.apache.http.cookie.params.CookieSpecPNames#DATE_PATTERNS}</li>
+ *  <li>{@link org.apache.http.cookie.params.CookieSpecPNames#SINGLE_COOKIE_HEADER}</li>
+ *  <li>{@link org.apache.http.auth.params.AuthPNames#CREDENTIAL_CHARSET}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#COOKIE_POLICY}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#HANDLE_AUTHENTICATION}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#HANDLE_REDIRECTS}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#MAX_REDIRECTS}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#ALLOW_CIRCULAR_REDIRECTS}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#VIRTUAL_HOST}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#DEFAULT_HOST}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#DEFAULT_HEADERS}</li>
+ *  <li>{@link org.apache.http.client.params.ClientPNames#CONN_MANAGER_TIMEOUT}</li>
+ * </ul>
+ * </p>
+ *
+ * @since 4.2
+ */
+ at ThreadSafe
+public class SystemDefaultHttpClient extends DefaultHttpClient {
+
+    public SystemDefaultHttpClient(final HttpParams params) {
+        super(null, params);
+    }
+
+    public SystemDefaultHttpClient() {
+        super(null, null);
+    }
+
+    @Override
+    protected ClientConnectionManager createClientConnectionManager() {
+        PoolingClientConnectionManager connmgr = new PoolingClientConnectionManager(
+                SchemeRegistryFactory.createSystemDefault());
+        String s = System.getProperty("http.keepAlive");
+        if ("true".equalsIgnoreCase(s)) {
+            s = System.getProperty("http.maxConnections", "5");
+            int max = Integer.parseInt(s);
+            connmgr.setDefaultMaxPerRoute(max);
+            connmgr.setMaxTotal(2 * max);
+        }
+        return connmgr;
+    }
+
+    @Override
+    protected HttpRoutePlanner createHttpRoutePlanner() {
+        return new ProxySelectorRoutePlanner(getConnectionManager().getSchemeRegistry(),
+                ProxySelector.getDefault());
+    }
+
+    @Override
+    protected ConnectionReuseStrategy createConnectionReuseStrategy() {
+        String s = System.getProperty("http.keepAlive");
+        if ("true".equalsIgnoreCase(s)) {
+            return new DefaultConnectionReuseStrategy();
+        } else {
+            return new NoConnectionReuseStrategy();
+        }
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/client/TargetAuthenticationStrategy.java b/httpclient/src/main/java/org/apache/http/impl/client/TargetAuthenticationStrategy.java
new file mode 100644
index 0000000..eb2cc8d
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/client/TargetAuthenticationStrategy.java
@@ -0,0 +1,48 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import org.apache.http.HttpStatus;
+import org.apache.http.annotation.Immutable;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.params.AuthPNames;
+import org.apache.http.client.AuthenticationStrategy;
+
+/**
+ * Default {@link AuthenticationStrategy} implementation for proxy host authentication.
+ *
+ * @since 4.2
+ */
+ at Immutable
+public class TargetAuthenticationStrategy extends AuthenticationStrategyImpl {
+
+    public TargetAuthenticationStrategy() {
+        super(HttpStatus.SC_UNAUTHORIZED, AUTH.WWW_AUTH, AuthPNames.TARGET_AUTH_PREF);
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/AbstractClientConnAdapter.java b/httpclient/src/main/java/org/apache/http/impl/conn/AbstractClientConnAdapter.java
index f0537e8..4d5718b 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/AbstractClientConnAdapter.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/AbstractClientConnAdapter.java
@@ -40,6 +40,7 @@ import org.apache.http.HttpRequest;
 import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.annotation.NotThreadSafe;
 import org.apache.http.conn.OperatedClientConnection;
 import org.apache.http.conn.ManagedClientConnection;
 import org.apache.http.conn.ClientConnectionManager;
@@ -65,16 +66,17 @@ import org.apache.http.protocol.HttpContext;
  * expected to tolerate multiple calls to the release method.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  do not use
  */
-public abstract class AbstractClientConnAdapter
-                            implements ManagedClientConnection, HttpContext {
+ at Deprecated
+ at NotThreadSafe
+public abstract class AbstractClientConnAdapter implements ManagedClientConnection, HttpContext {
 
     /**
-     * The connection manager, if any.
-     * This attribute MUST NOT be final, so the adapter can be detached
-     * from the connection manager without keeping a hard reference there.
+     * The connection manager.
      */
-    private volatile ClientConnectionManager connManager;
+    private final ClientConnectionManager connManager;
 
     /** The wrapped connection. */
     private volatile OperatedClientConnection wrappedConnection;
@@ -112,7 +114,6 @@ public abstract class AbstractClientConnAdapter
      */
     protected synchronized void detach() {
         wrappedConnection = null;
-        connManager = null; // base class attribute
         duration = Long.MAX_VALUE;
     }
 
@@ -125,9 +126,8 @@ public abstract class AbstractClientConnAdapter
     }
 
     /**
-     * @deprecated use {@link #assertValid(OperatedClientConnection)}
+     * @deprecated (4.1)  use {@link #assertValid(OperatedClientConnection)}
      */
-    @Deprecated
     protected final void assertNotAborted() throws InterruptedIOException {
         if (isReleased()) {
             throw new InterruptedIOException("Connection has been shut down");
@@ -304,9 +304,7 @@ public abstract class AbstractClientConnAdapter
             return;
         }
         released = true;
-        if (connManager != null) {
-            connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
-        }
+        connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
     }
 
     public synchronized void abortConnection() {
@@ -319,12 +317,10 @@ public abstract class AbstractClientConnAdapter
             shutdown();
         } catch (IOException ignore) {
         }
-        if (connManager != null) {
-            connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
-        }
+        connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS);
     }
 
-    public synchronized Object getAttribute(final String id) {
+    public Object getAttribute(final String id) {
         OperatedClientConnection conn = getWrappedConnection();
         assertValid(conn);
         if (conn instanceof HttpContext) {
@@ -334,7 +330,7 @@ public abstract class AbstractClientConnAdapter
         }
     }
 
-    public synchronized Object removeAttribute(final String id) {
+    public Object removeAttribute(final String id) {
         OperatedClientConnection conn = getWrappedConnection();
         assertValid(conn);
         if (conn instanceof HttpContext) {
@@ -344,7 +340,7 @@ public abstract class AbstractClientConnAdapter
         }
     }
 
-    public synchronized void setAttribute(final String id, final Object obj) {
+    public void setAttribute(final String id, final Object obj) {
         OperatedClientConnection conn = getWrappedConnection();
         assertValid(conn);
         if (conn instanceof HttpContext) {
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/AbstractPoolEntry.java b/httpclient/src/main/java/org/apache/http/impl/conn/AbstractPoolEntry.java
index 4e8ad0d..8ae374a 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/AbstractPoolEntry.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/AbstractPoolEntry.java
@@ -27,11 +27,11 @@
 package org.apache.http.impl.conn;
 
 import java.io.IOException;
+import java.io.InterruptedIOException;
 
 import org.apache.http.HttpHost;
 import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.HttpContext;
-import org.apache.http.annotation.NotThreadSafe;
 import org.apache.http.conn.routing.HttpRoute;
 import org.apache.http.conn.routing.RouteTracker;
 import org.apache.http.conn.ClientConnectionOperator;
@@ -52,8 +52,10 @@ import org.apache.http.conn.OperatedClientConnection;
  * underlying connection and the established route.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  do not use
  */
- at NotThreadSafe
+ at Deprecated
 public abstract class AbstractPoolEntry {
 
     /** The connection operator. */
@@ -157,7 +159,7 @@ public abstract class AbstractPoolEntry {
         // If this tracker was reset while connecting,
         // fail early.
         if (localTracker == null) {
-            throw new IOException("Request aborted");
+            throw new InterruptedIOException("Request aborted");
         }
 
         if (proxy == null) {
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/AbstractPooledConnAdapter.java b/httpclient/src/main/java/org/apache/http/impl/conn/AbstractPooledConnAdapter.java
index 70212da..db98e84 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/AbstractPooledConnAdapter.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/AbstractPooledConnAdapter.java
@@ -46,7 +46,10 @@ import org.apache.http.conn.OperatedClientConnection;
  * respective method of the wrapped connection.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  do not use
  */
+ at Deprecated
 public abstract class AbstractPooledConnAdapter extends AbstractClientConnAdapter {
 
     /** The wrapped pool entry. */
@@ -68,6 +71,8 @@ public abstract class AbstractPooledConnAdapter extends AbstractClientConnAdapte
      * Obtains the pool entry.
      *
      * @return  the pool entry, or <code>null</code> if detached
+     * 
+     * @deprecated (4.0.1) 
      */
     protected AbstractPoolEntry getPoolEntry() {
         return this.poolEntry;
@@ -88,9 +93,8 @@ public abstract class AbstractPooledConnAdapter extends AbstractClientConnAdapte
     }
 
     /**
-     * @deprecated use {@link #assertValid(AbstractPoolEntry)}
+     * @deprecated (4.1)  use {@link #assertValid(AbstractPoolEntry)}
      */
-    @Deprecated
     protected final void assertAttached() {
         if (poolEntry == null) {
             throw new ConnectionShutdownException();
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/BasicClientConnectionManager.java b/httpclient/src/main/java/org/apache/http/impl/conn/BasicClientConnectionManager.java
new file mode 100644
index 0000000..b4d645c
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/BasicClientConnectionManager.java
@@ -0,0 +1,275 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.annotation.GuardedBy;
+import org.apache.http.annotation.ThreadSafe;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.scheme.SchemeRegistry;
+
+/**
+ * A connection manager for a single connection. This connection manager maintains only one active
+ * connection at a time. Even though this class is thread-safe it ought to be used by one execution
+ * thread only.
+ * <p/>
+ * BasicClientConnManager will make an effort to reuse the connection for subsequent requests
+ * with the same {@link HttpRoute route}. It will, however, close the existing connection and
+ * open it for the given route, if the route of the persistent connection does not match that
+ * of the connection request. If the connection has been already been allocated
+ * {@link IllegalStateException} is thrown.
+ * <p/>
+ * This connection manager implementation can be used inside a EJB container instead of
+ * {@link PoolingClientConnectionManager}.
+ *
+ * @since 4.2
+ */
+ at ThreadSafe
+public class BasicClientConnectionManager implements ClientConnectionManager {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    private static final AtomicLong COUNTER = new AtomicLong();
+
+    /** The message to be logged on multiple allocation. */
+    public final static String MISUSE_MESSAGE =
+    "Invalid use of BasicClientConnManager: connection still allocated.\n" +
+    "Make sure to release the connection before allocating another one.";
+
+    /** The schemes supported by this connection manager. */
+    private final SchemeRegistry schemeRegistry;
+
+    /** The operator for opening and updating connections. */
+    private final ClientConnectionOperator connOperator;
+
+    /** The one and only entry in this pool. */
+    @GuardedBy("this")
+    private HttpPoolEntry poolEntry;
+
+    /** The currently issued managed connection, if any. */
+    @GuardedBy("this")
+    private ManagedClientConnectionImpl conn;
+
+    /** Indicates whether this connection manager is shut down. */
+    @GuardedBy("this")
+    private volatile boolean shutdown;
+
+    /**
+     * Creates a new simple connection manager.
+     *
+     * @param schreg    the scheme registry
+     */
+    public BasicClientConnectionManager(final SchemeRegistry schreg) {
+        if (schreg == null) {
+            throw new IllegalArgumentException("Scheme registry may not be null");
+        }
+        this.schemeRegistry = schreg;
+        this.connOperator = createConnectionOperator(schreg);
+    }
+
+    public BasicClientConnectionManager() {
+        this(SchemeRegistryFactory.createDefault());
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            shutdown();
+        } finally { // Make sure we call overridden method even if shutdown barfs
+            super.finalize();
+        }
+    }
+
+    public SchemeRegistry getSchemeRegistry() {
+        return this.schemeRegistry;
+    }
+
+    protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) {
+        return new DefaultClientConnectionOperator(schreg);
+    }
+
+    public final ClientConnectionRequest requestConnection(
+            final HttpRoute route,
+            final Object state) {
+
+        return new ClientConnectionRequest() {
+
+            public void abortRequest() {
+                // Nothing to abort, since requests are immediate.
+            }
+
+            public ManagedClientConnection getConnection(
+                    long timeout, TimeUnit tunit) {
+                return BasicClientConnectionManager.this.getConnection(
+                        route, state);
+            }
+
+        };
+    }
+
+    private void assertNotShutdown() {
+        if (this.shutdown) {
+            throw new IllegalStateException("Connection manager has been shut down");
+        }
+    }
+
+    ManagedClientConnection getConnection(final HttpRoute route, final Object state) {
+        if (route == null) {
+            throw new IllegalArgumentException("Route may not be null.");
+        }
+        assertNotShutdown();
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Get connection for route " + route);
+        }
+        synchronized (this) {
+            if (this.conn != null) {
+                throw new IllegalStateException(MISUSE_MESSAGE);
+            }
+            if (this.poolEntry != null && !this.poolEntry.getPlannedRoute().equals(route)) {
+                this.poolEntry.close();
+                this.poolEntry = null;
+            }
+            if (this.poolEntry == null) {
+                String id = Long.toString(COUNTER.getAndIncrement());
+                OperatedClientConnection conn = this.connOperator.createConnection();
+                this.poolEntry = new HttpPoolEntry(this.log, id, route, conn, 0, TimeUnit.MILLISECONDS);
+            }
+            long now = System.currentTimeMillis();
+            if (this.poolEntry.isExpired(now)) {
+                this.poolEntry.close();
+                this.poolEntry.getTracker().reset();
+            }
+            this.conn = new ManagedClientConnectionImpl(this, this.connOperator, this.poolEntry);
+            return this.conn;
+        }
+    }
+
+    public void releaseConnection(final ManagedClientConnection conn, long keepalive, TimeUnit tunit) {
+        assertNotShutdown();
+        if (!(conn instanceof ManagedClientConnectionImpl)) {
+            throw new IllegalArgumentException("Connection class mismatch, " +
+                 "connection not obtained from this manager");
+        }
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Releasing connection " + conn);
+        }
+        ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn;
+        synchronized (managedConn) {
+            if (managedConn.getPoolEntry() == null) {
+                return; // already released
+            }
+            ClientConnectionManager manager = managedConn.getManager();
+            if (manager != null && manager != this) {
+                throw new IllegalStateException("Connection not obtained from this manager");
+            }
+            synchronized (this) {
+                try {
+                    if (managedConn.isOpen() && !managedConn.isMarkedReusable()) {
+                        try {
+                            managedConn.shutdown();
+                        } catch (IOException iox) {
+                            if (this.log.isDebugEnabled()) {
+                                this.log.debug("I/O exception shutting down released connection", iox);
+                            }
+                        }
+                    }
+                    this.poolEntry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
+                    if (this.log.isDebugEnabled()) {
+                        String s;
+                        if (keepalive > 0) {
+                            s = "for " + keepalive + " " + tunit;
+                        } else {
+                            s = "indefinitely";
+                        }
+                        this.log.debug("Connection can be kept alive " + s);
+                    }
+                } finally {
+                    managedConn.detach();
+                    this.conn = null;
+                    if (this.poolEntry.isClosed()) {
+                        this.poolEntry = null;
+                    }
+                }
+            }
+        }
+    }
+
+    public void closeExpiredConnections() {
+        assertNotShutdown();
+        synchronized (this) {
+            long now = System.currentTimeMillis();
+            if (this.poolEntry != null && this.poolEntry.isExpired(now)) {
+                this.poolEntry.close();
+                this.poolEntry.getTracker().reset();
+            }
+        }
+    }
+
+    public void closeIdleConnections(long idletime, TimeUnit tunit) {
+        if (tunit == null) {
+            throw new IllegalArgumentException("Time unit must not be null.");
+        }
+        assertNotShutdown();
+        synchronized (this) {
+            long time = tunit.toMillis(idletime);
+            if (time < 0) {
+                time = 0;
+            }
+            long deadline = System.currentTimeMillis() - time;
+            if (this.poolEntry != null && this.poolEntry.getUpdated() <= deadline) {
+                this.poolEntry.close();
+                this.poolEntry.getTracker().reset();
+            }
+        }
+    }
+
+    public void shutdown() {
+        this.shutdown = true;
+        synchronized (this) {
+            try {
+                if (this.poolEntry != null) {
+                    this.poolEntry.close();
+                }
+            } finally {
+                this.poolEntry = null;
+                this.conn = null;
+            }
+        }
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnection.java b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnection.java
index a45133b..4b612d9 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnection.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnection.java
@@ -28,6 +28,7 @@
 package org.apache.http.impl.conn;
 
 import java.io.IOException;
+import java.io.InterruptedIOException;
 import java.net.Socket;
 import java.util.HashMap;
 import java.util.Map;
@@ -117,7 +118,7 @@ public class DefaultClientConnection extends SocketHttpClientConnection
         if (this.shutdown) {
             sock.close(); // allow this to throw...
             // ...but if it doesn't, explicitly throw one ourselves.
-            throw new IOException("Connection already shutdown");
+            throw new InterruptedIOException("Connection already shutdown");
         }
     }
 
@@ -149,7 +150,9 @@ public class DefaultClientConnection extends SocketHttpClientConnection
         shutdown = true;
         try {
             super.shutdown();
-            log.debug("Connection shut down");
+            if (log.isDebugEnabled()) {
+                log.debug("Connection " + this + " shut down");
+            }
             Socket sock = this.socket; // copy volatile attribute
             if (sock != null)
                 sock.close();
@@ -162,7 +165,9 @@ public class DefaultClientConnection extends SocketHttpClientConnection
     public void close() throws IOException {
         try {
             super.close();
-            log.debug("Connection closed");
+            if (log.isDebugEnabled()) {
+                log.debug("Connection " + this + " closed");
+            }
         } catch (IOException ex) {
             log.debug("I/O error closing connection", ex);
         }
@@ -211,12 +216,12 @@ public class DefaultClientConnection extends SocketHttpClientConnection
     }
 
     @Override
-    protected HttpMessageParser createResponseParser(
+    protected HttpMessageParser<HttpResponse> createResponseParser(
             final SessionInputBuffer buffer,
             final HttpResponseFactory responseFactory,
             final HttpParams params) {
         // override in derived class to specify a line parser
-        return new DefaultResponseParser
+        return new DefaultHttpResponseParser
             (buffer, null, responseFactory, params);
     }
 
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
index 9dfabce..19d40b6 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultClientConnectionOperator.java
@@ -45,13 +45,16 @@ import org.apache.http.protocol.HttpContext;
 
 import org.apache.http.conn.ConnectTimeoutException;
 import org.apache.http.conn.HttpHostConnectException;
+import org.apache.http.conn.HttpInetSocketAddress;
 import org.apache.http.conn.OperatedClientConnection;
 import org.apache.http.conn.ClientConnectionOperator;
-import org.apache.http.conn.scheme.LayeredSchemeSocketFactory;
+import org.apache.http.conn.scheme.SchemeLayeredSocketFactory;
 import org.apache.http.conn.scheme.Scheme;
 import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.conn.scheme.SchemeSocketFactory;
 
+import org.apache.http.conn.DnsResolver;
+
 /**
  * Default implementation of a {@link ClientConnectionOperator}. It uses a {@link SchemeRegistry}
  * to look up {@link SchemeSocketFactory} objects.
@@ -89,16 +92,45 @@ public class DefaultClientConnectionOperator implements ClientConnectionOperator
     /** The scheme registry for looking up socket factories. */
     protected final SchemeRegistry schemeRegistry; // @ThreadSafe
 
+    /** the custom-configured DNS lookup mechanism. */
+    protected final DnsResolver dnsResolver;
+
     /**
      * Creates a new client connection operator for the given scheme registry.
      *
      * @param schemes   the scheme registry
+     *
+     * @since 4.2
      */
     public DefaultClientConnectionOperator(final SchemeRegistry schemes) {
         if (schemes == null) {
             throw new IllegalArgumentException("Scheme registry amy not be null");
         }
         this.schemeRegistry = schemes;
+        this.dnsResolver = new SystemDefaultDnsResolver();
+    }
+
+    /**
+    * Creates a new client connection operator for the given scheme registry
+    * and the given custom DNS lookup mechanism.
+    *
+    * @param schemes
+    *            the scheme registry
+    * @param dnsResolver
+    *            the custom DNS lookup mechanism
+    */
+    public DefaultClientConnectionOperator(final SchemeRegistry schemes,final DnsResolver dnsResolver) {
+        if (schemes == null) {
+            throw new IllegalArgumentException(
+                     "Scheme registry may not be null");
+        }
+
+        if(dnsResolver == null){
+            throw new IllegalArgumentException("DNS resolver may not be null");
+        }
+
+        this.schemeRegistry = schemes;
+        this.dnsResolver = dnsResolver;
     }
 
     public OperatedClientConnection createConnection() {
@@ -136,7 +168,7 @@ public class DefaultClientConnectionOperator implements ClientConnectionOperator
             Socket sock = sf.createSocket(params);
             conn.opening(sock, target);
 
-            InetSocketAddress remoteAddress = new InetSocketAddress(address, port);
+            InetSocketAddress remoteAddress = new HttpInetSocketAddress(target, address, port);
             InetSocketAddress localAddress = null;
             if (local != null) {
                 localAddress = new InetSocketAddress(local, 0);
@@ -188,17 +220,17 @@ public class DefaultClientConnectionOperator implements ClientConnectionOperator
         }
 
         final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
-        if (!(schm.getSchemeSocketFactory() instanceof LayeredSchemeSocketFactory)) {
+        if (!(schm.getSchemeSocketFactory() instanceof SchemeLayeredSocketFactory)) {
             throw new IllegalArgumentException
                 ("Target scheme (" + schm.getName() +
                  ") must have layered socket factory.");
         }
 
-        LayeredSchemeSocketFactory lsf = (LayeredSchemeSocketFactory) schm.getSchemeSocketFactory();
+        SchemeLayeredSocketFactory lsf = (SchemeLayeredSocketFactory) schm.getSchemeSocketFactory();
         Socket sock;
         try {
             sock = lsf.createLayeredSocket(
-                    conn.getSocket(), target.getHostName(), target.getPort(), true);
+                    conn.getSocket(), target.getHostName(), target.getPort(), params);
         } catch (ConnectException ex) {
             throw new HttpHostConnectException(target, ex);
         }
@@ -230,16 +262,20 @@ public class DefaultClientConnectionOperator implements ClientConnectionOperator
 
     /**
      * Resolves the given host name to an array of corresponding IP addresses, based on the
-     * configured name service on the system.
+     * configured name service on the provided DNS resolver. If one wasn't provided, the system
+     * configuration is used.
      *
      * @param host host name to resolve
      * @return array of IP addresses
      * @exception  UnknownHostException  if no IP address for the host could be determined.
      *
+     * @see DnsResolver
+     * @see SystemDefaultDnsResolver
+     *
      * @since 4.1
      */
     protected InetAddress[] resolveHostname(final String host) throws UnknownHostException {
-        return InetAddress.getAllByName(host);
+            return dnsResolver.resolve(host);
     }
 
 }
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpResponseParser.java b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpResponseParser.java
new file mode 100644
index 0000000..98b9831
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpResponseParser.java
@@ -0,0 +1,120 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+
+import org.apache.http.annotation.ThreadSafe;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.ProtocolException;
+import org.apache.http.StatusLine;
+import org.apache.http.impl.io.AbstractMessageParser;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.message.LineParser;
+import org.apache.http.message.ParserCursor;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * Default HTTP response parser implementation.
+ * <p>
+ * The following parameters can be used to customize the behavior of this
+ * class:
+ * <ul>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
+ *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
+ * </ul>
+ *
+ * @since 4.2
+ */
+ at ThreadSafe // no public methods
+public class DefaultHttpResponseParser extends AbstractMessageParser<HttpResponse> {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    private final HttpResponseFactory responseFactory;
+    private final CharArrayBuffer lineBuf;
+
+    public DefaultHttpResponseParser(
+            final SessionInputBuffer buffer,
+            final LineParser parser,
+            final HttpResponseFactory responseFactory,
+            final HttpParams params) {
+        super(buffer, parser, params);
+        if (responseFactory == null) {
+            throw new IllegalArgumentException
+                ("Response factory may not be null");
+        }
+        this.responseFactory = responseFactory;
+        this.lineBuf = new CharArrayBuffer(128);
+    }
+
+    @Override
+    protected HttpResponse parseHead(
+            final SessionInputBuffer sessionBuffer) throws IOException, HttpException {
+        //read out the HTTP status string
+        int count = 0;
+        ParserCursor cursor = null;
+        do {
+            // clear the buffer
+            this.lineBuf.clear();
+            int i = sessionBuffer.readLine(this.lineBuf);
+            if (i == -1 && count == 0) {
+                // The server just dropped connection on us
+                throw new NoHttpResponseException("The target server failed to respond");
+            }
+            cursor = new ParserCursor(0, this.lineBuf.length());
+            if (lineParser.hasProtocolVersion(this.lineBuf, cursor)) {
+                // Got one
+                break;
+            } else if (i == -1 || reject(this.lineBuf, count)) {
+                // Giving up
+                throw new ProtocolException("The server failed to respond with a " +
+                        "valid HTTP response");
+            }
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Garbage in response: " + this.lineBuf.toString());
+            }
+            count++;
+        } while(true);
+        //create the status line from the status string
+        StatusLine statusline = lineParser.parseStatusLine(this.lineBuf, cursor);
+        return this.responseFactory.newHttpResponse(statusline, null);
+    }
+
+    protected boolean reject(CharArrayBuffer line, int count) {
+        return false;
+    }
+    
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java
index ba61ad0..4d0c2d1 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultHttpRoutePlanner.java
@@ -108,7 +108,12 @@ public class DefaultHttpRoutePlanner implements HttpRoutePlanner {
         final HttpHost proxy =
             ConnRouteParams.getDefaultProxy(request.getParams());
 
-        final Scheme schm = schemeRegistry.getScheme(target.getSchemeName());
+        final Scheme schm;
+        try {
+            schm = schemeRegistry.getScheme(target.getSchemeName());
+        } catch (IllegalStateException ex) {
+            throw new HttpException(ex.getMessage());
+        }
         // as it is typically used for TLS/SSL, we assume that
         // a layered scheme implies a secure connection
         final boolean secure = schm.isLayered();
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultResponseParser.java b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultResponseParser.java
index 43f296f..894fa13 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/DefaultResponseParser.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/DefaultResponseParser.java
@@ -39,7 +39,6 @@ import org.apache.http.HttpResponseFactory;
 import org.apache.http.NoHttpResponseException;
 import org.apache.http.ProtocolException;
 import org.apache.http.StatusLine;
-import org.apache.http.conn.params.ConnConnectionPNames;
 import org.apache.http.impl.io.AbstractMessageParser;
 import org.apache.http.io.SessionInputBuffer;
 import org.apache.http.message.LineParser;
@@ -59,9 +58,11 @@ import org.apache.http.util.CharArrayBuffer;
  * </ul>
  *
  * @since 4.0
+ * 
+ * @deprecated (4.2) use {@link DefaultHttpResponseParser}
  */
 @ThreadSafe // no public methods
-public class DefaultResponseParser extends AbstractMessageParser {
+public class DefaultResponseParser extends AbstractMessageParser<HttpMessage> {
 
     private final Log log = LogFactory.getLog(getClass());
 
@@ -81,10 +82,14 @@ public class DefaultResponseParser extends AbstractMessageParser {
         }
         this.responseFactory = responseFactory;
         this.lineBuf = new CharArrayBuffer(128);
-        this.maxGarbageLines = params.getIntParameter(
-            ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE, Integer.MAX_VALUE);
+        this.maxGarbageLines = getMaxGarbageLines(params);
     }
 
+    protected int getMaxGarbageLines(final HttpParams params) {
+        return params.getIntParameter(
+                org.apache.http.conn.params.ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE,
+                Integer.MAX_VALUE);
+    }
 
     @Override
     protected HttpMessage parseHead(
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/HttpConnPool.java b/httpclient/src/main/java/org/apache/http/impl/conn/HttpConnPool.java
new file mode 100644
index 0000000..ee27b75
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/HttpConnPool.java
@@ -0,0 +1,73 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.pool.AbstractConnPool;
+import org.apache.http.pool.ConnFactory;
+
+/**
+ * @since 4.2
+ */
+class HttpConnPool extends AbstractConnPool<HttpRoute, OperatedClientConnection, HttpPoolEntry> {
+
+    private static AtomicLong COUNTER = new AtomicLong();
+
+    private final Log log;
+    private final long timeToLive;
+    private final TimeUnit tunit;
+
+    public HttpConnPool(final Log log,
+            final int defaultMaxPerRoute, final int maxTotal,
+            final long timeToLive, final TimeUnit tunit) {
+        super(new InternalConnFactory(), defaultMaxPerRoute, maxTotal);
+        this.log = log;
+        this.timeToLive = timeToLive;
+        this.tunit = tunit;
+    }
+
+    @Override
+    protected HttpPoolEntry createEntry(final HttpRoute route, final OperatedClientConnection conn) {
+        String id = Long.toString(COUNTER.getAndIncrement());
+        return new HttpPoolEntry(this.log, id, route, conn, this.timeToLive, this.tunit);
+    }
+
+    static class InternalConnFactory implements ConnFactory<HttpRoute, OperatedClientConnection> {
+
+        public OperatedClientConnection create(final HttpRoute route) throws IOException {
+            return new DefaultClientConnection();
+        }
+
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/HttpPoolEntry.java b/httpclient/src/main/java/org/apache/http/impl/conn/HttpPoolEntry.java
new file mode 100644
index 0000000..71ac85b
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/HttpPoolEntry.java
@@ -0,0 +1,95 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.RouteTracker;
+import org.apache.http.pool.PoolEntry;
+
+/**
+ * @since 4.2
+ */
+class HttpPoolEntry extends PoolEntry<HttpRoute, OperatedClientConnection> {
+
+    private final Log log;
+    private final RouteTracker tracker;
+
+    public HttpPoolEntry(
+            final Log log,
+            final String id,
+            final HttpRoute route,
+            final OperatedClientConnection conn,
+            final long timeToLive, final TimeUnit tunit) {
+        super(id, route, conn, timeToLive, tunit);
+        this.log = log;
+        this.tracker = new RouteTracker(route);
+    }
+
+    @Override
+    public boolean isExpired(long now) {
+        boolean expired = super.isExpired(now);
+        if (expired && this.log.isDebugEnabled()) {
+            this.log.debug("Connection " + this + " expired @ " + new Date(getExpiry()));
+        }
+        return expired;
+    }
+
+    RouteTracker getTracker() {
+        return this.tracker;
+    }
+
+    HttpRoute getPlannedRoute() {
+        return getRoute();
+    }
+
+    HttpRoute getEffectiveRoute() {
+        return this.tracker.toRoute();
+    }
+
+    @Override
+    public boolean isClosed() {
+        OperatedClientConnection conn = getConnection();
+        return !conn.isOpen();
+    }
+
+    @Override
+    public void close() {
+        OperatedClientConnection conn = getConnection();
+        try {
+            conn.close();
+        } catch (IOException ex) {
+            this.log.debug("I/O error closing connection", ex);
+        }
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/IdleConnectionHandler.java b/httpclient/src/main/java/org/apache/http/impl/conn/IdleConnectionHandler.java
index aa5813a..ac5ce51 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/IdleConnectionHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/IdleConnectionHandler.java
@@ -45,7 +45,7 @@ import org.apache.http.HttpConnection;
  *
  * @since 4.0
  *
- * @deprecated no longer used
+ * @deprecated (4.1)  no longer used
  */
 @Deprecated
 public class IdleConnectionHandler {
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/InMemoryDnsResolver.java b/httpclient/src/main/java/org/apache/http/impl/conn/InMemoryDnsResolver.java
new file mode 100644
index 0000000..fc06da3
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/InMemoryDnsResolver.java
@@ -0,0 +1,97 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.conn;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.conn.DnsResolver;
+
+/**
+ * In-memory DNS resolver implementation.
+ *
+ * @since 4.2
+ */
+public class InMemoryDnsResolver implements DnsResolver {
+
+    /** Logger associated to this class. */
+    private final Log log = LogFactory.getLog(InMemoryDnsResolver.class);
+
+    /**
+     * In-memory collection that will hold the associations between a host name
+     * and an array of InetAddress instances.
+     */
+    private Map<String, InetAddress[]> dnsMap;
+
+    /**
+     * Builds a DNS resolver that will resolve the host names against a
+     * collection held in-memory.
+     */
+    public InMemoryDnsResolver() {
+        dnsMap = new ConcurrentHashMap<String, InetAddress[]>();
+    }
+
+    /**
+     * Associates the given array of IP addresses to the given host in this DNS overrider.
+     * The IP addresses are assumed to be already resolved.
+     *
+     * @param host
+     *            The host name to be associated with the given IP.
+     * @param ips
+     *            array of IP addresses to be resolved by this DNS overrider to the given
+     *            host name.
+     */
+    public void add(final String host, final InetAddress... ips) {
+        if (host == null) {
+            throw new IllegalArgumentException("Host name may not be null");
+        }
+        if (ips == null) {
+            throw new IllegalArgumentException("Array of IP addresses may not be null");
+        }
+        dnsMap.put(host, ips);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public InetAddress[] resolve(String host) throws UnknownHostException {
+        InetAddress[] resolvedAddresses = dnsMap.get(host);
+        if (log.isInfoEnabled()) {
+            log.info("Resolving " + host + " to " + Arrays.deepToString(resolvedAddresses));
+        }
+        if(resolvedAddresses == null){
+            throw new UnknownHostException(host + " cannot be resolved");
+        }
+        return resolvedAddresses;
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/LoggingSessionInputBuffer.java b/httpclient/src/main/java/org/apache/http/impl/conn/LoggingSessionInputBuffer.java
index 0562075..ff6cfa2 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/LoggingSessionInputBuffer.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/LoggingSessionInputBuffer.java
@@ -28,12 +28,12 @@ package org.apache.http.impl.conn;
 
 import java.io.IOException;
 
+import org.apache.http.Consts;
 import org.apache.http.annotation.Immutable;
 
 import org.apache.http.io.EofSensor;
 import org.apache.http.io.HttpTransportMetrics;
 import org.apache.http.io.SessionInputBuffer;
-import org.apache.http.protocol.HTTP;
 import org.apache.http.util.CharArrayBuffer;
 
 /**
@@ -67,7 +67,7 @@ public class LoggingSessionInputBuffer implements SessionInputBuffer, EofSensor
         this.in = in;
         this.eofSensor = in instanceof EofSensor ? (EofSensor) in : null;
         this.wire = wire;
-        this.charset = charset != null ? charset : HTTP.ASCII;
+        this.charset = charset != null ? charset : Consts.ASCII.name();
     }
 
     public LoggingSessionInputBuffer(final SessionInputBuffer in, final Wire wire) {
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java b/httpclient/src/main/java/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java
index 803853b..d6a332e 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/LoggingSessionOutputBuffer.java
@@ -28,11 +28,11 @@ package org.apache.http.impl.conn;
 
 import java.io.IOException;
 
+import org.apache.http.Consts;
 import org.apache.http.annotation.Immutable;
 
 import org.apache.http.io.HttpTransportMetrics;
 import org.apache.http.io.SessionOutputBuffer;
-import org.apache.http.protocol.HTTP;
 import org.apache.http.util.CharArrayBuffer;
 
 /**
@@ -63,7 +63,7 @@ public class LoggingSessionOutputBuffer implements SessionOutputBuffer {
         super();
         this.out = out;
         this.wire = wire;
-        this.charset = charset != null ? charset : HTTP.ASCII;
+        this.charset = charset != null ? charset : Consts.ASCII.name();
     }
 
     public LoggingSessionOutputBuffer(final SessionOutputBuffer out, final Wire wire) {
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/ManagedClientConnectionImpl.java b/httpclient/src/main/java/org/apache/http/impl/conn/ManagedClientConnectionImpl.java
new file mode 100644
index 0000000..83df90e
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/ManagedClientConnectionImpl.java
@@ -0,0 +1,468 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.annotation.NotThreadSafe;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.RouteTracker;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+ at NotThreadSafe
+class ManagedClientConnectionImpl implements ManagedClientConnection {
+
+    private final ClientConnectionManager manager;
+    private final ClientConnectionOperator operator;
+    private volatile HttpPoolEntry poolEntry;
+    private volatile boolean reusable;
+    private volatile long duration;
+
+    ManagedClientConnectionImpl(
+            final ClientConnectionManager manager,
+            final ClientConnectionOperator operator,
+            final HttpPoolEntry entry) {
+        super();
+        if (manager == null) {
+            throw new IllegalArgumentException("Connection manager may not be null");
+        }
+        if (operator == null) {
+            throw new IllegalArgumentException("Connection operator may not be null");
+        }
+        if (entry == null) {
+            throw new IllegalArgumentException("HTTP pool entry may not be null");
+        }
+        this.manager = manager;
+        this.operator = operator;
+        this.poolEntry = entry;
+        this.reusable = false;
+        this.duration = Long.MAX_VALUE;
+    }
+
+    HttpPoolEntry getPoolEntry() {
+        return this.poolEntry;
+    }
+
+    HttpPoolEntry detach() {
+        HttpPoolEntry local = this.poolEntry;
+        this.poolEntry = null;
+        return local;
+    }
+
+    public ClientConnectionManager getManager() {
+        return this.manager;
+    }
+
+    private OperatedClientConnection getConnection() {
+        HttpPoolEntry local = this.poolEntry;
+        if (local == null) {
+            return null;
+        }
+        return local.getConnection();
+    }
+
+    private OperatedClientConnection ensureConnection() {
+        HttpPoolEntry local = this.poolEntry;
+        if (local == null) {
+            throw new ConnectionShutdownException();
+        }
+        return local.getConnection();
+    }
+
+    private HttpPoolEntry ensurePoolEntry() {
+        HttpPoolEntry local = this.poolEntry;
+        if (local == null) {
+            throw new ConnectionShutdownException();
+        }
+        return local;
+    }
+
+    public void close() throws IOException {
+        HttpPoolEntry local = this.poolEntry;
+        if (local != null) {
+            OperatedClientConnection conn = local.getConnection();
+            local.getTracker().reset();
+            conn.close();
+        }
+    }
+
+    public void shutdown() throws IOException {
+        HttpPoolEntry local = this.poolEntry;
+        if (local != null) {
+            OperatedClientConnection conn = local.getConnection();
+            local.getTracker().reset();
+            conn.shutdown();
+        }
+    }
+
+    public boolean isOpen() {
+        OperatedClientConnection conn = getConnection();
+        if (conn != null) {
+            return conn.isOpen();
+        } else {
+            return false;
+        }
+    }
+
+    public boolean isStale() {
+        OperatedClientConnection conn = getConnection();
+        if (conn != null) {
+            return conn.isStale();
+        } else {
+            return true;
+        }
+    }
+
+    public void setSocketTimeout(int timeout) {
+        OperatedClientConnection conn = ensureConnection();
+        conn.setSocketTimeout(timeout);
+    }
+
+    public int getSocketTimeout() {
+        OperatedClientConnection conn = ensureConnection();
+        return conn.getSocketTimeout();
+    }
+
+    public HttpConnectionMetrics getMetrics() {
+        OperatedClientConnection conn = ensureConnection();
+        return conn.getMetrics();
+    }
+
+    public void flush() throws IOException {
+        OperatedClientConnection conn = ensureConnection();
+        conn.flush();
+    }
+
+    public boolean isResponseAvailable(int timeout) throws IOException {
+        OperatedClientConnection conn = ensureConnection();
+        return conn.isResponseAvailable(timeout);
+    }
+
+    public void receiveResponseEntity(
+            final HttpResponse response) throws HttpException, IOException {
+        OperatedClientConnection conn = ensureConnection();
+        conn.receiveResponseEntity(response);
+    }
+
+    public HttpResponse receiveResponseHeader() throws HttpException, IOException {
+        OperatedClientConnection conn = ensureConnection();
+        return conn.receiveResponseHeader();
+    }
+
+    public void sendRequestEntity(
+            final HttpEntityEnclosingRequest request) throws HttpException, IOException {
+        OperatedClientConnection conn = ensureConnection();
+        conn.sendRequestEntity(request);
+    }
+
+    public void sendRequestHeader(
+            final HttpRequest request) throws HttpException, IOException {
+        OperatedClientConnection conn = ensureConnection();
+        conn.sendRequestHeader(request);
+    }
+
+    public InetAddress getLocalAddress() {
+        OperatedClientConnection conn = ensureConnection();
+        return conn.getLocalAddress();
+    }
+
+    public int getLocalPort() {
+        OperatedClientConnection conn = ensureConnection();
+        return conn.getLocalPort();
+    }
+
+    public InetAddress getRemoteAddress() {
+        OperatedClientConnection conn = ensureConnection();
+        return conn.getRemoteAddress();
+    }
+
+    public int getRemotePort() {
+        OperatedClientConnection conn = ensureConnection();
+        return conn.getRemotePort();
+    }
+
+    public boolean isSecure() {
+        OperatedClientConnection conn = ensureConnection();
+        return conn.isSecure();
+    }
+
+    public SSLSession getSSLSession() {
+        OperatedClientConnection conn = ensureConnection();
+        SSLSession result = null;
+        Socket sock = conn.getSocket();
+        if (sock instanceof SSLSocket) {
+            result = ((SSLSocket)sock).getSession();
+        }
+        return result;
+    }
+
+    public Object getAttribute(final String id) {
+        OperatedClientConnection conn = ensureConnection();
+        if (conn instanceof HttpContext) {
+            return ((HttpContext) conn).getAttribute(id);
+        } else {
+            return null;
+        }
+    }
+
+    public Object removeAttribute(final String id) {
+        OperatedClientConnection conn = ensureConnection();
+        if (conn instanceof HttpContext) {
+            return ((HttpContext) conn).removeAttribute(id);
+        } else {
+            return null;
+        }
+    }
+
+    public void setAttribute(final String id, final Object obj) {
+        OperatedClientConnection conn = ensureConnection();
+        if (conn instanceof HttpContext) {
+            ((HttpContext) conn).setAttribute(id, obj);
+        }
+    }
+
+    public HttpRoute getRoute() {
+        HttpPoolEntry local = ensurePoolEntry();
+        return local.getEffectiveRoute();
+    }
+
+    public void open(
+            final HttpRoute route,
+            final HttpContext context,
+            final HttpParams params) throws IOException {
+        if (route == null) {
+            throw new IllegalArgumentException("Route may not be null");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        OperatedClientConnection conn;
+        synchronized (this) {
+            if (this.poolEntry == null) {
+                throw new ConnectionShutdownException();
+            }
+            RouteTracker tracker = this.poolEntry.getTracker();
+            if (tracker.isConnected()) {
+                throw new IllegalStateException("Connection already open");
+            }
+            conn = this.poolEntry.getConnection();
+        }
+
+        HttpHost proxy  = route.getProxyHost();
+        this.operator.openConnection(
+                conn,
+                (proxy != null) ? proxy : route.getTargetHost(),
+                route.getLocalAddress(),
+                context, params);
+
+        synchronized (this) {
+            if (this.poolEntry == null) {
+                throw new InterruptedIOException();
+            }
+            RouteTracker tracker = this.poolEntry.getTracker();
+            if (proxy == null) {
+                tracker.connectTarget(conn.isSecure());
+            } else {
+                tracker.connectProxy(proxy, conn.isSecure());
+            }
+        }
+    }
+
+    public void tunnelTarget(
+            boolean secure, final HttpParams params) throws IOException {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        HttpHost target;
+        OperatedClientConnection conn;
+        synchronized (this) {
+            if (this.poolEntry == null) {
+                throw new ConnectionShutdownException();
+            }
+            RouteTracker tracker = this.poolEntry.getTracker();
+            if (!tracker.isConnected()) {
+                throw new IllegalStateException("Connection not open");
+            }
+            if (tracker.isTunnelled()) {
+                throw new IllegalStateException("Connection is already tunnelled");
+            }
+            target = tracker.getTargetHost();
+            conn = this.poolEntry.getConnection();
+        }
+
+        conn.update(null, target, secure, params);
+
+        synchronized (this) {
+            if (this.poolEntry == null) {
+                throw new InterruptedIOException();
+            }
+            RouteTracker tracker = this.poolEntry.getTracker();
+            tracker.tunnelTarget(secure);
+        }
+    }
+
+    public void tunnelProxy(
+            final HttpHost next, boolean secure, final HttpParams params) throws IOException {
+        if (next == null) {
+            throw new IllegalArgumentException("Next proxy amy not be null");
+        }
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        OperatedClientConnection conn;
+        synchronized (this) {
+            if (this.poolEntry == null) {
+                throw new ConnectionShutdownException();
+            }
+            RouteTracker tracker = this.poolEntry.getTracker();
+            if (!tracker.isConnected()) {
+                throw new IllegalStateException("Connection not open");
+            }
+            conn = this.poolEntry.getConnection();
+        }
+
+        conn.update(null, next, secure, params);
+
+        synchronized (this) {
+            if (this.poolEntry == null) {
+                throw new InterruptedIOException();
+            }
+            RouteTracker tracker = this.poolEntry.getTracker();
+            tracker.tunnelProxy(next, secure);
+        }
+    }
+
+    public void layerProtocol(
+            final HttpContext context, final HttpParams params) throws IOException {
+        if (params == null) {
+            throw new IllegalArgumentException("HTTP parameters may not be null");
+        }
+        HttpHost target;
+        OperatedClientConnection conn;
+        synchronized (this) {
+            if (this.poolEntry == null) {
+                throw new ConnectionShutdownException();
+            }
+            RouteTracker tracker = this.poolEntry.getTracker();
+            if (!tracker.isConnected()) {
+                throw new IllegalStateException("Connection not open");
+            }
+            if (!tracker.isTunnelled()) {
+                throw new IllegalStateException("Protocol layering without a tunnel not supported");
+            }
+            if (tracker.isLayered()) {
+                throw new IllegalStateException("Multiple protocol layering not supported");
+            }
+            target = tracker.getTargetHost();
+            conn = this.poolEntry.getConnection();
+        }
+        this.operator.updateSecureConnection(conn, target, context, params);
+
+        synchronized (this) {
+            if (this.poolEntry == null) {
+                throw new InterruptedIOException();
+            }
+            RouteTracker tracker = this.poolEntry.getTracker();
+            tracker.layerProtocol(conn.isSecure());
+        }
+    }
+
+    public Object getState() {
+        HttpPoolEntry local = ensurePoolEntry();
+        return local.getState();
+    }
+
+    public void setState(final Object state) {
+        HttpPoolEntry local = ensurePoolEntry();
+        local.setState(state);
+    }
+
+    public void markReusable() {
+        this.reusable = true;
+    }
+
+    public void unmarkReusable() {
+        this.reusable = false;
+    }
+
+    public boolean isMarkedReusable() {
+        return this.reusable;
+    }
+
+    public void setIdleDuration(long duration, TimeUnit unit) {
+        if(duration > 0) {
+            this.duration = unit.toMillis(duration);
+        } else {
+            this.duration = -1;
+        }
+    }
+
+    public void releaseConnection() {
+        synchronized (this) {
+            if (this.poolEntry == null) {
+                return;
+            }
+            this.manager.releaseConnection(this, this.duration, TimeUnit.MILLISECONDS);
+            this.poolEntry = null;
+        }
+    }
+
+    public void abortConnection() {
+        synchronized (this) {
+            if (this.poolEntry == null) {
+                return;
+            }
+            this.reusable = false;
+            OperatedClientConnection conn = this.poolEntry.getConnection();
+            try {
+                conn.shutdown();
+            } catch (IOException ignore) {
+            }
+            this.manager.releaseConnection(this, this.duration, TimeUnit.MILLISECONDS);
+            this.poolEntry = null;
+        }
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java b/httpclient/src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java
new file mode 100644
index 0000000..48796cf
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/PoolingClientConnectionManager.java
@@ -0,0 +1,338 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.annotation.ThreadSafe;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.pool.ConnPoolControl;
+import org.apache.http.pool.PoolStats;
+import org.apache.http.impl.conn.DefaultClientConnectionOperator;
+import org.apache.http.impl.conn.SchemeRegistryFactory;
+import org.apache.http.conn.DnsResolver;
+
+/**
+ * Manages a pool of {@link OperatedClientConnection client connections} and
+ * is able to service connection requests from multiple execution threads.
+ * Connections are pooled on a per route basis. A request for a route which
+ * already the manager has persistent connections for available in the pool
+ * will be services by leasing a connection from the pool rather than
+ * creating a brand new connection.
+ * <p>
+ * PoolingConnectionManager maintains a maximum limit of connection on
+ * a per route basis and in total. Per default this implementation will
+ * create no more than than 2 concurrent connections per given route
+ * and no more 20 connections in total. For many real-world applications
+ * these limits may prove too constraining, especially if they use HTTP
+ * as a transport protocol for their services. Connection limits, however,
+ * can be adjusted using HTTP parameters.
+ *
+ * @since 4.2
+ */
+ at ThreadSafe
+public class PoolingClientConnectionManager implements ClientConnectionManager, ConnPoolControl<HttpRoute> {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    private final SchemeRegistry schemeRegistry;
+
+    private final HttpConnPool pool;
+
+    private final ClientConnectionOperator operator;
+
+    /** the custom-configured DNS lookup mechanism. */
+    private final DnsResolver dnsResolver;
+
+    public PoolingClientConnectionManager(final SchemeRegistry schreg) {
+        this(schreg, -1, TimeUnit.MILLISECONDS);
+    }
+
+    public PoolingClientConnectionManager(final SchemeRegistry schreg,final DnsResolver dnsResolver) {
+        this(schreg, -1, TimeUnit.MILLISECONDS,dnsResolver);
+    }
+
+    public PoolingClientConnectionManager() {
+        this(SchemeRegistryFactory.createDefault());
+    }
+
+    public PoolingClientConnectionManager(
+            final SchemeRegistry schemeRegistry,
+            final long timeToLive, final TimeUnit tunit) {
+        this(schemeRegistry, timeToLive, tunit, new SystemDefaultDnsResolver());
+    }
+
+    public PoolingClientConnectionManager(final SchemeRegistry schemeRegistry,
+                final long timeToLive, final TimeUnit tunit,
+                final DnsResolver dnsResolver) {
+        super();
+        if (schemeRegistry == null) {
+            throw new IllegalArgumentException("Scheme registry may not be null");
+        }
+        if (dnsResolver == null) {
+            throw new IllegalArgumentException("DNS resolver may not be null");
+        }
+        this.schemeRegistry = schemeRegistry;
+        this.dnsResolver  = dnsResolver;
+        this.operator = createConnectionOperator(schemeRegistry);
+        this.pool = new HttpConnPool(this.log, 2, 20, timeToLive, tunit);
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            shutdown();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Hook for creating the connection operator.
+     * It is called by the constructor.
+     * Derived classes can override this method to change the
+     * instantiation of the operator.
+     * The default implementation here instantiates
+     * {@link DefaultClientConnectionOperator DefaultClientConnectionOperator}.
+     *
+     * @param schreg    the scheme registry.
+     *
+     * @return  the connection operator to use
+     */
+    protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) {
+            return new DefaultClientConnectionOperator(schreg, this.dnsResolver);
+    }
+
+    public SchemeRegistry getSchemeRegistry() {
+        return this.schemeRegistry;
+    }
+
+    private String format(final HttpRoute route, final Object state) {
+        StringBuilder buf = new StringBuilder();
+        buf.append("[route: ").append(route).append("]");
+        if (state != null) {
+            buf.append("[state: ").append(state).append("]");
+        }
+        return buf.toString();
+    }
+
+    private String formatStats(final HttpRoute route) {
+        StringBuilder buf = new StringBuilder();
+        PoolStats totals = this.pool.getTotalStats();
+        PoolStats stats = this.pool.getStats(route);
+        buf.append("[total kept alive: ").append(totals.getAvailable()).append("; ");
+        buf.append("route allocated: ").append(stats.getLeased() + stats.getAvailable());
+        buf.append(" of ").append(stats.getMax()).append("; ");
+        buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable());
+        buf.append(" of ").append(totals.getMax()).append("]");
+        return buf.toString();
+    }
+
+    private String format(final HttpPoolEntry entry) {
+        StringBuilder buf = new StringBuilder();
+        buf.append("[id: ").append(entry.getId()).append("]");
+        buf.append("[route: ").append(entry.getRoute()).append("]");
+        Object state = entry.getState();
+        if (state != null) {
+            buf.append("[state: ").append(state).append("]");
+        }
+        return buf.toString();
+    }
+
+    public ClientConnectionRequest requestConnection(
+            final HttpRoute route,
+            final Object state) {
+        if (route == null) {
+            throw new IllegalArgumentException("HTTP route may not be null");
+        }
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Connection request: " + format(route, state) + formatStats(route));
+        }
+        final Future<HttpPoolEntry> future = this.pool.lease(route, state);
+
+        return new ClientConnectionRequest() {
+
+            public void abortRequest() {
+                future.cancel(true);
+            }
+
+            public ManagedClientConnection getConnection(
+                    final long timeout,
+                    final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException {
+                return leaseConnection(future, timeout, tunit);
+            }
+
+        };
+
+    }
+
+    ManagedClientConnection leaseConnection(
+            final Future<HttpPoolEntry> future,
+            final long timeout,
+            final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException {
+        HttpPoolEntry entry;
+        try {
+            entry = future.get(timeout, tunit);
+            if (entry == null || future.isCancelled()) {
+                throw new InterruptedException();
+            }
+            if (entry.getConnection() == null) {
+                throw new IllegalStateException("Pool entry with no connection");
+            }
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute()));
+            }
+            return new ManagedClientConnectionImpl(this, this.operator, entry);
+        } catch (ExecutionException ex) {
+            Throwable cause = ex.getCause();
+            if (cause == null) {
+                cause = ex;
+            }
+            this.log.error("Unexpected exception leasing connection from pool", cause);
+            // Should never happen
+            throw new InterruptedException();
+        } catch (TimeoutException ex) {
+            throw new ConnectionPoolTimeoutException("Timeout waiting for connection");
+        }
+    }
+
+    public void releaseConnection(
+            final ManagedClientConnection conn, final long keepalive, final TimeUnit tunit) {
+
+        if (!(conn instanceof ManagedClientConnectionImpl)) {
+            throw new IllegalArgumentException
+                ("Connection class mismatch, " +
+                 "connection not obtained from this manager.");
+        }
+        ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn;
+        if (managedConn.getManager() != this) {
+            throw new IllegalStateException("Connection not obtained from this manager.");
+        }
+
+        synchronized (managedConn) {
+            HttpPoolEntry entry = managedConn.detach();
+            if (entry == null) {
+                return;
+            }
+            try {
+                if (managedConn.isOpen() && !managedConn.isMarkedReusable()) {
+                    try {
+                        managedConn.shutdown();
+                    } catch (IOException iox) {
+                        if (this.log.isDebugEnabled()) {
+                            this.log.debug("I/O exception shutting down released connection", iox);
+                        }
+                    }
+                }
+                entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
+                if (this.log.isDebugEnabled()) {
+                    String s;
+                    if (keepalive > 0) {
+                        s = "for " + keepalive + " " + tunit;
+                    } else {
+                        s = "indefinitely";
+                    }
+                    this.log.debug("Connection " + format(entry) + " can be kept alive " + s);
+                }
+            } finally {
+                this.pool.release(entry, managedConn.isMarkedReusable());
+            }
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute()));
+            }
+        }
+    }
+
+    public void shutdown() {
+        this.log.debug("Connection manager is shutting down");
+        try {
+            this.pool.shutdown();
+        } catch (IOException ex) {
+            this.log.debug("I/O exception shutting down connection manager", ex);
+        }
+        this.log.debug("Connection manager shut down");
+    }
+
+    public void closeIdleConnections(long idleTimeout, TimeUnit tunit) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit);
+        }
+        this.pool.closeIdle(idleTimeout, tunit);
+    }
+
+    public void closeExpiredConnections() {
+        this.log.debug("Closing expired connections");
+        this.pool.closeExpired();
+    }
+
+    public int getMaxTotal() {
+        return this.pool.getMaxTotal();
+    }
+
+    public void setMaxTotal(int max) {
+        this.pool.setMaxTotal(max);
+    }
+
+    public int getDefaultMaxPerRoute() {
+        return this.pool.getDefaultMaxPerRoute();
+    }
+
+    public void setDefaultMaxPerRoute(int max) {
+        this.pool.setDefaultMaxPerRoute(max);
+    }
+
+    public int getMaxPerRoute(final HttpRoute route) {
+        return this.pool.getMaxPerRoute(route);
+    }
+
+    public void setMaxPerRoute(final HttpRoute route, int max) {
+        this.pool.setMaxPerRoute(route, max);
+    }
+
+    public PoolStats getTotalStats() {
+        return this.pool.getTotalStats();
+    }
+
+    public PoolStats getStats(final HttpRoute route) {
+        return this.pool.getStats(route);
+    }
+
+}
+
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/SchemeRegistryFactory.java b/httpclient/src/main/java/org/apache/http/impl/conn/SchemeRegistryFactory.java
index 52ee804..b94c038 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/SchemeRegistryFactory.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/SchemeRegistryFactory.java
@@ -38,6 +38,10 @@ import org.apache.http.conn.ssl.SSLSocketFactory;
 @ThreadSafe
 public final class SchemeRegistryFactory {
 
+    /**
+     * Initializes default scheme registry based on JSSE defaults. System properties will
+     * not be taken into consideration.
+     */
     public static SchemeRegistry createDefault() {
         SchemeRegistry registry = new SchemeRegistry();
         registry.register(
@@ -47,5 +51,37 @@ public final class SchemeRegistryFactory {
         return registry;
     }
 
+    /**
+     * Initializes default scheme registry using system properties as described in
+     * <a href="http://download.oracle.com/javase/1,5.0/docs/guide/security/jsse/JSSERefGuide.html">
+     * "JavaTM Secure Socket Extension (JSSE) Reference Guide for the JavaTM 2 Platform
+     * Standard Edition 5</a>
+     * <p>
+     * The following system properties are taken into account by this method:
+     * <ul>
+     *  <li>ssl.TrustManagerFactory.algorithm</li>
+     *  <li>javax.net.ssl.trustStoreType</li>
+     *  <li>javax.net.ssl.trustStore</li>
+     *  <li>javax.net.ssl.trustStoreProvider</li>
+     *  <li>javax.net.ssl.trustStorePassword</li>
+     *  <li>java.home</li>
+     *  <li>ssl.KeyManagerFactory.algorithm</li>
+     *  <li>javax.net.ssl.keyStoreType</li>
+     *  <li>javax.net.ssl.keyStore</li>
+     *  <li>javax.net.ssl.keyStoreProvider</li>
+     *  <li>javax.net.ssl.keyStorePassword</li>
+     * </ul>
+     * <p>
+     *
+     * @since 4.2
+     */
+    public static SchemeRegistry createSystemDefault() {
+        SchemeRegistry registry = new SchemeRegistry();
+        registry.register(
+                new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
+        registry.register(
+                new Scheme("https", 443, SSLSocketFactory.getSystemSocketFactory()));
+        return registry;
+    }
 }
 
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java b/httpclient/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java
index b194381..1140640 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/SingleClientConnManager.java
@@ -57,8 +57,11 @@ import org.apache.http.params.HttpParams;
  * already been allocated {@link IllegalStateException} is thrown.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link BasicClientConnectionManager}
  */
 @ThreadSafe
+ at Deprecated 
 public class SingleClientConnManager implements ClientConnectionManager {
 
     private final Log log = LogFactory.getLog(getClass());
@@ -79,19 +82,19 @@ public class SingleClientConnManager implements ClientConnectionManager {
 
     /** The one and only entry in this pool. */
     @GuardedBy("this")
-    protected PoolEntry uniquePoolEntry;
+    protected volatile PoolEntry uniquePoolEntry;
 
     /** The currently issued managed connection, if any. */
     @GuardedBy("this")
-    protected ConnAdapter managedConn;
+    protected volatile ConnAdapter managedConn;
 
     /** The time of the last connection release, or -1. */
     @GuardedBy("this")
-    protected long lastReleaseTime;
+    protected volatile long lastReleaseTime;
 
     /** The time the last released connection expires and shouldn't be reused. */
     @GuardedBy("this")
-    protected long connectionExpiresTime;
+    protected volatile long connectionExpiresTime;
 
     /** Indicates whether this connection manager is shut down. */
     protected volatile boolean isShutDown;
@@ -102,9 +105,8 @@ public class SingleClientConnManager implements ClientConnectionManager {
      * @param params    the parameters for this manager
      * @param schreg    the scheme registry
      *
-     * @deprecated use {@link SingleClientConnManager#SingleClientConnManager(SchemeRegistry)}
+     * @deprecated (4.1)  use {@link SingleClientConnManager#SingleClientConnManager(SchemeRegistry)}
      */
-    @Deprecated
     public SingleClientConnManager(HttpParams params,
                                    SchemeRegistry schreg) {
         this(schreg);
@@ -202,7 +204,7 @@ public class SingleClientConnManager implements ClientConnectionManager {
      * @return  a connection that can be used to communicate
      *          along the given route
      */
-    public synchronized ManagedClientConnection getConnection(HttpRoute route, Object state) {
+    public ManagedClientConnection getConnection(HttpRoute route, Object state) {
         if (route == null) {
             throw new IllegalArgumentException("Route may not be null.");
         }
@@ -212,47 +214,49 @@ public class SingleClientConnManager implements ClientConnectionManager {
             log.debug("Get connection for route " + route);
         }
 
-        if (managedConn != null)
-            throw new IllegalStateException(MISUSE_MESSAGE);
-
-        // check re-usability of the connection
-        boolean recreate = false;
-        boolean shutdown = false;
-
-        // Kill the connection if it expired.
-        closeExpiredConnections();
-
-        if (uniquePoolEntry.connection.isOpen()) {
-            RouteTracker tracker = uniquePoolEntry.tracker;
-            shutdown = (tracker == null || // can happen if method is aborted
-                        !tracker.toRoute().equals(route));
-        } else {
-            // If the connection is not open, create a new PoolEntry,
-            // as the connection may have been marked not reusable,
-            // due to aborts -- and the PoolEntry should not be reused
-            // either.  There's no harm in recreating an entry if
-            // the connection is closed.
-            recreate = true;
-        }
+        synchronized (this) {
+            if (managedConn != null)
+                throw new IllegalStateException(MISUSE_MESSAGE);
+
+            // check re-usability of the connection
+            boolean recreate = false;
+            boolean shutdown = false;
+
+            // Kill the connection if it expired.
+            closeExpiredConnections();
+
+            if (uniquePoolEntry.connection.isOpen()) {
+                RouteTracker tracker = uniquePoolEntry.tracker;
+                shutdown = (tracker == null || // can happen if method is aborted
+                            !tracker.toRoute().equals(route));
+            } else {
+                // If the connection is not open, create a new PoolEntry,
+                // as the connection may have been marked not reusable,
+                // due to aborts -- and the PoolEntry should not be reused
+                // either.  There's no harm in recreating an entry if
+                // the connection is closed.
+                recreate = true;
+            }
 
-        if (shutdown) {
-            recreate = true;
-            try {
-                uniquePoolEntry.shutdown();
-            } catch (IOException iox) {
-                log.debug("Problem shutting down connection.", iox);
+            if (shutdown) {
+                recreate = true;
+                try {
+                    uniquePoolEntry.shutdown();
+                } catch (IOException iox) {
+                    log.debug("Problem shutting down connection.", iox);
+                }
             }
-        }
 
-        if (recreate)
-            uniquePoolEntry = new PoolEntry();
+            if (recreate)
+                uniquePoolEntry = new PoolEntry();
 
-        managedConn = new ConnAdapter(uniquePoolEntry, route);
+            managedConn = new ConnAdapter(uniquePoolEntry, route);
 
-        return managedConn;
+            return managedConn;
+        }
     }
 
-    public synchronized void releaseConnection(
+    public void releaseConnection(
             ManagedClientConnection conn,
             long validDuration, TimeUnit timeUnit) {
         assertStillUp();
@@ -268,51 +272,55 @@ public class SingleClientConnManager implements ClientConnectionManager {
         }
 
         ConnAdapter sca = (ConnAdapter) conn;
-        if (sca.poolEntry == null)
-            return; // already released
-        ClientConnectionManager manager = sca.getManager();
-        if (manager != null && manager != this) {
-            throw new IllegalArgumentException
-                ("Connection not obtained from this manager.");
-        }
-
-        try {
-            // make sure that the response has been read completely
-            if (sca.isOpen() && (this.alwaysShutDown ||
-                                 !sca.isMarkedReusable())
-                ) {
-                if (log.isDebugEnabled()) {
-                    log.debug
-                        ("Released connection open but not reusable.");
+        synchronized (sca) {
+            if (sca.poolEntry == null)
+                return; // already released
+            ClientConnectionManager manager = sca.getManager();
+            if (manager != null && manager != this) {
+                throw new IllegalArgumentException
+                    ("Connection not obtained from this manager.");
+            }
+            try {
+                // make sure that the response has been read completely
+                if (sca.isOpen() && (this.alwaysShutDown ||
+                                     !sca.isMarkedReusable())
+                    ) {
+                    if (log.isDebugEnabled()) {
+                        log.debug
+                            ("Released connection open but not reusable.");
+                    }
+
+                    // make sure this connection will not be re-used
+                    // we might have gotten here because of a shutdown trigger
+                    // shutdown of the adapter also clears the tracked route
+                    sca.shutdown();
+                }
+            } catch (IOException iox) {
+                if (log.isDebugEnabled())
+                    log.debug("Exception shutting down released connection.",
+                              iox);
+            } finally {
+                sca.detach();
+                synchronized (this) {
+                    managedConn = null;
+                    lastReleaseTime = System.currentTimeMillis();
+                    if(validDuration > 0)
+                        connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
+                    else
+                        connectionExpiresTime = Long.MAX_VALUE;
                 }
-
-                // make sure this connection will not be re-used
-                // we might have gotten here because of a shutdown trigger
-                // shutdown of the adapter also clears the tracked route
-                sca.shutdown();
             }
-        } catch (IOException iox) {
-            if (log.isDebugEnabled())
-                log.debug("Exception shutting down released connection.",
-                          iox);
-        } finally {
-            sca.detach();
-            managedConn = null;
-            lastReleaseTime = System.currentTimeMillis();
-            if(validDuration > 0)
-                connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
-            else
-                connectionExpiresTime = Long.MAX_VALUE;
         }
     }
 
-    public synchronized void closeExpiredConnections() {
-        if(System.currentTimeMillis() >= connectionExpiresTime) {
+    public void closeExpiredConnections() {
+        long time = connectionExpiresTime;
+        if (System.currentTimeMillis() >= time) {
             closeIdleConnections(0, TimeUnit.MILLISECONDS);
         }
     }
 
-    public synchronized void closeIdleConnections(long idletime, TimeUnit tunit) {
+    public void closeIdleConnections(long idletime, TimeUnit tunit) {
         assertStillUp();
 
         // idletime can be 0 or negative, no problem there
@@ -320,51 +328,51 @@ public class SingleClientConnManager implements ClientConnectionManager {
             throw new IllegalArgumentException("Time unit must not be null.");
         }
 
-        if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
-            final long cutoff =
-                System.currentTimeMillis() - tunit.toMillis(idletime);
-            if (lastReleaseTime <= cutoff) {
-                try {
-                    uniquePoolEntry.close();
-                } catch (IOException iox) {
-                    // ignore
-                    log.debug("Problem closing idle connection.", iox);
+        synchronized (this) {
+            if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
+                final long cutoff =
+                    System.currentTimeMillis() - tunit.toMillis(idletime);
+                if (lastReleaseTime <= cutoff) {
+                    try {
+                        uniquePoolEntry.close();
+                    } catch (IOException iox) {
+                        // ignore
+                        log.debug("Problem closing idle connection.", iox);
+                    }
                 }
             }
         }
     }
 
-    public synchronized void shutdown() {
-
+    public void shutdown() {
         this.isShutDown = true;
-
-        if (managedConn != null)
-            managedConn.detach();
-
-        try {
-            if (uniquePoolEntry != null) // and connection open?
-                uniquePoolEntry.shutdown();
-        } catch (IOException iox) {
-            // ignore
-            log.debug("Problem while shutting down manager.", iox);
-        } finally {
-            uniquePoolEntry = null;
+        synchronized (this) {
+            try {
+                if (uniquePoolEntry != null) // and connection open?
+                    uniquePoolEntry.shutdown();
+            } catch (IOException iox) {
+                // ignore
+                log.debug("Problem while shutting down manager.", iox);
+            } finally {
+                uniquePoolEntry = null;
+                managedConn = null;
+            }
         }
     }
 
-    /**
-     * @deprecated no longer used
-     */
-    @Deprecated
-    protected synchronized void revokeConnection() {
-        if (managedConn == null)
+    protected void revokeConnection() {
+        ConnAdapter conn = managedConn;
+        if (conn == null)
             return;
-        managedConn.detach();
-        try {
-            uniquePoolEntry.shutdown();
-        } catch (IOException iox) {
-            // ignore
-            log.debug("Problem while shutting down connection.", iox);
+        conn.detach();
+
+        synchronized (this) {
+            try {
+                uniquePoolEntry.shutdown();
+            } catch (IOException iox) {
+                // ignore
+                log.debug("Problem while shutting down connection.", iox);
+            }
         }
     }
 
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/SystemDefaultDnsResolver.java b/httpclient/src/main/java/org/apache/http/impl/conn/SystemDefaultDnsResolver.java
new file mode 100644
index 0000000..1196d95
--- /dev/null
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/SystemDefaultDnsResolver.java
@@ -0,0 +1,48 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.conn;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.apache.http.conn.DnsResolver;
+
+/**
+ * DNS resolver that uses the default OS implementation for resolving host names.
+ *
+ * @since 4.2
+ */
+public class SystemDefaultDnsResolver implements DnsResolver {
+
+    /**
+     * {@inheritDoc}
+     */
+    public InetAddress[] resolve(String host) throws UnknownHostException {
+        return InetAddress.getAllByName(host);
+    }
+
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/Wire.java b/httpclient/src/main/java/org/apache/http/impl/conn/Wire.java
index bc0b9e2..11cb492 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/Wire.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/Wire.java
@@ -141,7 +141,10 @@ public class Wire {
         input(new byte[] {(byte) b});
     }
 
-    @Deprecated
+    /**
+     * @deprecated (4.1)  do not use
+     */
+    @Deprecated 
     public void output(final String s)
       throws IOException {
         if (s == null) {
@@ -150,7 +153,10 @@ public class Wire {
         output(s.getBytes());
     }
 
-    @Deprecated
+    /**
+     * @deprecated (4.1)  do not use
+     */
+    @Deprecated 
     public void input(final String s)
       throws IOException {
         if (s == null) {
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java
index a687bdf..1892d38 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/AbstractConnPool.java
@@ -53,10 +53,11 @@ import org.apache.http.impl.conn.IdleConnectionHandler;
  * Don't use <code>synchronized</code> for that purpose!
  *
  * @since 4.0
+ *
+ * @deprecated (4.2) use {@link org.apache.http.pool.AbstractConnPool}
  */
-
 @Deprecated
-public abstract class AbstractConnPool implements RefQueueHandler {
+public abstract class AbstractConnPool {
 
     private final Log log;
 
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java
index 5aeb539..ea270da 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntry.java
@@ -29,7 +29,6 @@ package org.apache.http.impl.conn.tsccm;
 import java.lang.ref.ReferenceQueue;
 import java.util.concurrent.TimeUnit;
 
-import org.apache.http.annotation.NotThreadSafe;
 import org.apache.http.conn.OperatedClientConnection;
 import org.apache.http.conn.ClientConnectionOperator;
 import org.apache.http.conn.routing.HttpRoute;
@@ -39,8 +38,10 @@ import org.apache.http.impl.conn.AbstractPoolEntry;
  * Basic implementation of a connection pool entry.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link org.apache.http.pool.PoolEntry}
  */
- at NotThreadSafe
+ at Deprecated
 public class BasicPoolEntry extends AbstractPoolEntry {
 
     private final long created;
@@ -49,10 +50,6 @@ public class BasicPoolEntry extends AbstractPoolEntry {
     private long validUntil;
     private long expiry;
 
-    /**
-     * @deprecated do not use
-     */
-    @Deprecated
     public BasicPoolEntry(ClientConnectionOperator op,
                           HttpRoute route,
                           ReferenceQueue<Object> queue) {
@@ -109,7 +106,6 @@ public class BasicPoolEntry extends AbstractPoolEntry {
         return super.route;
     }
 
-    @Deprecated
     protected final BasicPoolEntryRef getWeakRef() {
         return null;
     }
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java
index b565876..ce23ee6 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPoolEntryRef.java
@@ -26,24 +26,21 @@
 
 package org.apache.http.impl.conn.tsccm;
 
-
 import java.lang.ref.WeakReference;
 import java.lang.ref.ReferenceQueue;
 
-import org.apache.http.annotation.Immutable;
-
 import org.apache.http.conn.routing.HttpRoute;
 
-
-
 /**
  * A weak reference to a {@link BasicPoolEntry BasicPoolEntry}.
  * This reference explicitly keeps the planned route, so the connection
  * can be reclaimed if it is lost to garbage collection.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  do not use
  */
- at Immutable
+ at Deprecated 
 public class BasicPoolEntryRef extends WeakReference<BasicPoolEntry> {
 
     /** The planned route of the entry. */
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java
index 607fde7..5f20a96 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/BasicPooledConnAdapter.java
@@ -36,7 +36,10 @@ import org.apache.http.impl.conn.AbstractPooledConnAdapter;
  * can be {@link #detach detach}ed to prevent further use on release.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  do not use
  */
+ at Deprecated 
 public class BasicPooledConnAdapter extends AbstractPooledConnAdapter {
 
     /**
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
index b0a5394..864c6d6 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ConnPoolByRoute.java
@@ -40,7 +40,6 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.http.annotation.ThreadSafe;
 import org.apache.http.conn.routing.HttpRoute;
 import org.apache.http.conn.ClientConnectionOperator;
 import org.apache.http.conn.ConnectionPoolTimeoutException;
@@ -63,10 +62,11 @@ import org.apache.http.params.HttpParams;
  * not via <code>synchronized</code> methods.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link org.apache.http.pool.AbstractConnPool}
  */
- at ThreadSafe
- at SuppressWarnings("deprecation")
-public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependency on AbstractConnPool
+ at Deprecated 
+public class ConnPoolByRoute extends AbstractConnPool {
 
     private final Log log = LogFactory.getLog(getClass());
 
@@ -147,9 +147,8 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
     /**
      * Creates a new connection pool, managed by route.
      *
-     * @deprecated use {@link ConnPoolByRoute#ConnPoolByRoute(ClientConnectionOperator, ConnPerRoute, int)}
+     * @deprecated (4.1)  use {@link ConnPoolByRoute#ConnPoolByRoute(ClientConnectionOperator, ConnPerRoute, int)}
      */
-    @Deprecated
     public ConnPoolByRoute(final ClientConnectionOperator operator, final HttpParams params) {
         this(operator,
                 ConnManagerParams.getMaxConnectionsPerRoute(params),
@@ -449,7 +448,7 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
 
             RouteSpecificPool rospl = getRoutePool(route, true);
 
-            if (reusable) {
+            if (reusable && rospl.getCapacity() >= 0) {
                 if (log.isDebugEnabled()) {
                     String s;
                     if (validDuration > 0) {
@@ -464,6 +463,7 @@ public class ConnPoolByRoute extends AbstractConnPool { //TODO: remove dependenc
                 entry.updateExpiry(validDuration, timeUnit);
                 freeConnections.add(entry);
             } else {
+                closeConnection(entry);
                 rospl.dropEntry();
                 numConnections--;
             }
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
index c96e245..a27dae7 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/PoolEntryRequest.java
@@ -26,6 +26,7 @@
 
 package org.apache.http.impl.conn.tsccm;
 
+import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.http.conn.ConnectionPoolTimeoutException;
@@ -34,7 +35,10 @@ import org.apache.http.conn.ConnectionPoolTimeoutException;
  * Encapsulates a request for a {@link BasicPoolEntry}.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link Future}
  */
+ at Deprecated 
 public interface PoolEntryRequest {
 
     /**
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/RefQueueHandler.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/RefQueueHandler.java
deleted file mode 100644
index f4666bc..0000000
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/RefQueueHandler.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * ====================================================================
- *
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.impl.conn.tsccm;
-
-import java.lang.ref.Reference;
-
-/**
- * @deprecated do not use
- *
- * @since 4.0
- */
- at Deprecated
-public interface RefQueueHandler {
-
-    /**
-     * Invoked when a reference is found on the queue.
-     *
-     * @param ref       the reference to handle
-     */
-    public void handleReference(Reference<?> ref)
-        ;
-}
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/RefQueueWorker.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/RefQueueWorker.java
deleted file mode 100644
index 1d65c06..0000000
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/RefQueueWorker.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * ====================================================================
- *
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.impl.conn.tsccm;
-
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-
-/**
- * A worker thread for processing queued references.
- * {@link Reference Reference}s can be
- * {@link ReferenceQueue queued}
- * automatically by the garbage collector.
- * If that feature is used, a daemon thread should be executing
- * this worker. It will pick up the queued references and pass them
- * on to a handler for appropriate processing.
- *
- * @deprecated do not use
- */
- at Deprecated
-public class RefQueueWorker implements Runnable {
-
-    /** The reference queue to monitor. */
-    protected final ReferenceQueue<?> refQueue;
-
-    /** The handler for the references found. */
-    protected final RefQueueHandler refHandler;
-
-
-    /**
-     * The thread executing this handler.
-     * This attribute is also used as a shutdown indicator.
-     */
-    protected volatile Thread workerThread;
-
-
-    /**
-     * Instantiates a new worker to listen for lost connections.
-     *
-     * @param queue     the queue on which to wait for references
-     * @param handler   the handler to pass the references to
-     */
-    public RefQueueWorker(ReferenceQueue<?> queue, RefQueueHandler handler) {
-        if (queue == null) {
-            throw new IllegalArgumentException("Queue must not be null.");
-        }
-        if (handler == null) {
-            throw new IllegalArgumentException("Handler must not be null.");
-        }
-
-        refQueue   = queue;
-        refHandler = handler;
-    }
-
-
-    /**
-     * The main loop of this worker.
-     * If initialization succeeds, this method will only return
-     * after {@link #shutdown shutdown()}. Only one thread can
-     * execute the main loop at any time.
-     */
-    public void run() {
-
-        if (this.workerThread == null) {
-            this.workerThread = Thread.currentThread();
-        }
-
-        while (this.workerThread == Thread.currentThread()) {
-            try {
-                // remove the next reference and process it
-                Reference<?> ref = refQueue.remove();
-                refHandler.handleReference(ref);
-            } catch (InterruptedException ignore) {
-            }
-        }
-    }
-
-
-    /**
-     * Shuts down this worker.
-     * It can be re-started afterwards by another call to {@link #run run()}.
-     */
-    public void shutdown() {
-        Thread wt = this.workerThread;
-        if (wt != null) {
-            this.workerThread = null; // indicate shutdown
-            wt.interrupt();
-        }
-    }
-
-
-    /**
-     * Obtains a description of this worker.
-     *
-     * @return  a descriptive string for this worker
-     */
-    @Override
-    public String toString() {
-        return "RefQueueWorker::" + this.workerThread;
-    }
-
-} // class RefQueueWorker
-
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java
index be67f03..3283ad1 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/RouteSpecificPool.java
@@ -31,8 +31,6 @@ import java.util.ListIterator;
 import java.util.Queue;
 import java.util.LinkedList;
 
-import org.apache.http.annotation.NotThreadSafe;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.http.conn.OperatedClientConnection;
@@ -47,8 +45,10 @@ import org.apache.http.util.LangUtils;
  * containing pool takes care of synchronization.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link org.apache.http.pool.AbstractConnPool}
  */
- at NotThreadSafe // e.g. numEntries, freeEntries,
+ at Deprecated
 public class RouteSpecificPool {
 
     private final Log log = LogFactory.getLog(getClass());
@@ -56,7 +56,6 @@ public class RouteSpecificPool {
     /** The route this pool is for. */
     protected final HttpRoute route; //Immutable
 
-    @Deprecated
     protected final int maxEntries;
 
     /** Connections per route */
@@ -75,11 +74,9 @@ public class RouteSpecificPool {
     /** The number of created entries. */
     protected int numEntries;
 
-
     /**
-     * @deprecated use {@link RouteSpecificPool#RouteSpecificPool(HttpRoute, ConnPerRoute)}
+     * @deprecated (4.1)  use {@link RouteSpecificPool#RouteSpecificPool(HttpRoute, ConnPerRoute)}
      */
-    @Deprecated
     public RouteSpecificPool(HttpRoute route, int maxEntries) {
         this.route = route;
         this.maxEntries = maxEntries;
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
index f884654..022794a 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.java
@@ -43,6 +43,7 @@ import org.apache.http.conn.ManagedClientConnection;
 import org.apache.http.conn.OperatedClientConnection;
 import org.apache.http.params.HttpParams;
 import org.apache.http.impl.conn.DefaultClientConnectionOperator;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
 import org.apache.http.impl.conn.SchemeRegistryFactory;
 
 /**
@@ -62,8 +63,11 @@ import org.apache.http.impl.conn.SchemeRegistryFactory;
  * can be adjusted using HTTP parameters.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  use {@link PoolingClientConnectionManager}
  */
 @ThreadSafe
+ at Deprecated 
 public class ThreadSafeClientConnManager implements ClientConnectionManager {
 
     private final Log log;
@@ -71,7 +75,6 @@ public class ThreadSafeClientConnManager implements ClientConnectionManager {
     /** The schemes supported by this connection manager. */
     protected final SchemeRegistry schemeRegistry; // @ThreadSafe
 
-    @Deprecated
     protected final AbstractConnPool connectionPool;
 
     /** The pool of connections being managed. */
@@ -109,13 +112,30 @@ public class ThreadSafeClientConnManager implements ClientConnectionManager {
      */
     public ThreadSafeClientConnManager(final SchemeRegistry schreg,
             long connTTL, TimeUnit connTTLTimeUnit) {
+        this(schreg, connTTL, connTTLTimeUnit, new ConnPerRouteBean());
+    }
+
+    /**
+     * Creates a new thread safe connection manager.
+     *
+     * @param schreg    the scheme registry.
+     * @param connTTL   max connection lifetime, <=0 implies "infinity"
+     * @param connTTLTimeUnit   TimeUnit of connTTL
+     * @param connPerRoute    mapping of maximum connections per route,
+     *   provided as a dependency so it can be managed externally, e.g.
+     *   for dynamic connection pool size management.
+     *
+     * @since 4.2
+     */
+    public ThreadSafeClientConnManager(final SchemeRegistry schreg,
+            long connTTL, TimeUnit connTTLTimeUnit, ConnPerRouteBean connPerRoute) {
         super();
         if (schreg == null) {
             throw new IllegalArgumentException("Scheme registry may not be null");
         }
         this.log = LogFactory.getLog(getClass());
         this.schemeRegistry = schreg;
-        this.connPerRoute = new ConnPerRouteBean();
+        this.connPerRoute = connPerRoute;
         this.connOperator = createConnectionOperator(schreg);
         this.pool = createConnectionPool(connTTL, connTTLTimeUnit) ;
         this.connectionPool = this.pool;
@@ -127,9 +147,8 @@ public class ThreadSafeClientConnManager implements ClientConnectionManager {
      * @param params    the parameters for this manager.
      * @param schreg    the scheme registry.
      *
-     * @deprecated use {@link ThreadSafeClientConnManager#ThreadSafeClientConnManager(SchemeRegistry)}
+     * @deprecated (4.1)  use {@link ThreadSafeClientConnManager#ThreadSafeClientConnManager(SchemeRegistry)}
      */
-    @Deprecated
     public ThreadSafeClientConnManager(HttpParams params,
                                        SchemeRegistry schreg) {
         if (schreg == null) {
@@ -157,9 +176,8 @@ public class ThreadSafeClientConnManager implements ClientConnectionManager {
      *
      * @return  the connection pool to use
      *
-     * @deprecated use #createConnectionPool(long, TimeUnit))
+     * @deprecated (4.1)  use #createConnectionPool(long, TimeUnit))
      */
-    @Deprecated
     protected AbstractConnPool createConnectionPool(final HttpParams params) {
         return new ConnPoolByRoute(connOperator, params);
     }
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java
index bab3e2c..47bafd6 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThread.java
@@ -30,8 +30,6 @@ package org.apache.http.impl.conn.tsccm;
 import java.util.Date;
 import java.util.concurrent.locks.Condition;
 
-import org.apache.http.annotation.NotThreadSafe;
-
 /**
  * Represents a thread waiting for a connection.
  * This class implements throwaway objects. It is instantiated whenever
@@ -44,8 +42,10 @@ import org.apache.http.annotation.NotThreadSafe;
  *
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  do not use
  */
- at NotThreadSafe
+ at Deprecated 
 public class WaitingThread {
 
     /** The condition on which the thread is waiting. */
diff --git a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java
index f0aee20..ce06894 100644
--- a/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java
+++ b/httpclient/src/main/java/org/apache/http/impl/conn/tsccm/WaitingThreadAborter.java
@@ -26,17 +26,16 @@
 
 package org.apache.http.impl.conn.tsccm;
 
-import org.apache.http.annotation.NotThreadSafe;
-
-// TODO - only called from ConnPoolByRoute currently; consider adding it as nested class
 /**
  * A simple class that can interrupt a {@link WaitingThread}.
  *
  * Must be called with the pool lock held.
  *
  * @since 4.0
+ *
+ * @deprecated (4.2)  do not use
  */
- at NotThreadSafe
+ at Deprecated 
 public class WaitingThreadAborter {
 
     private WaitingThread waitingThread;
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/BestMatchSpec.java b/httpclient/src/main/java/org/apache/http/impl/cookie/BestMatchSpec.java
index ce6211e..1e9704b 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/BestMatchSpec.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/BestMatchSpec.java
@@ -182,7 +182,7 @@ public class BestMatchSpec implements CookieSpec {
 
     public List<Header> formatCookies(final List<Cookie> cookies) {
         if (cookies == null) {
-            throw new IllegalArgumentException("List of cookie may not be null");
+            throw new IllegalArgumentException("List of cookies may not be null");
         }
         int version = Integer.MAX_VALUE;
         boolean isSetCookie2 = true;
@@ -218,4 +218,4 @@ public class BestMatchSpec implements CookieSpec {
         return "best-match";
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/BrowserCompatSpec.java b/httpclient/src/main/java/org/apache/http/impl/cookie/BrowserCompatSpec.java
index 018619f..81678fc 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/BrowserCompatSpec.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/BrowserCompatSpec.java
@@ -55,24 +55,6 @@ import org.apache.http.util.CharArrayBuffer;
 @NotThreadSafe // superclass is @NotThreadSafe
 public class BrowserCompatSpec extends CookieSpecBase {
 
-    @Deprecated
-    protected static final String[] DATE_PATTERNS = new String[] {
-            DateUtils.PATTERN_RFC1123,
-            DateUtils.PATTERN_RFC1036,
-            DateUtils.PATTERN_ASCTIME,
-            "EEE, dd-MMM-yyyy HH:mm:ss z",
-            "EEE, dd-MMM-yyyy HH-mm-ss z",
-            "EEE, dd MMM yy HH:mm:ss z",
-            "EEE dd-MMM-yyyy HH:mm:ss z",
-            "EEE dd MMM yyyy HH:mm:ss z",
-            "EEE dd-MMM-yyyy HH-mm-ss z",
-            "EEE dd-MMM-yy HH:mm:ss z",
-            "EEE dd MMM yy HH:mm:ss z",
-            "EEE,dd-MMM-yy HH:mm:ss z",
-            "EEE,dd-MMM-yyyy HH:mm:ss z",
-            "EEE, dd-MM-yyyy HH:mm:ss z",
-        };
-
     private static final String[] DEFAULT_DATE_PATTERNS = new String[] {
         DateUtils.PATTERN_RFC1123,
         DateUtils.PATTERN_RFC1036,
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/IgnoreSpecFactory.java b/httpclient/src/main/java/org/apache/http/impl/cookie/IgnoreSpecFactory.java
index b1adb55..254861d 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/IgnoreSpecFactory.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/IgnoreSpecFactory.java
@@ -34,7 +34,7 @@ import org.apache.http.params.HttpParams;
 
 /**
  * {@link CookieSpecFactory} implementation that ignores all cookies.
- * 
+ *
  * @since 4.1
  */
 @Immutable
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java
index 90919cd..f8fc034 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965CommentUrlAttributeHandler.java
@@ -64,4 +64,4 @@ public class RFC2965CommentUrlAttributeHandler implements CookieAttributeHandler
           return true;
       }
 
-  }
\ No newline at end of file
+  }
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java
index 67b9237..01a3a35 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965DiscardAttributeHandler.java
@@ -64,4 +64,4 @@ public class RFC2965DiscardAttributeHandler implements CookieAttributeHandler {
           return true;
       }
 
-  }
\ No newline at end of file
+  }
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java
index 2789e64..076745b 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965DomainAttributeHandler.java
@@ -191,4 +191,4 @@ public class RFC2965DomainAttributeHandler implements CookieAttributeHandler {
         return effectiveHostWithoutDomain.indexOf('.') == -1;
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java
index 5f73b4f..6056330 100644
--- a/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java
+++ b/httpclient/src/main/java/org/apache/http/impl/cookie/RFC2965VersionAttributeHandler.java
@@ -95,4 +95,4 @@ public class RFC2965VersionAttributeHandler implements CookieAttributeHandler {
         return true;
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient/src/site/site.xml b/httpclient/src/site/site.xml
index 463453f..02ea590 100644
--- a/httpclient/src/site/site.xml
+++ b/httpclient/src/site/site.xml
@@ -30,7 +30,7 @@
   <body>
     <menu name="HttpClient Overview">
       <item name="Description" href="../index.html"/>
-      <item name="Examples" href="../examples.html"/>
+      <item name="Quick Start" href="../quickstart.html"/>
     </menu>
     <menu ref="modules" />
     <menu ref="reports"/>      
diff --git a/httpclient/src/test/java/org/apache/http/client/entity/TestDecompressingEntity.java b/httpclient/src/test/java/org/apache/http/client/entity/TestDecompressingEntity.java
new file mode 100644
index 0000000..e3bf408
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/client/entity/TestDecompressingEntity.java
@@ -0,0 +1,109 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.entity;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.CRC32;
+import java.util.zip.CheckedInputStream;
+import java.util.zip.Checksum;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.util.EntityUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestDecompressingEntity {
+
+    @Test
+    public void testNonStreaming() throws Exception {
+        CRC32 crc32 = new CRC32();
+        StringEntity wrapped = new StringEntity("1234567890", "ASCII");
+        ChecksumEntity entity = new ChecksumEntity(wrapped, crc32);
+        Assert.assertFalse(entity.isStreaming());
+        String s = EntityUtils.toString(entity);
+        Assert.assertEquals("1234567890", s);
+        Assert.assertEquals(639479525L, crc32.getValue());
+        InputStream in1 = entity.getContent();
+        InputStream in2 = entity.getContent();
+        Assert.assertTrue(in1 != in2);
+    }
+
+    @Test
+    public void testStreaming() throws Exception {
+        CRC32 crc32 = new CRC32();
+        ByteArrayInputStream in = new ByteArrayInputStream("1234567890".getBytes("ASCII"));
+        InputStreamEntity wrapped = new InputStreamEntity(in, -1);
+        ChecksumEntity entity = new ChecksumEntity(wrapped, crc32);
+        Assert.assertTrue(entity.isStreaming());
+        String s = EntityUtils.toString(entity);
+        Assert.assertEquals("1234567890", s);
+        Assert.assertEquals(639479525L, crc32.getValue());
+        InputStream in1 = entity.getContent();
+        InputStream in2 = entity.getContent();
+        Assert.assertTrue(in1 == in2);
+        EntityUtils.consume(entity);
+        EntityUtils.consume(entity);
+    }
+
+    @Test
+    public void testWriteToStream() throws Exception {
+        CRC32 crc32 = new CRC32();
+        StringEntity wrapped = new StringEntity("1234567890", "ASCII");
+        ChecksumEntity entity = new ChecksumEntity(wrapped, crc32);
+        Assert.assertFalse(entity.isStreaming());
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        entity.writeTo(out);
+
+        String s = new String(out.toByteArray(), "ASCII");
+        Assert.assertEquals("1234567890", s);
+        Assert.assertEquals(639479525L, crc32.getValue());
+    }
+
+    static class ChecksumEntity extends DecompressingEntity {
+
+        private final Checksum checksum;
+
+        public ChecksumEntity(final HttpEntity wrapped, final Checksum checksum) {
+            super(wrapped);
+            this.checksum = checksum;
+        }
+
+        @Override
+        InputStream getDecompressingInputStream(final InputStream wrapped) throws IOException {
+            return new CheckedInputStream(wrapped, this.checksum);
+        }
+
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestCookie2Support.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestCookie2Support.java
index 573345a..704426d 100644
--- a/httpclient/src/test/java/org/apache/http/client/protocol/TestCookie2Support.java
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestCookie2Support.java
@@ -64,9 +64,10 @@ public class TestCookie2Support extends BasicServerTestBase {
 
     @Before
     public void setUp() throws Exception {
-        localServer = new LocalTestServer(null, null);
-        localServer.registerDefaultHandlers();
-        localServer.start();
+        this.localServer = new LocalTestServer(null, null);
+        this.localServer.registerDefaultHandlers();
+        this.localServer.start();
+        this.httpclient = new DefaultHttpClient();
     }
 
     private static class CookieVer0Service implements HttpRequestHandler {
@@ -88,8 +89,7 @@ public class TestCookie2Support extends BasicServerTestBase {
     public void testCookieVersionSupportHeader1() throws Exception {
         this.localServer.register("*", new CookieVer0Service());
 
-        DefaultHttpClient client = new DefaultHttpClient();
-        client.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);
+        this.httpclient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);
 
         CookieStore cookieStore = new BasicCookieStore();
         HttpContext context = new BasicHttpContext();
@@ -97,7 +97,7 @@ public class TestCookie2Support extends BasicServerTestBase {
 
         HttpGet httpget = new HttpGet("/test/");
 
-        HttpResponse response1 = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response1 = this.httpclient.execute(getServerHttp(), httpget, context);
         HttpEntity e1 = response1.getEntity();
         EntityUtils.consume(e1);
 
@@ -105,7 +105,7 @@ public class TestCookie2Support extends BasicServerTestBase {
         Assert.assertNotNull(cookies);
         Assert.assertEquals(1, cookies.size());
 
-        HttpResponse response2 = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response2 = this.httpclient.execute(getServerHttp(), httpget, context);
         HttpEntity e2 = response2.getEntity();
         EntityUtils.consume(e2);
 
@@ -136,8 +136,7 @@ public class TestCookie2Support extends BasicServerTestBase {
     public void testCookieVersionSupportHeader2() throws Exception {
         this.localServer.register("*", new CookieVer1Service());
 
-        DefaultHttpClient client = new DefaultHttpClient();
-        client.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);
+        this.httpclient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);
 
         CookieStore cookieStore = new BasicCookieStore();
         HttpContext context = new BasicHttpContext();
@@ -145,7 +144,7 @@ public class TestCookie2Support extends BasicServerTestBase {
 
         HttpGet httpget = new HttpGet("/test/");
 
-        HttpResponse response1 = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response1 = this.httpclient.execute(getServerHttp(), httpget, context);
         HttpEntity e1 = response1.getEntity();
         EntityUtils.consume(e1);
 
@@ -153,7 +152,7 @@ public class TestCookie2Support extends BasicServerTestBase {
         Assert.assertNotNull(cookies);
         Assert.assertEquals(2, cookies.size());
 
-        HttpResponse response2 = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response2 = this.httpclient.execute(getServerHttp(), httpget, context);
         HttpEntity e2 = response2.getEntity();
         EntityUtils.consume(e2);
 
@@ -183,8 +182,7 @@ public class TestCookie2Support extends BasicServerTestBase {
     public void testCookieVersionSupportHeader3() throws Exception {
         this.localServer.register("*", new CookieVer2Service());
 
-        DefaultHttpClient client = new DefaultHttpClient();
-        client.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);
+        this.httpclient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);
 
         CookieStore cookieStore = new BasicCookieStore();
         HttpContext context = new BasicHttpContext();
@@ -192,7 +190,7 @@ public class TestCookie2Support extends BasicServerTestBase {
 
         HttpGet httpget = new HttpGet("/test/");
 
-        HttpResponse response1 = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response1 = this.httpclient.execute(getServerHttp(), httpget, context);
         HttpEntity e1 = response1.getEntity();
         EntityUtils.consume(e1);
 
@@ -200,7 +198,7 @@ public class TestCookie2Support extends BasicServerTestBase {
         Assert.assertNotNull(cookies);
         Assert.assertEquals(1, cookies.size());
 
-        HttpResponse response2 = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response2 = this.httpclient.execute(getServerHttp(), httpget, context);
         HttpEntity e2 = response2.getEntity();
         EntityUtils.consume(e2);
 
@@ -231,8 +229,7 @@ public class TestCookie2Support extends BasicServerTestBase {
     public void testSetCookieVersionMix() throws Exception {
         this.localServer.register("*", new SetCookieVersionMixService());
 
-        DefaultHttpClient client = new DefaultHttpClient();
-        client.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);
+        this.httpclient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BEST_MATCH);
 
         CookieStore cookieStore = new BasicCookieStore();
         HttpContext context = new BasicHttpContext();
@@ -240,7 +237,7 @@ public class TestCookie2Support extends BasicServerTestBase {
 
         HttpGet httpget = new HttpGet("/test/");
 
-        HttpResponse response1 = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response1 = this.httpclient.execute(getServerHttp(), httpget, context);
         HttpEntity e1 = response1.getEntity();
         EntityUtils.consume(e1);
 
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestRedirects.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestRedirects.java
index 611ef34..4238a32 100644
--- a/httpclient/src/test/java/org/apache/http/client/protocol/TestRedirects.java
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestRedirects.java
@@ -71,9 +71,10 @@ public class TestRedirects extends BasicServerTestBase {
 
     @Before
     public void setUp() throws Exception {
-        localServer = new LocalTestServer(null, null);
-        localServer.registerDefaultHandlers();
-        localServer.start();
+        this.localServer = new LocalTestServer(null, null);
+        this.localServer.registerDefaultHandlers();
+        this.localServer.start();
+        this.httpclient = new DefaultHttpClient();
     }
 
     private static class BasicRedirectService implements HttpRequestHandler {
@@ -225,12 +226,11 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*",
                 new BasicRedirectService(host, port, HttpStatus.SC_MULTIPLE_CHOICES));
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         HttpGet httpget = new HttpGet("/oldlocation/");
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -248,12 +248,11 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*",
                 new BasicRedirectService(host, port, HttpStatus.SC_MOVED_PERMANENTLY));
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         HttpGet httpget = new HttpGet("/oldlocation/");
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -275,12 +274,11 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*",
                 new BasicRedirectService(host, port, HttpStatus.SC_MOVED_TEMPORARILY));
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         HttpGet httpget = new HttpGet("/oldlocation/");
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -310,12 +308,11 @@ public class TestRedirects extends BasicServerTestBase {
 
         });
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         HttpGet httpget = new HttpGet("/oldlocation/");
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -337,12 +334,11 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*",
                 new BasicRedirectService(host, port, HttpStatus.SC_SEE_OTHER));
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         HttpGet httpget = new HttpGet("/oldlocation/");
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -364,12 +360,11 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*",
                 new BasicRedirectService(host, port, HttpStatus.SC_NOT_MODIFIED));
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         HttpGet httpget = new HttpGet("/oldlocation/");
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -387,12 +382,11 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*",
                 new BasicRedirectService(host, port, HttpStatus.SC_USE_PROXY));
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         HttpGet httpget = new HttpGet("/oldlocation/");
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -410,12 +404,11 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*",
                 new BasicRedirectService(host, port, HttpStatus.SC_TEMPORARY_REDIRECT));
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         HttpGet httpget = new HttpGet("/oldlocation/");
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -433,13 +426,12 @@ public class TestRedirects extends BasicServerTestBase {
     public void testMaxRedirectCheck() throws Exception {
         this.localServer.register("*", new CircularRedirectService());
 
-        DefaultHttpClient client = new DefaultHttpClient();
-        client.getParams().setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);
-        client.getParams().setIntParameter(ClientPNames.MAX_REDIRECTS, 5);
+        this.httpclient.getParams().setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, true);
+        this.httpclient.getParams().setIntParameter(ClientPNames.MAX_REDIRECTS, 5);
 
         HttpGet httpget = new HttpGet("/circular-oldlocation/");
         try {
-            client.execute(getServerHttp(), httpget);
+            this.httpclient.execute(getServerHttp(), httpget);
         } catch (ClientProtocolException e) {
             Assert.assertTrue(e.getCause() instanceof RedirectException);
             throw e;
@@ -450,13 +442,12 @@ public class TestRedirects extends BasicServerTestBase {
     public void testCircularRedirect() throws Exception {
         this.localServer.register("*", new CircularRedirectService());
 
-        DefaultHttpClient client = new DefaultHttpClient();
-        client.getParams().setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, false);
+        this.httpclient.getParams().setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, false);
 
         HttpGet httpget = new HttpGet("/circular-oldlocation/");
 
         try {
-            client.execute(getServerHttp(), httpget);
+            this.httpclient.execute(getServerHttp(), httpget);
         } catch (ClientProtocolException e) {
             Assert.assertTrue(e.getCause() instanceof CircularRedirectException);
             throw e;
@@ -470,13 +461,12 @@ public class TestRedirects extends BasicServerTestBase {
         String host = address.getHostName();
         this.localServer.register("*", new BasicRedirectService(host, port));
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         HttpPost httppost = new HttpPost("/oldlocation/");
         httppost.setEntity(new StringEntity("stuff"));
 
-        HttpResponse response = client.execute(getServerHttp(), httppost, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httppost, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -495,13 +485,12 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*", new BasicRedirectService(host, port,
                 HttpStatus.SC_SEE_OTHER));
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         HttpPost httppost = new HttpPost("/oldlocation/");
         httppost.setEntity(new StringEntity("stuff"));
 
-        HttpResponse response = client.execute(getServerHttp(), httppost, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httppost, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -519,14 +508,13 @@ public class TestRedirects extends BasicServerTestBase {
         String host = address.getHostName();
         this.localServer.register("*", new RelativeRedirectService());
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
-        client.getParams().setBooleanParameter(
+        this.httpclient.getParams().setBooleanParameter(
                 ClientPNames.REJECT_RELATIVE_REDIRECT, false);
         HttpGet httpget = new HttpGet("/oldlocation/");
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -547,14 +535,13 @@ public class TestRedirects extends BasicServerTestBase {
         String host = address.getHostName();
         this.localServer.register("*", new RelativeRedirectService2());
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
-        client.getParams().setBooleanParameter(
+        this.httpclient.getParams().setBooleanParameter(
                 ClientPNames.REJECT_RELATIVE_REDIRECT, false);
         HttpGet httpget = new HttpGet("/test/oldlocation");
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -572,29 +559,25 @@ public class TestRedirects extends BasicServerTestBase {
     public void testRejectRelativeRedirect() throws Exception {
         this.localServer.register("*", new RelativeRedirectService());
 
-        DefaultHttpClient client = new DefaultHttpClient();
-
-        client.getParams().setBooleanParameter(
+        this.httpclient.getParams().setBooleanParameter(
                 ClientPNames.REJECT_RELATIVE_REDIRECT, true);
         HttpGet httpget = new HttpGet("/oldlocation/");
 
         try {
-            client.execute(getServerHttp(), httpget);
+            this.httpclient.execute(getServerHttp(), httpget);
         } catch (ClientProtocolException e) {
             Assert.assertTrue(e.getCause() instanceof ProtocolException);
             throw e;
         }
     }
 
-    @Test(expected=IllegalStateException.class)
+    @Test(expected=ClientProtocolException.class)
     public void testRejectBogusRedirectLocation() throws Exception {
         this.localServer.register("*", new BogusRedirectService("xxx://bogus"));
 
-        DefaultHttpClient client = new DefaultHttpClient();
-
         HttpGet httpget = new HttpGet("/oldlocation/");
 
-        client.execute(getServerHttp(), httpget);
+        this.httpclient.execute(getServerHttp(), httpget);
     }
 
     @Test(expected=ClientProtocolException.class)
@@ -605,12 +588,10 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*",
                 new BogusRedirectService("http://"+ host +":"+ port +"/newlocation/?p=I have spaces"));
 
-        DefaultHttpClient client = new DefaultHttpClient();
-
         HttpGet httpget = new HttpGet("/oldlocation/");
 
         try {
-            client.execute(getServerHttp(), httpget);
+            this.httpclient.execute(getServerHttp(), httpget);
         } catch (ClientProtocolException e) {
             Assert.assertTrue(e.getCause() instanceof ProtocolException);
             throw e;
@@ -626,10 +607,8 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*",
                 new BasicRedirectService(host, port));
 
-        DefaultHttpClient client = new DefaultHttpClient();
-
         CookieStore cookieStore = new BasicCookieStore();
-        client.setCookieStore(cookieStore);
+        this.httpclient.setCookieStore(cookieStore);
 
         BasicClientCookie cookie = new BasicClientCookie("name", "value");
         cookie.setDomain(host);
@@ -641,7 +620,7 @@ public class TestRedirects extends BasicServerTestBase {
         HttpGet httpget = new HttpGet("/oldlocation/");
 
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -663,18 +642,17 @@ public class TestRedirects extends BasicServerTestBase {
         this.localServer.register("*",
                 new BasicRedirectService(host, port));
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         List<Header> defaultHeaders = new ArrayList<Header>(1);
         defaultHeaders.add(new BasicHeader(HTTP.USER_AGENT, "my-test-client"));
 
-        client.getParams().setParameter(ClientPNames.DEFAULT_HEADERS, defaultHeaders);
+        this.httpclient.getParams().setParameter(ClientPNames.DEFAULT_HEADERS, defaultHeaders);
 
         HttpGet httpget = new HttpGet("/oldlocation/");
 
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAcceptEncoding.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAcceptEncoding.java
index 7677aa4..baca22d 100644
--- a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAcceptEncoding.java
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAcceptEncoding.java
@@ -50,4 +50,17 @@ public class TestRequestAcceptEncoding {
         Assert.assertEquals("gzip,deflate", header.getValue());
     }
 
+    @Test
+    public void testAcceptEncodingAlreadyPResent() throws Exception {
+        HttpRequest request = new BasicHttpRequest("GET", "/");
+        request.addHeader("Accept-Encoding", "stuff");
+        HttpContext context = new BasicHttpContext();
+
+        HttpRequestInterceptor interceptor = new RequestAcceptEncoding();
+        interceptor.process(request, context);
+        Header header = request.getFirstHeader("Accept-Encoding");
+        Assert.assertNotNull(header);
+        Assert.assertEquals("stuff", header.getValue());
+    }
+
 }
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAddCookies.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAddCookies.java
index eca8dcb..6311411 100644
--- a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAddCookies.java
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAddCookies.java
@@ -64,6 +64,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 
+ at SuppressWarnings("boxing")
 public class TestRequestAddCookies {
 
     private HttpHost target;
@@ -499,6 +500,48 @@ public class TestRequestAddCookies {
         Assert.assertEquals(0, headers2.length);
     }
 
+    // Helper method
+    private BasicClientCookie makeCookie(String name, String value, String domain, String path) {
+        BasicClientCookie cookie = new BasicClientCookie(name, value);
+        cookie.setDomain(domain);
+        cookie.setPath(path);
+        return cookie;
+    }
+
+    @Test
+    // Test for ordering adapted from test in Commons HC 3.1
+    public void testCookieOrder() throws Exception {
+        HttpRequest request = new BasicHttpRequest("GET", "/foobar/yada/yada");
+
+        this.cookieStore.clear();
+
+        cookieStore.addCookie(makeCookie("nomatch", "value", "localhost.local", "/noway"));
+        cookieStore.addCookie(makeCookie("name2",   "value", "localhost.local", "/foobar/yada"));
+        cookieStore.addCookie(makeCookie("name3",   "value", "localhost.local", "/foobar"));
+        cookieStore.addCookie(makeCookie("name1",   "value", "localhost.local", "/foobar/yada/yada"));
+
+        HttpRoute route = new HttpRoute(this.target, null, false);
+
+        HttpRoutedConnection conn = Mockito.mock(HttpRoutedConnection.class);
+        Mockito.when(conn.getRoute()).thenReturn(route);
+        Mockito.when(conn.isSecure()).thenReturn(Boolean.FALSE);
+
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, this.target);
+        context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+        context.setAttribute(ClientContext.COOKIE_STORE, this.cookieStore);
+        context.setAttribute(ClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
+
+        HttpRequestInterceptor interceptor = new RequestAddCookies();
+        interceptor.process(request, context);
+
+        Header[] headers1 = request.getHeaders(SM.COOKIE);
+        Assert.assertNotNull(headers1);
+        Assert.assertEquals(1, headers1.length);
+
+        Assert.assertEquals("name1=value; name2=value; name3=value", headers1[0].getValue());
+    }
+
     @Test
     public void testAddSpecVersionHeader() throws Exception {
         HttpRequest request = new BasicHttpRequest("GET", "/");
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAuthCache.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAuthCache.java
index 6f6742f..d499432 100644
--- a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAuthCache.java
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAuthCache.java
@@ -31,6 +31,7 @@ import junit.framework.Assert;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.auth.AuthProtocolState;
 import org.apache.http.auth.AuthScope;
 import org.apache.http.auth.AuthState;
 import org.apache.http.auth.Credentials;
@@ -68,8 +69,8 @@ public class TestRequestAuthCache {
         this.credProvider = new BasicCredentialsProvider();
         this.creds1 = new UsernamePasswordCredentials("user1", "secret1");
         this.creds2 = new UsernamePasswordCredentials("user2", "secret2");
-        this.authscope1 = new AuthScope(this.target.getHostName(), this.target.getPort());
-        this.authscope2 = new AuthScope(this.proxy.getHostName(), this.proxy.getPort());
+        this.authscope1 = new AuthScope(this.target);
+        this.authscope2 = new AuthScope(this.proxy);
         this.authscheme1 = new BasicScheme();
         this.authscheme2 = new BasicScheme();
 
@@ -230,10 +231,10 @@ public class TestRequestAuthCache {
 
         context.setAttribute(ClientContext.AUTH_CACHE, authCache);
 
-        this.targetState.setAuthScheme(new BasicScheme());
-        this.targetState.setCredentials(new UsernamePasswordCredentials("user3", "secret3"));
-        this.proxyState.setAuthScheme(new BasicScheme());
-        this.proxyState.setCredentials(new UsernamePasswordCredentials("user4", "secret4"));
+        this.targetState.setState(AuthProtocolState.CHALLENGED);
+        this.targetState.update(new BasicScheme(), new UsernamePasswordCredentials("user3", "secret3"));
+        this.proxyState.setState(AuthProtocolState.CHALLENGED);
+        this.proxyState.update(new BasicScheme(), new UsernamePasswordCredentials("user4", "secret4"));
 
         HttpRequestInterceptor interceptor = new RequestAuthCache();
         interceptor.process(request, context);
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAuthenticationBase.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAuthenticationBase.java
new file mode 100644
index 0000000..f792e51
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestAuthenticationBase.java
@@ -0,0 +1,216 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.protocol;
+
+import java.io.IOException;
+import java.util.LinkedList;
+
+import junit.framework.Assert;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthOption;
+import org.apache.http.auth.AuthProtocolState;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.AuthenticationException;
+import org.apache.http.auth.ContextAwareAuthScheme;
+import org.apache.http.auth.Credentials;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestRequestAuthenticationBase {
+
+    static class TestRequestAuthentication extends RequestAuthenticationBase {
+
+        public void process(
+                final HttpRequest request,
+                final HttpContext context) throws HttpException, IOException {
+            AuthState authState = (AuthState) context.getAttribute("test-auth-state");
+            super.process(authState, request, context);
+        }
+
+    }
+
+    private ContextAwareAuthScheme authScheme;
+    private Credentials credentials;
+    private AuthState authState;
+    private HttpContext context;
+    private HttpRequestInterceptor interceptor;
+
+    @Before
+    public void setUp() throws Exception {
+        this.authScheme = Mockito.mock(ContextAwareAuthScheme.class);
+        this.credentials = Mockito.mock(Credentials.class);
+        this.authState = new AuthState();
+        this.context = new BasicHttpContext();
+        this.context.setAttribute("test-auth-state", this.authState);
+        this.interceptor = new TestRequestAuthentication();
+    }
+
+    @Test
+    public void testAuthFailureState() throws Exception {
+        HttpRequest request = new BasicHttpRequest("GET", "/");
+        this.authState.setState(AuthProtocolState.FAILURE);
+        this.authState.update(this.authScheme, this.credentials);
+
+        this.interceptor.process(request, this.context);
+
+        Assert.assertFalse(request.containsHeader(AUTH.WWW_AUTH_RESP));
+
+        Mockito.verify(this.authScheme, Mockito.never()).authenticate(
+                Mockito.any(Credentials.class),
+                Mockito.any(HttpRequest.class),
+                Mockito.any(HttpContext.class));
+    }
+
+    @Test
+    public void testAuthChallengeStateNoOption() throws Exception {
+        HttpRequest request = new BasicHttpRequest("GET", "/");
+        this.authState.setState(AuthProtocolState.CHALLENGED);
+        this.authState.update(this.authScheme, this.credentials);
+
+        Mockito.when(this.authScheme.authenticate(
+                Mockito.any(Credentials.class),
+                Mockito.any(HttpRequest.class),
+                Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(AUTH.WWW_AUTH_RESP, "stuff"));
+
+        this.interceptor.process(request, this.context);
+
+        Assert.assertTrue(request.containsHeader(AUTH.WWW_AUTH_RESP));
+
+        Mockito.verify(this.authScheme).authenticate(this.credentials, request, this.context);
+    }
+
+    @Test
+    public void testAuthChallengeStateOneOptions() throws Exception {
+        HttpRequest request = new BasicHttpRequest("GET", "/");
+        this.authState.setState(AuthProtocolState.CHALLENGED);
+        LinkedList<AuthOption> authOptions = new LinkedList<AuthOption>();
+        authOptions.add(new AuthOption(this.authScheme, this.credentials));
+        this.authState.update(authOptions);
+
+        Mockito.when(this.authScheme.authenticate(
+                Mockito.any(Credentials.class),
+                Mockito.any(HttpRequest.class),
+                Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(AUTH.WWW_AUTH_RESP, "stuff"));
+
+        this.interceptor.process(request, this.context);
+
+        Assert.assertSame(this.authScheme, this.authState.getAuthScheme());
+        Assert.assertSame(this.credentials, this.authState.getCredentials());
+        Assert.assertNull(this.authState.getAuthOptions());
+
+        Assert.assertTrue(request.containsHeader(AUTH.WWW_AUTH_RESP));
+
+        Mockito.verify(this.authScheme).authenticate(this.credentials, request, this.context);
+    }
+
+    @Test
+    public void testAuthChallengeStateMultipleOption() throws Exception {
+        HttpRequest request = new BasicHttpRequest("GET", "/");
+        this.authState.setState(AuthProtocolState.CHALLENGED);
+
+        LinkedList<AuthOption> authOptions = new LinkedList<AuthOption>();
+        ContextAwareAuthScheme authScheme1 = Mockito.mock(ContextAwareAuthScheme.class);
+        Mockito.doThrow(new AuthenticationException()).when(authScheme1).authenticate(
+                Mockito.any(Credentials.class),
+                Mockito.any(HttpRequest.class),
+                Mockito.any(HttpContext.class));
+        ContextAwareAuthScheme authScheme2 = Mockito.mock(ContextAwareAuthScheme.class);
+        Mockito.when(authScheme2.authenticate(
+                Mockito.any(Credentials.class),
+                Mockito.any(HttpRequest.class),
+                Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(AUTH.WWW_AUTH_RESP, "stuff"));
+        authOptions.add(new AuthOption(authScheme1, this.credentials));
+        authOptions.add(new AuthOption(authScheme2, this.credentials));
+        this.authState.update(authOptions);
+
+        this.interceptor.process(request, this.context);
+
+        Assert.assertSame(authScheme2, this.authState.getAuthScheme());
+        Assert.assertSame(this.credentials, this.authState.getCredentials());
+        Assert.assertNull(this.authState.getAuthOptions());
+
+        Assert.assertTrue(request.containsHeader(AUTH.WWW_AUTH_RESP));
+
+        Mockito.verify(authScheme1, Mockito.times(1)).authenticate(this.credentials, request, this.context);
+        Mockito.verify(authScheme2, Mockito.times(1)).authenticate(this.credentials, request, this.context);
+    }
+
+    @Test
+    public void testAuthSuccess() throws Exception {
+        HttpRequest request = new BasicHttpRequest("GET", "/");
+        this.authState.setState(AuthProtocolState.SUCCESS);
+        this.authState.update(this.authScheme, this.credentials);
+
+        Mockito.when(this.authScheme.isConnectionBased()).thenReturn(Boolean.FALSE);
+        Mockito.when(this.authScheme.authenticate(
+                Mockito.any(Credentials.class),
+                Mockito.any(HttpRequest.class),
+                Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(AUTH.WWW_AUTH_RESP, "stuff"));
+
+        this.interceptor.process(request, this.context);
+
+        Assert.assertSame(this.authScheme, this.authState.getAuthScheme());
+        Assert.assertSame(this.credentials, this.authState.getCredentials());
+        Assert.assertNull(this.authState.getAuthOptions());
+
+        Assert.assertTrue(request.containsHeader(AUTH.WWW_AUTH_RESP));
+
+        Mockito.verify(this.authScheme).authenticate(this.credentials, request, this.context);
+    }
+
+    @Test
+    public void testAuthSuccessConnectionBased() throws Exception {
+        HttpRequest request = new BasicHttpRequest("GET", "/");
+        this.authState.setState(AuthProtocolState.SUCCESS);
+        this.authState.update(this.authScheme, this.credentials);
+
+        Mockito.when(this.authScheme.isConnectionBased()).thenReturn(Boolean.TRUE);
+        Mockito.when(this.authScheme.authenticate(
+                Mockito.any(Credentials.class),
+                Mockito.any(HttpRequest.class),
+                Mockito.any(HttpContext.class))).thenReturn(new BasicHeader(AUTH.WWW_AUTH_RESP, "stuff"));
+
+        this.interceptor.process(request, this.context);
+
+        Assert.assertFalse(request.containsHeader(AUTH.WWW_AUTH_RESP));
+
+        Mockito.verify(this.authScheme, Mockito.never()).authenticate(
+                Mockito.any(Credentials.class),
+                Mockito.any(HttpRequest.class),
+                Mockito.any(HttpContext.class));
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestProxyAuthentication.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestProxyAuthentication.java
index b255df1..c3dc228 100644
--- a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestProxyAuthentication.java
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestProxyAuthentication.java
@@ -33,7 +33,7 @@ import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequestInterceptor;
 import org.apache.http.auth.AUTH;
-import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.AuthProtocolState;
 import org.apache.http.auth.AuthState;
 import org.apache.http.auth.Credentials;
 import org.apache.http.auth.UsernamePasswordCredentials;
@@ -81,14 +81,11 @@ public class TestRequestProxyAuthentication {
 
         BasicScheme authscheme = new BasicScheme();
         Credentials creds = new UsernamePasswordCredentials("user", "secret");
-        AuthScope authscope = new AuthScope("localhost", 8080, "auth-realm", "http");
         BasicHeader challenge = new BasicHeader(AUTH.PROXY_AUTH, "BASIC realm=auth-realm");
         authscheme.processChallenge(challenge);
 
         AuthState authstate = new AuthState();
-        authstate.setAuthScheme(authscheme);
-        authstate.setAuthScope(authscope);
-        authstate.setCredentials(creds);
+        authstate.update(authscheme, creds);
 
         context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
         context.setAttribute(ClientContext.PROXY_AUTH_STATE, authstate);
@@ -115,15 +112,12 @@ public class TestRequestProxyAuthentication {
 
         BasicScheme authscheme = new BasicScheme();
         Credentials creds = new UsernamePasswordCredentials("user", "secret");
-        AuthScope authscope = new AuthScope("localhost", 8080, "auth-realm", "http");
 
         BasicHeader challenge = new BasicHeader(AUTH.PROXY_AUTH, "BASIC realm=auth-realm");
         authscheme.processChallenge(challenge);
 
         AuthState authstate = new AuthState();
-        authstate.setAuthScheme(authscheme);
-        authstate.setAuthScope(authscope);
-        authstate.setCredentials(creds);
+        authstate.update(authscheme, creds);
 
         context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
         context.setAttribute(ClientContext.PROXY_AUTH_STATE, authstate);
@@ -150,15 +144,12 @@ public class TestRequestProxyAuthentication {
 
         BasicScheme authscheme = new BasicScheme();
         Credentials creds = new UsernamePasswordCredentials("user", "secret");
-        AuthScope authscope = new AuthScope("localhost", 8080, "auth-realm", "http");
 
         BasicHeader challenge = new BasicHeader(AUTH.PROXY_AUTH, "BASIC realm=auth-realm");
         authscheme.processChallenge(challenge);
 
         AuthState authstate = new AuthState();
-        authstate.setAuthScheme(authscheme);
-        authstate.setAuthScope(authscope);
-        authstate.setCredentials(creds);
+        authstate.update(authscheme, creds);
 
         context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
         context.setAttribute(ClientContext.PROXY_AUTH_STATE, authstate);
@@ -217,36 +208,6 @@ public class TestRequestProxyAuthentication {
     }
 
     @Test
-    public void testAuthCredentialsNotSet() throws Exception {
-        HttpRequest request = new BasicHttpRequest("GET", "/");
-        HttpContext context = new BasicHttpContext();
-
-        HttpHost target = new HttpHost("localhost", 80, "http");
-        HttpHost proxy = new HttpHost("localhost", 8080);
-        HttpRoute route = new HttpRoute(target, null, proxy, false,
-                TunnelType.PLAIN, LayerType.PLAIN);
-
-        HttpRoutedConnection conn = Mockito.mock(HttpRoutedConnection.class);
-        Mockito.when(conn.getRoute()).thenReturn(route);
-
-        AuthState authstate = new AuthState();
-
-        BasicScheme authscheme = new BasicScheme();
-        BasicHeader challenge = new BasicHeader(AUTH.PROXY_AUTH, "BASIC realm=auth-realm");
-        authscheme.processChallenge(challenge);
-
-        authstate.setAuthScheme(authscheme);
-
-        context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
-        context.setAttribute(ClientContext.PROXY_AUTH_STATE, authstate);
-
-        HttpRequestInterceptor interceptor = new RequestProxyAuthentication();
-        interceptor.process(request, context);
-        Header header = request.getFirstHeader(AUTH.PROXY_AUTH_RESP);
-        Assert.assertNull(header);
-    }
-
-    @Test
     public void testConnectionBasedAuthOnlyIfChallenged() throws Exception {
         HttpRequest request = new BasicHttpRequest("GET", "/");
         HttpContext context = new BasicHttpContext();
@@ -275,10 +236,8 @@ public class TestRequestProxyAuthentication {
 
         Credentials creds = new UsernamePasswordCredentials("user", "secret");
 
-        authstate.setAuthScheme(authscheme);
-        authstate.setCredentials(creds);
-        // No challenge
-        authstate.setAuthScope(null);
+        authstate.setState(AuthProtocolState.SUCCESS);
+        authstate.update(authscheme, creds);
 
         context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
         context.setAttribute(ClientContext.PROXY_AUTH_STATE, authstate);
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestTargetAuthentication.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestTargetAuthentication.java
index 7c4d55a..04c2623 100644
--- a/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestTargetAuthentication.java
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestRequestTargetAuthentication.java
@@ -32,7 +32,7 @@ import org.apache.http.Header;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequestInterceptor;
 import org.apache.http.auth.AUTH;
-import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.AuthProtocolState;
 import org.apache.http.auth.AuthState;
 import org.apache.http.auth.Credentials;
 import org.apache.http.auth.UsernamePasswordCredentials;
@@ -66,14 +66,11 @@ public class TestRequestTargetAuthentication {
 
         BasicScheme authscheme = new BasicScheme();
         Credentials creds = new UsernamePasswordCredentials("user", "secret");
-        AuthScope authscope = new AuthScope("localhost", 8080, "auth-realm", "http");
         BasicHeader challenge = new BasicHeader(AUTH.WWW_AUTH, "BASIC realm=auth-realm");
         authscheme.processChallenge(challenge);
 
         AuthState authstate = new AuthState();
-        authstate.setAuthScheme(authscheme);
-        authstate.setAuthScope(authscope);
-        authstate.setCredentials(creds);
+        authstate.update(authscheme, creds);
 
         context.setAttribute(ClientContext.TARGET_AUTH_STATE, authstate);
 
@@ -91,14 +88,11 @@ public class TestRequestTargetAuthentication {
 
         BasicScheme authscheme = new BasicScheme();
         Credentials creds = new UsernamePasswordCredentials("user", "secret");
-        AuthScope authscope = new AuthScope("localhost", 8080, "auth-realm", "http");
         BasicHeader challenge = new BasicHeader(AUTH.WWW_AUTH, "BASIC realm=auth-realm");
         authscheme.processChallenge(challenge);
 
         AuthState authstate = new AuthState();
-        authstate.setAuthScheme(authscheme);
-        authstate.setAuthScope(authscope);
-        authstate.setCredentials(creds);
+        authstate.update(authscheme, creds);
 
         context.setAttribute(ClientContext.TARGET_AUTH_STATE, authstate);
 
@@ -116,15 +110,12 @@ public class TestRequestTargetAuthentication {
 
         BasicScheme authscheme = new BasicScheme();
         Credentials creds = new UsernamePasswordCredentials("user", "secret");
-        AuthScope authscope = new AuthScope("localhost", 8080, "auth-realm", "http");
 
         BasicHeader challenge = new BasicHeader(AUTH.WWW_AUTH, "BASIC realm=auth-realm");
         authscheme.processChallenge(challenge);
 
         AuthState authstate = new AuthState();
-        authstate.setAuthScheme(authscheme);
-        authstate.setAuthScope(authscope);
-        authstate.setCredentials(creds);
+        authstate.update(authscheme, creds);
 
         context.setAttribute(ClientContext.TARGET_AUTH_STATE, authstate);
 
@@ -164,27 +155,6 @@ public class TestRequestTargetAuthentication {
     }
 
     @Test
-    public void testAuthCredentialsNotSet() throws Exception {
-        HttpRequest request = new BasicHttpRequest("GET", "/");
-        HttpContext context = new BasicHttpContext();
-
-        AuthState authstate = new AuthState();
-
-        BasicScheme authscheme = new BasicScheme();
-        BasicHeader challenge = new BasicHeader(AUTH.WWW_AUTH, "BASIC realm=auth-realm");
-        authscheme.processChallenge(challenge);
-
-        authstate.setAuthScheme(authscheme);
-
-        context.setAttribute(ClientContext.TARGET_AUTH_STATE, authstate);
-
-        HttpRequestInterceptor interceptor = new RequestTargetAuthentication();
-        interceptor.process(request, context);
-        Header header = request.getFirstHeader(AUTH.WWW_AUTH_RESP);
-        Assert.assertNull(header);
-    }
-
-    @Test
     public void testConnectionBasedAuthOnlyIfChallenged() throws Exception {
         HttpRequest request = new BasicHttpRequest("GET", "/");
         HttpContext context = new BasicHttpContext();
@@ -205,10 +175,8 @@ public class TestRequestTargetAuthentication {
 
         Credentials creds = new UsernamePasswordCredentials("user", "secret");
 
-        authstate.setAuthScheme(authscheme);
-        authstate.setCredentials(creds);
-        // No challenge
-        authstate.setAuthScope(null);
+        authstate.setState(AuthProtocolState.SUCCESS);
+        authstate.update(authscheme, creds);
 
         context.setAttribute(ClientContext.TARGET_AUTH_STATE, authstate);
 
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestResponseAuthCache.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestResponseAuthCache.java
index d8edf35..465d5bd 100644
--- a/httpclient/src/test/java/org/apache/http/client/protocol/TestResponseAuthCache.java
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestResponseAuthCache.java
@@ -33,7 +33,7 @@ import org.apache.http.HttpResponse;
 import org.apache.http.HttpResponseInterceptor;
 import org.apache.http.HttpVersion;
 import org.apache.http.auth.AUTH;
-import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.AuthProtocolState;
 import org.apache.http.auth.AuthState;
 import org.apache.http.auth.Credentials;
 import org.apache.http.auth.UsernamePasswordCredentials;
@@ -48,16 +48,14 @@ import org.apache.http.protocol.HttpContext;
 import org.junit.Before;
 import org.junit.Test;
 
+ at Deprecated 
 public class TestResponseAuthCache {
 
     private HttpHost target;
     private HttpHost proxy;
-    private Credentials creds1;
-    private Credentials creds2;
-    private AuthScope authscope1;
-    private AuthScope authscope2;
     private BasicScheme authscheme1;
     private BasicScheme authscheme2;
+    private Credentials credentials;
     private AuthState targetState;
     private AuthState proxyState;
 
@@ -66,12 +64,9 @@ public class TestResponseAuthCache {
         this.target = new HttpHost("localhost", 80);
         this.proxy = new HttpHost("localhost", 8080);
 
-        this.creds1 = new UsernamePasswordCredentials("user1", "secret1");
-        this.creds2 = new UsernamePasswordCredentials("user2", "secret2");
-        this.authscope1 = new AuthScope(this.target.getHostName(), this.target.getPort());
-        this.authscope2 = new AuthScope(this.proxy.getHostName(), this.proxy.getPort());
         this.authscheme1 = new BasicScheme();
         this.authscheme2 = new BasicScheme();
+        this.credentials = new UsernamePasswordCredentials("user", "pwd");
 
         this.targetState = new AuthState();
         this.proxyState = new AuthState();
@@ -100,13 +95,11 @@ public class TestResponseAuthCache {
         this.authscheme2.processChallenge(
                 new BasicHeader(AUTH.PROXY_AUTH, "BASIC realm=auth-realm"));
 
-        this.targetState.setAuthScheme(this.authscheme1);
-        this.targetState.setCredentials(this.creds1);
-        this.targetState.setAuthScope(this.authscope1);
+        this.targetState.setState(AuthProtocolState.CHALLENGED);
+        this.targetState.update(this.authscheme1, this.credentials);
 
-        this.proxyState.setAuthScheme(this.authscheme2);
-        this.proxyState.setCredentials(this.creds2);
-        this.proxyState.setAuthScope(this.authscope2);
+        this.proxyState.setState(AuthProtocolState.CHALLENGED);
+        this.proxyState.update(this.authscheme2, this.credentials);
 
         HttpContext context = new BasicHttpContext();
         context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, this.target);
@@ -159,13 +152,11 @@ public class TestResponseAuthCache {
     public void testAuthSchemeNotCompleted() throws Exception {
         HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
 
-        this.targetState.setAuthScheme(this.authscheme1);
-        this.targetState.setCredentials(this.creds1);
-        this.targetState.setAuthScope(this.authscope1);
+        this.targetState.setState(AuthProtocolState.CHALLENGED);
+        this.targetState.update(this.authscheme1, this.credentials);
 
-        this.proxyState.setAuthScheme(this.authscheme2);
-        this.proxyState.setCredentials(this.creds2);
-        this.proxyState.setAuthScope(this.authscope2);
+        this.proxyState.setState(AuthProtocolState.CHALLENGED);
+        this.proxyState.update(this.authscheme2, this.credentials);
 
         HttpContext context = new BasicHttpContext();
         context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, this.target);
@@ -189,13 +180,11 @@ public class TestResponseAuthCache {
         this.authscheme2.processChallenge(
                 new BasicHeader(AUTH.PROXY_AUTH, "BASIC realm=auth-realm"));
 
-        this.targetState.setAuthScheme(this.authscheme1);
-        this.targetState.setCredentials(this.creds1);
-        this.targetState.setAuthScope(null);
+        this.targetState.setState(AuthProtocolState.UNCHALLENGED);
+        this.targetState.update(this.authscheme1, this.credentials);
 
-        this.proxyState.setAuthScheme(this.authscheme2);
-        this.proxyState.setCredentials(this.creds2);
-        this.proxyState.setAuthScope(null);
+        this.proxyState.setState(AuthProtocolState.UNCHALLENGED);
+        this.proxyState.update(this.authscheme2, this.credentials);
 
         HttpContext context = new BasicHttpContext();
         context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, this.target);
@@ -221,13 +210,11 @@ public class TestResponseAuthCache {
         this.authscheme2.processChallenge(
                 new BasicHeader(AUTH.PROXY_AUTH, "BASIC realm=auth-realm"));
 
-        this.targetState.setAuthScheme(this.authscheme1);
-        this.targetState.setCredentials(null);
-        this.targetState.setAuthScope(this.authscope1);
+        this.targetState.setState(AuthProtocolState.FAILURE);
+        this.targetState.update(this.authscheme1, this.credentials);
 
-        this.proxyState.setAuthScheme(this.authscheme2);
-        this.proxyState.setCredentials(null);
-        this.proxyState.setAuthScope(this.authscope2);
+        this.proxyState.setState(AuthProtocolState.FAILURE);
+        this.proxyState.update(this.authscheme2, this.credentials);
 
         HttpContext context = new BasicHttpContext();
         context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, this.target);
diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestUriEscapes.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestUriEscapes.java
index 96032a8..e61910b 100644
--- a/httpclient/src/test/java/org/apache/http/client/protocol/TestUriEscapes.java
+++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestUriEscapes.java
@@ -51,9 +51,10 @@ public class TestUriEscapes extends BasicServerTestBase {
 
     @Before
     public void setUp() throws Exception {
-        localServer = new LocalTestServer(null, null);
-        localServer.registerDefaultHandlers();
-        localServer.start();
+        this.localServer = new LocalTestServer(null, null);
+        this.localServer.registerDefaultHandlers();
+        this.localServer.start();
+        this.httpclient = new DefaultHttpClient();
     }
 
     private static class UriListeningService implements HttpRequestHandler {
@@ -83,18 +84,17 @@ public class TestUriEscapes extends BasicServerTestBase {
         UriListeningService listener = new UriListeningService();
         this.localServer.register("*", listener);
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpResponse response;
 
         if(!relative) {
             String request = "http://" + host + ":" + port + uri;
             HttpGet httpget = new HttpGet(request);
-            response = client.execute(httpget);
+            response = this.httpclient.execute(httpget);
             EntityUtils.consume(response.getEntity());
         } else {
             HttpHost target = new HttpHost(host, port);
             HttpGet httpget = new HttpGet(uri);
-            response = client.execute(target, httpget);
+            response = this.httpclient.execute(target, httpget);
             EntityUtils.consume(response.getEntity());
         }
 
@@ -104,12 +104,12 @@ public class TestUriEscapes extends BasicServerTestBase {
 
     @Test
     public void testEscapedAmpersandInQueryAbsolute() throws Exception {
-        doTest("/path/a=b&c=%26d", false);
+        doTest("/path?a=b&c=%26d", false);
     }
 
     @Test
     public void testEscapedAmpersandInQueryRelative() throws Exception {
-        doTest("/path/a=b&c=%26d", true);
+        doTest("/path?a=b&c=%26d", true);
     }
 
     @Test
@@ -134,12 +134,12 @@ public class TestUriEscapes extends BasicServerTestBase {
 
     @Test
     public void testEscapedAmpersandInPathAbsolute() throws Exception {
-        doTest("/this%26that?a=b&c=d", false);
+        doTest("/this%26that?a=b&c=%26d", false);
     }
 
     @Test
     public void testEscapedAmpersandInPathRelative() throws Exception {
-        doTest("/this%26that?a=b&c=d", true);
+        doTest("/this%26that?a=b&c=%26d", true);
     }
 
     @Test
diff --git a/httpclient/src/test/java/org/apache/http/client/utils/TestHttpClientUtils.java b/httpclient/src/test/java/org/apache/http/client/utils/TestHttpClientUtils.java
new file mode 100644
index 0000000..f6adea3
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/client/utils/TestHttpClientUtils.java
@@ -0,0 +1,125 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.client.utils;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
+import org.apache.http.localserver.BasicServerTestBase;
+import org.apache.http.localserver.LocalTestServer;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.ResponseConnControl;
+import org.apache.http.protocol.ResponseContent;
+import org.apache.http.protocol.ResponseDate;
+import org.apache.http.protocol.ResponseServer;
+import org.apache.http.util.EntityUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestHttpClientUtils extends BasicServerTestBase {
+
+    @Before
+    public void setUp() throws Exception {
+        BasicHttpProcessor httpproc = new BasicHttpProcessor();
+        httpproc.addInterceptor(new ResponseDate());
+        httpproc.addInterceptor(new ResponseServer());
+        httpproc.addInterceptor(new ResponseContent());
+        httpproc.addInterceptor(new ResponseConnControl());
+        this.localServer = new LocalTestServer(httpproc, null);
+    }
+
+    @Test
+    public void testCloseQuietlyClient() throws Exception {
+        HttpClient httpClient = new DefaultHttpClient();
+        HttpClientUtils.closeQuietly(httpClient);
+    }
+
+    @Test
+    public void testCloseQuietlyNullClient() throws Exception {
+        HttpClient httpClient = null;
+        HttpClientUtils.closeQuietly(httpClient);
+    }
+
+    @Test
+    public void testCloseQuietlyClientTwice() {
+        PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager();
+        HttpClient httpClient = new DefaultHttpClient(connectionManager);
+        HttpClientUtils.closeQuietly(httpClient);
+        HttpClientUtils.closeQuietly(httpClient);
+    }
+
+    @Test
+    public void testCloseQuietlyResponse() throws Exception {
+        this.localServer.registerDefaultHandlers();
+        this.localServer.start();
+        PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager();
+        HttpClient httpClient = new DefaultHttpClient(connectionManager);
+        HttpHost target = getServerHttp();
+        HttpResponse response = httpClient.execute(target, new HttpGet("/"));
+        HttpClientUtils.closeQuietly(response);
+        HttpClientUtils.closeQuietly(httpClient);
+    }
+
+    @Test
+    public void testCloseQuietlyResponseNull() throws Exception {
+        PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager();
+        HttpClient httpClient = new DefaultHttpClient(connectionManager);
+        HttpResponse response = null;
+        HttpClientUtils.closeQuietly(response);
+        HttpClientUtils.closeQuietly(httpClient);
+    }
+
+    @Test
+    public void testCloseQuietlyResponseTwice() throws Exception {
+        this.localServer.registerDefaultHandlers();
+        this.localServer.start();
+        PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager();
+        HttpClient httpClient = new DefaultHttpClient(connectionManager);
+        HttpHost target = getServerHttp();
+        HttpResponse response = httpClient.execute(target, new HttpGet("/"));
+        HttpClientUtils.closeQuietly(response);
+        HttpClientUtils.closeQuietly(response);
+        HttpClientUtils.closeQuietly(httpClient);
+    }
+
+    @Test
+    public void testCloseQuietlyResponseAfterConsumeContent() throws Exception {
+        this.localServer.registerDefaultHandlers();
+        this.localServer.start();
+        PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager();
+        HttpClient httpClient = new DefaultHttpClient(connectionManager);
+        HttpHost target = getServerHttp();
+        HttpResponse response = httpClient.execute(target, new HttpGet("/"));
+        EntityUtils.consume(response.getEntity());
+        HttpClientUtils.closeQuietly(response);
+        HttpClientUtils.closeQuietly(httpClient);
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/client/utils/TestURIBuilder.java b/httpclient/src/test/java/org/apache/http/client/utils/TestURIBuilder.java
new file mode 100644
index 0000000..dd5c1a4
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/client/utils/TestURIBuilder.java
@@ -0,0 +1,228 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.client.utils;
+
+import java.net.URI;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestURIBuilder {
+
+    @Test
+    public void testHierarchicalUri() throws Exception {
+        URI uri = new URI("http", "stuff", "localhost", 80, "/some stuff", "param=stuff", "fragment");
+        URIBuilder uribuilder = new URIBuilder(uri);
+        URI result = uribuilder.build();
+        Assert.assertEquals(new URI("http://stuff@localhost:80/some%20stuff?param=stuff#fragment"), result);
+    }
+
+    @Test
+    public void testMutationToRelativeUri() throws Exception {
+        URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment");
+        URIBuilder uribuilder = new URIBuilder(uri).setHost(null);
+        URI result = uribuilder.build();
+        Assert.assertEquals(new URI("http:///stuff?param=stuff#fragment"), result);
+    }
+
+    @Test
+    public void testMutationRemoveFragment() throws Exception {
+        URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment");
+        URI result = new URIBuilder(uri).setFragment(null).build();
+        Assert.assertEquals(new URI("http://stuff@localhost:80/stuff?param=stuff"), result);
+    }
+
+    @Test
+    public void testMutationRemoveUserInfo() throws Exception {
+        URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment");
+        URI result = new URIBuilder(uri).setUserInfo(null).build();
+        Assert.assertEquals(new URI("http://localhost:80/stuff?param=stuff#fragment"), result);
+    }
+
+    @Test
+    public void testMutationRemovePort() throws Exception {
+        URI uri = new URI("http://stuff@localhost:80/stuff?param=stuff#fragment");
+        URI result = new URIBuilder(uri).setPort(-1).build();
+        Assert.assertEquals(new URI("http://stuff@localhost/stuff?param=stuff#fragment"), result);
+    }
+
+    @Test
+    public void testOpaqueUri() throws Exception {
+        URI uri = new URI("stuff", "some-stuff", "fragment");
+        URIBuilder uribuilder = new URIBuilder(uri);
+        URI result = uribuilder.build();
+        Assert.assertEquals(uri, result);
+    }
+
+    @Test
+    public void testOpaqueUriMutation() throws Exception {
+        URI uri = new URI("stuff", "some-stuff", "fragment");
+        URIBuilder uribuilder = new URIBuilder(uri).setQuery("param1&param2=stuff").setFragment(null);
+        Assert.assertEquals(new URI("stuff:?param1&param2=stuff"), uribuilder.build());
+    }
+
+    @Test
+    public void testHierarchicalUriMutation() throws Exception {
+        URIBuilder uribuilder = new URIBuilder("/").setScheme("http").setHost("localhost").setPort(80).setPath("/stuff");
+        Assert.assertEquals(new URI("http://localhost:80/stuff"), uribuilder.build());
+    }
+
+    @Test
+    public void testEmpty() throws Exception {
+        URIBuilder uribuilder = new URIBuilder();
+        URI result = uribuilder.build();
+        Assert.assertEquals(new URI(""), result);
+    }
+
+    @Test
+    public void testSetUserInfo() throws Exception {
+        URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff", null);
+        URIBuilder uribuilder = new URIBuilder(uri).setUserInfo("user", "password");
+        URI result = uribuilder.build();
+        Assert.assertEquals(new URI("http://user:password@localhost:80/?param=stuff"), result);
+    }
+
+    @Test
+    public void testRemoveParameters() throws Exception {
+        URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff", null);
+        URIBuilder uribuilder = new URIBuilder(uri).removeQuery();
+        URI result = uribuilder.build();
+        Assert.assertEquals(new URI("http://localhost:80/"), result);
+    }
+
+    @Test
+    public void testSetParameter() throws Exception {
+        URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff&blah&blah", null);
+        URIBuilder uribuilder = new URIBuilder(uri).setParameter("param", "some other stuff")
+            .setParameter("blah", "blah");
+        URI result = uribuilder.build();
+        Assert.assertEquals(new URI("http://localhost:80/?param=some+other+stuff&blah=blah"), result);
+    }
+
+    @Test
+    public void testParameterWithSpecialChar() throws Exception {
+        URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff", null);
+        URIBuilder uribuilder = new URIBuilder(uri).addParameter("param", "1 + 1 = 2")
+            .addParameter("param", "blah&blah");
+        URI result = uribuilder.build();
+        Assert.assertEquals(new URI("http://localhost:80/?param=stuff&param=1+%2B+1+%3D+2&" +
+                "param=blah%26blah"), result);
+    }
+
+    @Test
+    public void testAddParameter() throws Exception {
+        URI uri = new URI("http", null, "localhost", 80, "/", "param=stuff&blah&blah", null);
+        URIBuilder uribuilder = new URIBuilder(uri).addParameter("param", "some other stuff")
+            .addParameter("blah", "blah");
+        URI result = uribuilder.build();
+        Assert.assertEquals(new URI("http://localhost:80/?param=stuff&blah&blah&" +
+                "param=some+other+stuff&blah=blah"), result);
+    }
+
+    @Test
+    public void testQueryEncoding() throws Exception {
+        URI uri1 = new URI("https://somehost.com/stuff?client_id=1234567890" +
+                "&redirect_uri=https%3A%2F%2Fsomehost.com%2Fblah+blah%2F");
+        URI uri2 = new URIBuilder("https://somehost.com/stuff")
+            .addParameter("client_id","1234567890")
+            .addParameter("redirect_uri","https://somehost.com/blah blah/").build();
+        Assert.assertEquals(uri1, uri2);
+    }
+
+    @Test
+    public void testPathEncoding() throws Exception {
+        URI uri1 = new URI("https://somehost.com/some%20path%20with%20blanks/");
+        URI uri2 = new URIBuilder()
+            .setScheme("https")
+            .setHost("somehost.com")
+            .setPath("/some path with blanks/")
+            .build();
+        Assert.assertEquals(uri1, uri2);
+    }
+
+    @Test
+    public void testAgainstURI() throws Exception {
+        // Check that the URI generated by URI builder agrees with that generated by using URI directly
+        final String scheme="https";
+        final String host="localhost";
+        final String specials="/abcd!$&*()_-+.,=:;'~@[]?<>|#^%\"{}\\£`¬¦xyz"; // N.B. excludes space
+        URI uri = new URI(scheme, specials, host, 80, specials, specials, specials);
+
+        URI bld = new URIBuilder()
+                .setScheme(scheme)
+                .setHost(host)
+                .setUserInfo(specials)
+                .setPath(specials)
+                .addParameter(specials, null) // hack to bypass parsing of query data
+                .setFragment(specials)
+                .build();
+
+        Assert.assertEquals(uri.getHost(), bld.getHost());
+        
+        Assert.assertEquals(uri.getUserInfo(), bld.getUserInfo());
+        
+        Assert.assertEquals(uri.getPath(), bld.getPath());
+
+        Assert.assertEquals(uri.getQuery(), bld.getQuery());
+
+        Assert.assertEquals(uri.getFragment(), bld.getFragment());
+
+    }
+
+    @Test
+    public void testAgainstURIEncoded() throws Exception {
+        // Check that the encoded URI generated by URI builder agrees with that generated by using URI directly
+        final String scheme="https";
+        final String host="localhost";
+        final String specials="/ abcd!$&*()_-+.,=:;'~<>/@[]|#^%\"{}\\`xyz"; // N.B. excludes £¬¦
+        final String formdatasafe = "abcd-_.*zyz";
+        URI uri = new URI(scheme, specials, host, 80, specials,
+                          formdatasafe, // TODO replace with specials when supported
+                          specials);
+
+        URI bld = new URIBuilder()
+                .setScheme(scheme)
+                .setHost(host)
+                .setUserInfo(specials)
+                .setPath(specials)
+                .addParameter(formdatasafe, null) // TODO replace with specials when supported
+                .setFragment(specials)
+                .build();
+
+        Assert.assertEquals(uri.getHost(), bld.getHost());
+        
+        Assert.assertEquals(uri.getRawUserInfo(), bld.getRawUserInfo());
+        
+        Assert.assertEquals(uri.getRawPath(), bld.getRawPath());
+
+        Assert.assertEquals(uri.getRawQuery(), bld.getRawQuery());
+
+        Assert.assertEquals(uri.getRawFragment(), bld.getRawFragment());
+
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java b/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java
index 29a3c24..73debe0 100644
--- a/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java
+++ b/httpclient/src/test/java/org/apache/http/client/utils/TestURIUtils.java
@@ -40,310 +40,156 @@ public class TestURIUtils {
     private URI baseURI = URI.create("http://a/b/c/d;p?q");
 
     @Test
-    public void testRewite00() throws Exception {
-        URI uri = URI.create("http://thishost/stuff");
+    public void testRewrite() throws Exception {
         HttpHost target = new HttpHost("thathost", -1);
-        Assert.assertEquals("http://thathost/stuff", URIUtils.rewriteURI(uri, target).toString());
-    }
-
-    @Test
-    public void testRewite01() throws Exception {
-        URI uri = URI.create("http://thishost/stuff");
-        Assert.assertEquals("/stuff", URIUtils.rewriteURI(uri, null).toString());
-    }
-
-    @Test
-    public void testRewite02() throws Exception {
-        URI uri = URI.create("http://thishost//");
-        Assert.assertEquals("/", URIUtils.rewriteURI(uri, null).toString());
-    }
-
-    @Test
-    public void testRewite03() throws Exception {
-        URI uri = URI.create("http://thishost//stuff///morestuff");
-        Assert.assertEquals("/stuff///morestuff", URIUtils.rewriteURI(uri, null).toString());
-    }
-
-    @Test
-    public void testRewite04() throws Exception {
-        URI uri = URI.create("http://thishost/stuff#crap");
-        HttpHost target = new HttpHost("thathost", -1);
-        Assert.assertEquals("http://thathost/stuff", URIUtils.rewriteURI(uri, target, true).toString());
-    }
-
-    @Test
-    public void testRewite05() throws Exception {
-        URI uri = URI.create("http://thishost/stuff#crap");
-        HttpHost target = new HttpHost("thathost", -1);
-        Assert.assertEquals("http://thathost/stuff#crap", URIUtils.rewriteURI(uri, target, false).toString());
-    }
-
-    @Test
-    public void testRewite06() throws Exception {
-        URI uri = URI.create("http://thishost//////////////stuff/");
-        Assert.assertEquals("/stuff/", URIUtils.rewriteURI(uri, null).toString());
-    }
-
-    @Test
-    public void testResolve00() {
+        Assert.assertEquals("http://thathost/stuff", URIUtils.rewriteURI(
+                URI.create("http://thishost/stuff"), target).toString());
+        Assert.assertEquals("/stuff", URIUtils.rewriteURI(
+                URI.create("http://thishost/stuff"), null).toString());
+        Assert.assertEquals("/", URIUtils.rewriteURI(
+                URI.create("http://thishost//"), null).toString());
+        Assert.assertEquals("/stuff///morestuff", URIUtils.rewriteURI(
+                URI.create("http://thishost//stuff///morestuff"), null).toString());
+        Assert.assertEquals("http://thathost/stuff", URIUtils.rewriteURI(
+                URI.create("http://thishost/stuff#crap"), target, true).toString());
+        Assert.assertEquals("http://thathost/stuff#crap", URIUtils.rewriteURI(
+                URI.create("http://thishost/stuff#crap"), target, false).toString());
+        Assert.assertEquals("/stuff/", URIUtils.rewriteURI(
+                URI.create("http://thishost//////////////stuff/"), null).toString());
+        Assert.assertEquals("http://thathost/stuff", URIUtils.rewriteURI(
+                URI.create("http://thathost/stuff")).toString());
+        Assert.assertEquals("http://thathost/stuff", URIUtils.rewriteURI(
+                URI.create("http://thathost/stuff#fragment")).toString());
+        Assert.assertEquals("http://thathost/stuff", URIUtils.rewriteURI(
+                URI.create("http://userinfo@thathost/stuff#fragment")).toString());
+    }
+
+    @Test
+    public void testRewritePort() throws Exception {
+        HttpHost target = new HttpHost("thathost", 8080); // port should be copied
+        Assert.assertEquals("http://thathost:8080/stuff", URIUtils.rewriteURI(
+                URI.create("http://thishost:80/stuff#crap"), target, true).toString());
+        Assert.assertEquals("http://thathost:8080/stuff#crap", URIUtils.rewriteURI(
+                URI.create("http://thishost:80/stuff#crap"), target, false).toString());
+        target = new HttpHost("thathost", -1); // input port should be dropped
+        Assert.assertEquals("http://thathost/stuff", URIUtils.rewriteURI(
+                URI.create("http://thishost:80/stuff#crap"), target, true).toString());
+        Assert.assertEquals("http://thathost/stuff#crap", URIUtils.rewriteURI(
+                URI.create("http://thishost:80/stuff#crap"), target, false).toString());
+    }
+
+    @Test
+    public void testRewriteScheme() throws Exception {
+        HttpHost target = new HttpHost("thathost", -1, "file"); // scheme should be copied
+        Assert.assertEquals("file://thathost/stuff", URIUtils.rewriteURI(
+                URI.create("http://thishost:80/stuff#crap"), target, true).toString());
+    }
+
+    @Test
+    public void testResolve() {
         Assert.assertEquals("g:h", URIUtils.resolve(this.baseURI, "g:h").toString());
-    }
-
-    @Test
-    public void testResolve01() {
         Assert.assertEquals("http://a/b/c/g", URIUtils.resolve(this.baseURI, "g").toString());
-    }
-
-    @Test
-    public void testResolve02() {
         Assert.assertEquals("http://a/b/c/g", URIUtils.resolve(this.baseURI, "./g").toString());
-    }
-
-    @Test
-    public void testResolve03() {
         Assert.assertEquals("http://a/b/c/g/", URIUtils.resolve(this.baseURI, "g/").toString());
-    }
-
-    @Test
-    public void testResolve04() {
         Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "/g").toString());
-    }
-
-    @Test
-    public void testResolve05() {
         Assert.assertEquals("http://g", URIUtils.resolve(this.baseURI, "//g").toString());
-    }
-
-    @Test
-    public void testResolve06() {
         Assert.assertEquals("http://a/b/c/d;p?y", URIUtils.resolve(this.baseURI, "?y").toString());
-    }
-
-    @Test
-    public void testResolve06_() {
         Assert.assertEquals("http://a/b/c/d;p?y#f", URIUtils.resolve(this.baseURI, "?y#f")
                 .toString());
-    }
-
-    @Test
-    public void testResolve07() {
         Assert.assertEquals("http://a/b/c/g?y", URIUtils.resolve(this.baseURI, "g?y").toString());
-    }
-
-    @Test
-    public void testResolve08() {
         Assert.assertEquals("http://a/b/c/d;p?q#s", URIUtils.resolve(this.baseURI, "#s")
                 .toString());
-    }
-
-    @Test
-    public void testResolve09() {
         Assert.assertEquals("http://a/b/c/g#s", URIUtils.resolve(this.baseURI, "g#s").toString());
-    }
-
-    @Test
-    public void testResolve10() {
         Assert.assertEquals("http://a/b/c/g?y#s", URIUtils.resolve(this.baseURI, "g?y#s")
                 .toString());
-    }
-
-    @Test
-    public void testResolve11() {
         Assert.assertEquals("http://a/b/c/;x", URIUtils.resolve(this.baseURI, ";x").toString());
-    }
-
-    @Test
-    public void testResolve12() {
         Assert.assertEquals("http://a/b/c/g;x", URIUtils.resolve(this.baseURI, "g;x").toString());
-    }
-
-    @Test
-    public void testResolve13() {
         Assert.assertEquals("http://a/b/c/g;x?y#s", URIUtils.resolve(this.baseURI, "g;x?y#s")
                 .toString());
-    }
-
-    @Test
-    public void testResolve14() {
         Assert.assertEquals("http://a/b/c/d;p?q", URIUtils.resolve(this.baseURI, "").toString());
-    }
-
-    @Test
-    public void testResolve15() {
         Assert.assertEquals("http://a/b/c/", URIUtils.resolve(this.baseURI, ".").toString());
-    }
-
-    @Test
-    public void testResolve16() {
         Assert.assertEquals("http://a/b/c/", URIUtils.resolve(this.baseURI, "./").toString());
-    }
-
-    @Test
-    public void testResolve17() {
         Assert.assertEquals("http://a/b/", URIUtils.resolve(this.baseURI, "..").toString());
-    }
-
-    @Test
-    public void testResolve18() {
         Assert.assertEquals("http://a/b/", URIUtils.resolve(this.baseURI, "../").toString());
-    }
-
-    @Test
-    public void testResolve19() {
         Assert.assertEquals("http://a/b/g", URIUtils.resolve(this.baseURI, "../g").toString());
-    }
-
-    @Test
-    public void testResolve20() {
         Assert.assertEquals("http://a/", URIUtils.resolve(this.baseURI, "../..").toString());
-    }
-
-    @Test
-    public void testResolve21() {
         Assert.assertEquals("http://a/", URIUtils.resolve(this.baseURI, "../../").toString());
-    }
-
-    @Test
-    public void testResolve22() {
         Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "../../g").toString());
-    }
-
-    @Test
-    public void testResolveAbnormal23() {
         Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "../../../g").toString());
-    }
-
-    @Test
-    public void testResolveAbnormal24() {
         Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "../../../../g")
                 .toString());
-    }
-
-    @Test
-    public void testResolve25() {
         Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "/./g").toString());
-    }
-
-    @Test
-    public void testResolve26() {
         Assert.assertEquals("http://a/g", URIUtils.resolve(this.baseURI, "/../g").toString());
-    }
-
-    @Test
-    public void testResolve27() {
         Assert.assertEquals("http://a/b/c/g.", URIUtils.resolve(this.baseURI, "g.").toString());
-    }
-
-    @Test
-    public void testResolve28() {
         Assert.assertEquals("http://a/b/c/.g", URIUtils.resolve(this.baseURI, ".g").toString());
-    }
-
-    @Test
-    public void testResolve29() {
         Assert.assertEquals("http://a/b/c/g..", URIUtils.resolve(this.baseURI, "g..").toString());
-    }
-
-    @Test
-    public void testResolve30() {
         Assert.assertEquals("http://a/b/c/..g", URIUtils.resolve(this.baseURI, "..g").toString());
-    }
-
-    @Test
-    public void testResolve31() {
         Assert.assertEquals("http://a/b/g", URIUtils.resolve(this.baseURI, "./../g").toString());
-    }
-
-    @Test
-    public void testResolve32() {
         Assert.assertEquals("http://a/b/c/g/", URIUtils.resolve(this.baseURI, "./g/.").toString());
-    }
-
-    @Test
-    public void testResolve33() {
         Assert.assertEquals("http://a/b/c/g/h", URIUtils.resolve(this.baseURI, "g/./h").toString());
-    }
-
-    @Test
-    public void testResolve34() {
         Assert.assertEquals("http://a/b/c/h", URIUtils.resolve(this.baseURI, "g/../h").toString());
-    }
-
-    @Test
-    public void testResolve35() {
         Assert.assertEquals("http://a/b/c/g;x=1/y", URIUtils.resolve(this.baseURI, "g;x=1/./y")
                 .toString());
-    }
-
-    @Test
-    public void testResolve36() {
         Assert.assertEquals("http://a/b/c/y", URIUtils.resolve(this.baseURI, "g;x=1/../y")
                 .toString());
-    }
-
-    @Test
-    public void testResolve37() {
         Assert.assertEquals("http://a/b/c/g?y/./x", URIUtils.resolve(this.baseURI, "g?y/./x")
                 .toString());
-    }
-
-    @Test
-    public void testResolve38() {
         Assert.assertEquals("http://a/b/c/g?y/../x", URIUtils.resolve(this.baseURI, "g?y/../x")
                 .toString());
-    }
-
-    @Test
-    public void testResolve39() {
         Assert.assertEquals("http://a/b/c/g#s/./x", URIUtils.resolve(this.baseURI, "g#s/./x")
                 .toString());
-    }
-
-    @Test
-    public void testResolve40() {
         Assert.assertEquals("http://a/b/c/g#s/../x", URIUtils.resolve(this.baseURI, "g#s/../x")
                 .toString());
-    }
-
-    @Test
-    public void testResolve41() {
         Assert.assertEquals("http:g", URIUtils.resolve(this.baseURI, "http:g").toString());
-    }
-
-    // examples from section 5.2.4
-    @Test
-    public void testResolve42() {
+        // examples from section 5.2.4
         Assert.assertEquals("http://s/a/g", URIUtils.resolve(this.baseURI,
                 "http://s/a/b/c/./../../g").toString());
-    }
-
-    @Test
-    public void testResolve43() {
         Assert.assertEquals("http://s/mid/6", URIUtils.resolve(this.baseURI,
                 "http://s/mid/content=5/../6").toString());
     }
 
     @Test
-    public void testHTTPCLIENT_911() throws Exception{
-        Assert.assertEquals(new HttpHost("localhost"),URIUtils.extractHost(new URI("http://localhost/abcd")));
-        Assert.assertEquals(new HttpHost("localhost"),URIUtils.extractHost(new URI("http://localhost/abcd%3A")));
-        
-        Assert.assertEquals(new HttpHost("local_host"),URIUtils.extractHost(new URI("http://local_host/abcd")));
-        Assert.assertEquals(new HttpHost("local_host"),URIUtils.extractHost(new URI("http://local_host/abcd%3A")));
-        
-        Assert.assertEquals(new HttpHost("localhost",8),URIUtils.extractHost(new URI("http://localhost:8/abcd")));
-        Assert.assertEquals(new HttpHost("local_host",8),URIUtils.extractHost(new URI("http://local_host:8/abcd")));
+    public void testExtractHost() throws Exception {
+        Assert.assertEquals(new HttpHost("localhost"),
+                URIUtils.extractHost(new URI("http://localhost/abcd")));
+        Assert.assertEquals(new HttpHost("localhost"),
+                URIUtils.extractHost(new URI("http://localhost/abcd%3A")));
+
+        Assert.assertEquals(new HttpHost("local_host"),
+                URIUtils.extractHost(new URI("http://local_host/abcd")));
+        Assert.assertEquals(new HttpHost("local_host"),
+                URIUtils.extractHost(new URI("http://local_host/abcd%3A")));
+
+        Assert.assertEquals(new HttpHost("localhost",8),
+                URIUtils.extractHost(new URI("http://localhost:8/abcd")));
+        Assert.assertEquals(new HttpHost("local_host",8),
+                URIUtils.extractHost(new URI("http://local_host:8/abcd")));
 
         // URI seems to OK with missing port number
-        Assert.assertEquals(new HttpHost("localhost"),URIUtils.extractHost(new URI("http://localhost:/abcd")));
-        Assert.assertEquals(new HttpHost("local_host"),URIUtils.extractHost(new URI("http://local_host:/abcd")));
+        Assert.assertEquals(new HttpHost("localhost",-1),URIUtils.extractHost(
+                new URI("http://localhost:/abcd")));
+        Assert.assertEquals(new HttpHost("local_host",-1),URIUtils.extractHost(
+                new URI("http://local_host:/abcd")));
 
-        Assert.assertEquals(new HttpHost("localhost",8080),URIUtils.extractHost(new URI("http://user:pass@localhost:8080/abcd")));
-        Assert.assertEquals(new HttpHost("local_host",8080),URIUtils.extractHost(new URI("http://user:pass@local_host:8080/abcd")));
+        Assert.assertEquals(new HttpHost("localhost",8080),
+                URIUtils.extractHost(new URI("http://user:pass@localhost:8080/abcd")));
+        Assert.assertEquals(new HttpHost("local_host",8080),
+                URIUtils.extractHost(new URI("http://user:pass@local_host:8080/abcd")));
 
-        Assert.assertEquals(new HttpHost("localhost",8080),URIUtils.extractHost(new URI("http://@localhost:8080/abcd")));
-        Assert.assertEquals(new HttpHost("local_host",8080),URIUtils.extractHost(new URI("http://@local_host:8080/abcd")));
+        Assert.assertEquals(new HttpHost("localhost",8080),URIUtils.extractHost(
+                new URI("http://@localhost:8080/abcd")));
+        Assert.assertEquals(new HttpHost("local_host",8080),URIUtils.extractHost(
+                new URI("http://@local_host:8080/abcd")));
 
+        Assert.assertEquals(new HttpHost("[2a00:1450:400c:c01::69]",8080),
+                URIUtils.extractHost(new URI("http://[2a00:1450:400c:c01::69]:8080/")));
+
+        Assert.assertEquals(new HttpHost("localhost",8080),
+                URIUtils.extractHost(new URI("http://localhost:8080/;sessionid=stuff/abcd")));
+        Assert.assertEquals(new HttpHost("localhost",8080),
+                URIUtils.extractHost(new URI("http://localhost:8080;sessionid=stuff/abcd")));
+        Assert.assertEquals(new HttpHost("localhost",-1),
+                URIUtils.extractHost(new URI("http://localhost:;sessionid=stuff/abcd")));
     }
-    
+
 }
diff --git a/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java b/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java
index 00c035b..2934b5d 100644
--- a/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java
+++ b/httpclient/src/test/java/org/apache/http/client/utils/TestURLEncodedUtils.java
@@ -31,7 +31,9 @@ import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.http.Consts;
 import org.apache.http.NameValuePair;
+import org.apache.http.entity.ContentType;
 import org.apache.http.entity.StringEntity;
 import org.apache.http.message.BasicNameValuePair;
 import org.apache.http.protocol.HTTP;
@@ -41,55 +43,135 @@ import org.junit.Test;
 public class TestURLEncodedUtils {
 
     @Test
-    public void testParseURI () throws Exception {
+    public void testParseURLCodedContent () throws Exception {
         List <NameValuePair> result;
 
-        result = parse("", null);
+        result = parse("");
         Assert.assertTrue(result.isEmpty());
 
-        result = parse("Name1=Value1", null);
+        result = parse("Name0");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "Name0", null);
+
+        result = parse("Name1=Value1");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "Name1", "Value1");
+
+        result = parse("Name2=");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "Name2", "");
+
+        result = parse("Name3");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "Name3", null);
+
+        result = parse("Name4=Value%204%21");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "Name4", "Value 4!");
+
+        result = parse("Name4=Value%2B4%21");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "Name4", "Value+4!");
+
+        result = parse("Name4=Value%204%21%20%214");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "Name4", "Value 4! !4");
+
+        result = parse("Name5=aaa&Name6=bbb");
+        Assert.assertEquals(2, result.size());
+        assertNameValuePair(result.get(0), "Name5", "aaa");
+        assertNameValuePair(result.get(1), "Name6", "bbb");
+
+        result = parse("Name7=aaa&Name7=b%2Cb&Name7=ccc");
+        Assert.assertEquals(3, result.size());
+        assertNameValuePair(result.get(0), "Name7", "aaa");
+        assertNameValuePair(result.get(1), "Name7", "b,b");
+        assertNameValuePair(result.get(2), "Name7", "ccc");
+
+        result = parse("Name8=xx%2C%20%20yy%20%20%2Czz");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "Name8", "xx,  yy  ,zz");
+
+        result = parse("price=10%20%E2%82%AC");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "price", "10 \u20AC");
+    }
+
+    @Test
+    public void testParseURLCodedContentString () throws Exception {
+        List <NameValuePair> result;
+
+        result = parseString("");
+        Assert.assertTrue(result.isEmpty());
+
+        result = parseString("Name0");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "Name0", null);
+
+        result = parseString("Name1=Value1");
         Assert.assertEquals(1, result.size());
         assertNameValuePair(result.get(0), "Name1", "Value1");
 
-        result = parse("Name2=", null);
+        result = parseString("Name2=");
         Assert.assertEquals(1, result.size());
-        assertNameValuePair(result.get(0), "Name2", null);
+        assertNameValuePair(result.get(0), "Name2", "");
 
-        result = parse("Name3", null);
+        result = parseString("Name3");
         Assert.assertEquals(1, result.size());
         assertNameValuePair(result.get(0), "Name3", null);
 
-        result = parse("Name4=Value+4%21", null);
+        result = parseString("Name4=Value%204%21");
         Assert.assertEquals(1, result.size());
         assertNameValuePair(result.get(0), "Name4", "Value 4!");
 
-        result = parse("Name4=Value%2B4%21", null);
+        result = parseString("Name4=Value%2B4%21");
         Assert.assertEquals(1, result.size());
         assertNameValuePair(result.get(0), "Name4", "Value+4!");
 
-        result = parse("Name4=Value+4%21+%214", null);
+        result = parseString("Name4=Value%204%21%20%214");
         Assert.assertEquals(1, result.size());
         assertNameValuePair(result.get(0), "Name4", "Value 4! !4");
 
-        result = parse("Name5=aaa&Name6=bbb", null);
+        result = parseString("Name5=aaa&Name6=bbb");
         Assert.assertEquals(2, result.size());
         assertNameValuePair(result.get(0), "Name5", "aaa");
         assertNameValuePair(result.get(1), "Name6", "bbb");
 
-        result = parse("Name7=aaa&Name7=b%2Cb&Name7=ccc", null);
+        result = parseString("Name7=aaa&Name7=b%2Cb&Name7=ccc");
         Assert.assertEquals(3, result.size());
         assertNameValuePair(result.get(0), "Name7", "aaa");
         assertNameValuePair(result.get(1), "Name7", "b,b");
         assertNameValuePair(result.get(2), "Name7", "ccc");
 
-        result = parse("Name8=xx%2C++yy++%2Czz", null);
+        result = parseString("Name8=xx%2C%20%20yy%20%20%2Czz");
         Assert.assertEquals(1, result.size());
         assertNameValuePair(result.get(0), "Name8", "xx,  yy  ,zz");
+
+        result = parseString("price=10%20%E2%82%AC");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "price", "10 \u20AC");
+    }
+
+    @Test
+    public void testParseInvalidURLCodedContent () throws Exception {
+        List <NameValuePair> result;
+
+        result = parse("name=%");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "name", "%");
+
+        result = parse("name=%a");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "name", "%a");
+
+        result = parse("name=%wa%20");
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "name", "%wa ");
     }
 
     @Test
     public void testParseEntity () throws Exception {
-        final StringEntity entity = new StringEntity("Name1=Value1", null);
+        final StringEntity entity = new StringEntity("Name1=Value1");
 
         entity.setContentType(URLEncodedUtils.CONTENT_TYPE);
         final List <NameValuePair> result = URLEncodedUtils.parse(entity);
@@ -127,13 +209,13 @@ public class TestURLEncodedUtils {
         parameters.add(new BasicNameValuePair("russian", ru_hello));
         parameters.add(new BasicNameValuePair("swiss", ch_hello));
 
-        String s = URLEncodedUtils.format(parameters, HTTP.UTF_8);
+        String s = URLEncodedUtils.format(parameters, Consts.UTF_8);
 
         Assert.assertEquals("russian=%D0%92%D1%81%D0%B5%D0%BC_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82" +
                 "&swiss=Gr%C3%BCezi_z%C3%A4m%C3%A4", s);
 
-        StringEntity entity = new StringEntity(s, HTTP.UTF_8);
-        entity.setContentType(URLEncodedUtils.CONTENT_TYPE + HTTP.CHARSET_PARAM + HTTP.UTF_8);
+        StringEntity entity = new StringEntity(s, ContentType.create(
+                URLEncodedUtils.CONTENT_TYPE, Consts.UTF_8));
         List <NameValuePair> result = URLEncodedUtils.parse(entity);
         Assert.assertEquals(2, result.size());
         assertNameValuePair(result.get(0), "russian", ru_hello);
@@ -141,8 +223,44 @@ public class TestURLEncodedUtils {
     }
 
     @Test
+    public void testParseUTF8String () throws Exception {
+        String ru_hello = constructString(RUSSIAN_HELLO);
+        String ch_hello = constructString(SWISS_GERMAN_HELLO);
+        List <NameValuePair> parameters = new ArrayList<NameValuePair>();
+        parameters.add(new BasicNameValuePair("russian", ru_hello));
+        parameters.add(new BasicNameValuePair("swiss", ch_hello));
+
+        String s = URLEncodedUtils.format(parameters, Consts.UTF_8);
+
+        List <NameValuePair> result = URLEncodedUtils.parse(s, Consts.UTF_8);
+        Assert.assertEquals(2, result.size());
+        assertNameValuePair(result.get(0), "russian", ru_hello);
+        assertNameValuePair(result.get(1), "swiss", ch_hello);
+    }
+
+    @Test
+    public void testParseEntityDefaultContentType () throws Exception {
+        String ch_hello = constructString(SWISS_GERMAN_HELLO);
+        String us_hello = "hi there";
+        List <NameValuePair> parameters = new ArrayList<NameValuePair>();
+        parameters.add(new BasicNameValuePair("english", us_hello));
+        parameters.add(new BasicNameValuePair("swiss", ch_hello));
+
+        String s = URLEncodedUtils.format(parameters, HTTP.DEF_CONTENT_CHARSET);
+
+        Assert.assertEquals("english=hi+there&swiss=Gr%FCezi_z%E4m%E4", s);
+
+        StringEntity entity = new StringEntity(s, ContentType.create(
+                URLEncodedUtils.CONTENT_TYPE, HTTP.DEF_CONTENT_CHARSET));
+        List <NameValuePair> result = URLEncodedUtils.parse(entity);
+        Assert.assertEquals(2, result.size());
+        assertNameValuePair(result.get(0), "english", us_hello);
+        assertNameValuePair(result.get(1), "swiss", ch_hello);
+    }
+
+    @Test
     public void testIsEncoded () throws Exception {
-        final StringEntity entity = new StringEntity("...", null);
+        final StringEntity entity = new StringEntity("...");
 
         entity.setContentType(URLEncodedUtils.CONTENT_TYPE);
         Assert.assertTrue(URLEncodedUtils.isEncoded(entity));
@@ -157,46 +275,99 @@ public class TestURLEncodedUtils {
     @Test
     public void testFormat () throws Exception {
         final List <NameValuePair> params = new ArrayList <NameValuePair>();
-        Assert.assertEquals(0, URLEncodedUtils.format(params, null).length());
+        Assert.assertEquals(0, URLEncodedUtils.format(params, Consts.ASCII).length());
+
+        params.clear();
+        params.add(new BasicNameValuePair("Name0", null));
+        Assert.assertEquals("Name0", URLEncodedUtils.format(params, Consts.ASCII));
 
         params.clear();
         params.add(new BasicNameValuePair("Name1", "Value1"));
-        Assert.assertEquals("Name1=Value1", URLEncodedUtils.format(params, null));
+        Assert.assertEquals("Name1=Value1", URLEncodedUtils.format(params, Consts.ASCII));
 
         params.clear();
-        params.add(new BasicNameValuePair("Name2", null));
-        Assert.assertEquals("Name2=", URLEncodedUtils.format(params, null));
+        params.add(new BasicNameValuePair("Name2", ""));
+        Assert.assertEquals("Name2=", URLEncodedUtils.format(params, Consts.ASCII));
 
         params.clear();
-        params.add(new BasicNameValuePair("Name4", "Value 4!"));
-        Assert.assertEquals("Name4=Value+4%21", URLEncodedUtils.format(params, null));
+        params.add(new BasicNameValuePair("Name4", "Value 4&"));
+        Assert.assertEquals("Name4=Value+4%26", URLEncodedUtils.format(params, Consts.ASCII));
 
         params.clear();
-        params.add(new BasicNameValuePair("Name4", "Value+4!"));
-        Assert.assertEquals("Name4=Value%2B4%21", URLEncodedUtils.format(params, null));
+        params.add(new BasicNameValuePair("Name4", "Value+4&"));
+        Assert.assertEquals("Name4=Value%2B4%26", URLEncodedUtils.format(params, Consts.ASCII));
 
         params.clear();
-        params.add(new BasicNameValuePair("Name4", "Value 4! !4"));
-        Assert.assertEquals("Name4=Value+4%21+%214", URLEncodedUtils.format(params, null));
+        params.add(new BasicNameValuePair("Name4", "Value 4& =4"));
+        Assert.assertEquals("Name4=Value+4%26+%3D4", URLEncodedUtils.format(params, Consts.ASCII));
 
         params.clear();
         params.add(new BasicNameValuePair("Name5", "aaa"));
         params.add(new BasicNameValuePair("Name6", "bbb"));
-        Assert.assertEquals("Name5=aaa&Name6=bbb", URLEncodedUtils.format(params, null));
+        Assert.assertEquals("Name5=aaa&Name6=bbb", URLEncodedUtils.format(params, Consts.ASCII));
 
         params.clear();
         params.add(new BasicNameValuePair("Name7", "aaa"));
         params.add(new BasicNameValuePair("Name7", "b,b"));
         params.add(new BasicNameValuePair("Name7", "ccc"));
-        Assert.assertEquals("Name7=aaa&Name7=b%2Cb&Name7=ccc", URLEncodedUtils.format(params, null));
+        Assert.assertEquals("Name7=aaa&Name7=b%2Cb&Name7=ccc", URLEncodedUtils.format(params, Consts.ASCII));
 
         params.clear();
         params.add(new BasicNameValuePair("Name8", "xx,  yy  ,zz"));
-        Assert.assertEquals("Name8=xx%2C++yy++%2Czz", URLEncodedUtils.format(params, null));
+        Assert.assertEquals("Name8=xx%2C++yy++%2Czz", URLEncodedUtils.format(params, Consts.ASCII));
+    }
+
+    @Test
+    public void testFormatString() throws Exception { // as above, using String
+        final List <NameValuePair> params = new ArrayList <NameValuePair>();
+        Assert.assertEquals(0, URLEncodedUtils.format(params, "US-ASCII").length());
+
+        params.clear();
+        params.add(new BasicNameValuePair("Name0", null));
+        Assert.assertEquals("Name0", URLEncodedUtils.format(params, "US-ASCII"));
+
+        params.clear();
+        params.add(new BasicNameValuePair("Name1", "Value1"));
+        Assert.assertEquals("Name1=Value1", URLEncodedUtils.format(params, "US-ASCII"));
+
+        params.clear();
+        params.add(new BasicNameValuePair("Name2", ""));
+        Assert.assertEquals("Name2=", URLEncodedUtils.format(params, "US-ASCII"));
+
+        params.clear();
+        params.add(new BasicNameValuePair("Name4", "Value 4&"));
+        Assert.assertEquals("Name4=Value+4%26", URLEncodedUtils.format(params, "US-ASCII"));
+
+        params.clear();
+        params.add(new BasicNameValuePair("Name4", "Value+4&"));
+        Assert.assertEquals("Name4=Value%2B4%26", URLEncodedUtils.format(params, "US-ASCII"));
+
+        params.clear();
+        params.add(new BasicNameValuePair("Name4", "Value 4& =4"));
+        Assert.assertEquals("Name4=Value+4%26+%3D4", URLEncodedUtils.format(params, "US-ASCII"));
+
+        params.clear();
+        params.add(new BasicNameValuePair("Name5", "aaa"));
+        params.add(new BasicNameValuePair("Name6", "bbb"));
+        Assert.assertEquals("Name5=aaa&Name6=bbb", URLEncodedUtils.format(params, "US-ASCII"));
+
+        params.clear();
+        params.add(new BasicNameValuePair("Name7", "aaa"));
+        params.add(new BasicNameValuePair("Name7", "b,b"));
+        params.add(new BasicNameValuePair("Name7", "ccc"));
+        Assert.assertEquals("Name7=aaa&Name7=b%2Cb&Name7=ccc", URLEncodedUtils.format(params, "US-ASCII"));
+
+        params.clear();
+        params.add(new BasicNameValuePair("Name8", "xx,  yy  ,zz"));
+        Assert.assertEquals("Name8=xx%2C++yy++%2Czz", URLEncodedUtils.format(params, "US-ASCII"));
+    }
+
+    private List <NameValuePair> parse (final String params) {
+        return URLEncodedUtils.parse(params, Consts.UTF_8);
     }
 
-    private List <NameValuePair> parse (final String params, final String encoding) {
-        return URLEncodedUtils.parse(URI.create("http://hc.apache.org/params?" + params), encoding);
+    private List <NameValuePair> parseString (final String uri) throws Exception {
+        return URLEncodedUtils.parse(new URI("?"+uri), "UTF-8");
     }
 
     private static void assertNameValuePair (
@@ -207,4 +378,4 @@ public class TestURLEncodedUtils {
         Assert.assertEquals(parameter.getValue(), expectedValue);
     }
 
-}
\ No newline at end of file
+}
diff --git a/httpclient/src/test/java/org/apache/http/conn/TestConnectionAutoRelease.java b/httpclient/src/test/java/org/apache/http/conn/TestConnectionAutoRelease.java
index c77b2f7..3f7c443 100644
--- a/httpclient/src/test/java/org/apache/http/conn/TestConnectionAutoRelease.java
+++ b/httpclient/src/test/java/org/apache/http/conn/TestConnectionAutoRelease.java
@@ -40,12 +40,12 @@ import org.apache.http.HttpResponse;
 import org.apache.http.MalformedChunkCodingException;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.entity.BasicHttpEntity;
 import org.apache.http.impl.DefaultHttpServerConnection;
 import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
 import org.apache.http.localserver.ServerTestBase;
+import org.apache.http.pool.PoolStats;
 import org.apache.http.protocol.ExecutionContext;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.protocol.HttpRequestHandler;
@@ -55,20 +55,15 @@ import org.junit.Test;
 
 public class TestConnectionAutoRelease extends ServerTestBase {
 
-    private ThreadSafeClientConnManager createTSCCM(SchemeRegistry schreg) {
-        if (schreg == null)
-            schreg = supportedSchemes;
-        return new ThreadSafeClientConnManager(schreg);
-    }
-
     @Test
     public void testReleaseOnEntityConsumeContent() throws Exception {
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
         mgr.setDefaultMaxPerRoute(1);
         mgr.setMaxTotal(1);
 
         // Zero connections in the pool
-        Assert.assertEquals(0, mgr.getConnectionsInPool());
+        PoolStats stats = mgr.getTotalStats();
+        Assert.assertEquals(0, stats.getAvailable());
 
         DefaultHttpClient client = new DefaultHttpClient(mgr);
 
@@ -90,7 +85,8 @@ public class TestConnectionAutoRelease extends ServerTestBase {
         EntityUtils.consume(e);
 
         // Expect one connection in the pool
-        Assert.assertEquals(1, mgr.getConnectionsInPool());
+        stats = mgr.getTotalStats();
+        Assert.assertEquals(1, stats.getAvailable());
 
         // Make sure one connection is available
         connreq = mgr.requestConnection(new HttpRoute(target), null);
@@ -103,12 +99,13 @@ public class TestConnectionAutoRelease extends ServerTestBase {
 
     @Test
     public void testReleaseOnEntityWriteTo() throws Exception {
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
         mgr.setDefaultMaxPerRoute(1);
         mgr.setMaxTotal(1);
 
         // Zero connections in the pool
-        Assert.assertEquals(0, mgr.getConnectionsInPool());
+        PoolStats stats = mgr.getTotalStats();
+        Assert.assertEquals(0, stats.getAvailable());
 
         DefaultHttpClient client = new DefaultHttpClient(mgr);
 
@@ -131,7 +128,8 @@ public class TestConnectionAutoRelease extends ServerTestBase {
         e.writeTo(outsteam);
 
         // Expect one connection in the pool
-        Assert.assertEquals(1, mgr.getConnectionsInPool());
+        stats = mgr.getTotalStats();
+        Assert.assertEquals(1, stats.getAvailable());
 
         // Make sure one connection is available
         connreq = mgr.requestConnection(new HttpRoute(target), null);
@@ -144,12 +142,13 @@ public class TestConnectionAutoRelease extends ServerTestBase {
 
     @Test
     public void testReleaseOnAbort() throws Exception {
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
         mgr.setDefaultMaxPerRoute(1);
         mgr.setMaxTotal(1);
 
         // Zero connections in the pool
-        Assert.assertEquals(0, mgr.getConnectionsInPool());
+        PoolStats stats = mgr.getTotalStats();
+        Assert.assertEquals(0, stats.getAvailable());
 
         DefaultHttpClient client = new DefaultHttpClient(mgr);
 
@@ -171,7 +170,7 @@ public class TestConnectionAutoRelease extends ServerTestBase {
         httpget.abort();
 
         // Expect zero connections in the pool
-        Assert.assertEquals(0, mgr.getConnectionsInPool());
+        Assert.assertEquals(0, mgr.getTotalStats().getAvailable());
 
         // Make sure one connection is available
         connreq = mgr.requestConnection(new HttpRoute(target), null);
@@ -217,12 +216,12 @@ public class TestConnectionAutoRelease extends ServerTestBase {
 
         });
 
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
         mgr.setDefaultMaxPerRoute(1);
         mgr.setMaxTotal(1);
 
         // Zero connections in the pool
-        Assert.assertEquals(0, mgr.getConnectionsInPool());
+        Assert.assertEquals(0, mgr.getTotalStats().getAvailable());
 
         DefaultHttpClient client = new DefaultHttpClient(mgr);
 
@@ -250,7 +249,7 @@ public class TestConnectionAutoRelease extends ServerTestBase {
         }
 
         // Expect zero connections in the pool
-        Assert.assertEquals(0, mgr.getConnectionsInPool());
+        Assert.assertEquals(0, mgr.getTotalStats().getAvailable());
 
         // Make sure one connection is available
         connreq = mgr.requestConnection(new HttpRoute(target), null);
diff --git a/httpclient/src/test/java/org/apache/http/conn/TestConnectionReuse.java b/httpclient/src/test/java/org/apache/http/conn/TestConnectionReuse.java
index d7e7522..525c5d2 100644
--- a/httpclient/src/test/java/org/apache/http/conn/TestConnectionReuse.java
+++ b/httpclient/src/test/java/org/apache/http/conn/TestConnectionReuse.java
@@ -44,7 +44,7 @@ import org.apache.http.conn.scheme.Scheme;
 import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.conn.scheme.SchemeSocketFactory;
 import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
 import org.apache.http.localserver.LocalTestServer;
 import org.apache.http.localserver.RandomHandler;
 import org.apache.http.params.BasicHttpParams;
@@ -99,7 +99,7 @@ public class TestConnectionReuse {
         SchemeSocketFactory sf = PlainSocketFactory.getSocketFactory();
         supportedSchemes.register(new Scheme("http", 80, sf));
 
-        ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(supportedSchemes);
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(supportedSchemes);
         mgr.setMaxTotal(5);
         mgr.setDefaultMaxPerRoute(5);
 
@@ -130,7 +130,7 @@ public class TestConnectionReuse {
         }
 
         // Expect some connection in the pool
-        Assert.assertTrue(mgr.getConnectionsInPool() > 0);
+        Assert.assertTrue(mgr.getTotalStats().getAvailable() > 0);
 
         mgr.shutdown();
     }
@@ -170,7 +170,7 @@ public class TestConnectionReuse {
         SchemeSocketFactory sf = PlainSocketFactory.getSocketFactory();
         supportedSchemes.register(new Scheme("http", 80, sf));
 
-        ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(supportedSchemes);
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(supportedSchemes);
         mgr.setMaxTotal(5);
         mgr.setDefaultMaxPerRoute(5);
 
@@ -201,7 +201,7 @@ public class TestConnectionReuse {
         }
 
         // Expect zero connections in the pool
-        Assert.assertEquals(0, mgr.getConnectionsInPool());
+        Assert.assertEquals(0, mgr.getTotalStats().getAvailable());
 
         mgr.shutdown();
     }
@@ -231,7 +231,7 @@ public class TestConnectionReuse {
         SchemeSocketFactory sf = PlainSocketFactory.getSocketFactory();
         supportedSchemes.register(new Scheme("http", 80, sf));
 
-        ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(supportedSchemes);
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(supportedSchemes);
         mgr.setMaxTotal(5);
         mgr.setDefaultMaxPerRoute(5);
 
@@ -262,7 +262,7 @@ public class TestConnectionReuse {
         }
 
         // Expect zero connections in the pool
-        Assert.assertEquals(0, mgr.getConnectionsInPool());
+        Assert.assertEquals(0, mgr.getTotalStats().getAvailable());
 
         mgr.shutdown();
     }
@@ -293,7 +293,7 @@ public class TestConnectionReuse {
         SchemeSocketFactory sf = PlainSocketFactory.getSocketFactory();
         supportedSchemes.register(new Scheme("http", 80, sf));
 
-        ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(supportedSchemes);
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(supportedSchemes);
         mgr.setMaxTotal(1);
         mgr.setDefaultMaxPerRoute(1);
 
@@ -303,13 +303,13 @@ public class TestConnectionReuse {
         HttpResponse response = client.execute(target, new HttpGet("/random/2000"));
         EntityUtils.consume(response.getEntity());
 
-        Assert.assertEquals(1, mgr.getConnectionsInPool());
+        Assert.assertEquals(1, mgr.getTotalStats().getAvailable());
         Assert.assertEquals(1, localServer.getAcceptedConnectionCount());
 
         response = client.execute(target, new HttpGet("/random/2000"));
         EntityUtils.consume(response.getEntity());
 
-        Assert.assertEquals(1, mgr.getConnectionsInPool());
+        Assert.assertEquals(1, mgr.getTotalStats().getAvailable());
         Assert.assertEquals(1, localServer.getAcceptedConnectionCount());
 
         // Now sleep for 1.1 seconds and let the timeout do its work
@@ -317,7 +317,7 @@ public class TestConnectionReuse {
         response = client.execute(target, new HttpGet("/random/2000"));
         EntityUtils.consume(response.getEntity());
 
-        Assert.assertEquals(1, mgr.getConnectionsInPool());
+        Assert.assertEquals(1, mgr.getTotalStats().getAvailable());
         Assert.assertEquals(2, localServer.getAcceptedConnectionCount());
 
         // Do another request just under the 1 second limit & make
@@ -326,7 +326,7 @@ public class TestConnectionReuse {
         response = client.execute(target, new HttpGet("/random/2000"));
         EntityUtils.consume(response.getEntity());
 
-        Assert.assertEquals(1, mgr.getConnectionsInPool());
+        Assert.assertEquals(1, mgr.getTotalStats().getAvailable());
         Assert.assertEquals(2, localServer.getAcceptedConnectionCount());
 
 
diff --git a/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java b/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java
index 41e2026..db8daa7 100644
--- a/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java
+++ b/httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java
@@ -31,6 +31,7 @@ import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.Arrays;
 
 import javax.net.ssl.SSLException;
 
@@ -228,4 +229,109 @@ public class TestHostnameVerifier {
         }
     }
 
+    // Test helper method
+    private void checkMatching(X509HostnameVerifier hv, String host,
+            String[] cns, String[] alts, boolean shouldFail) {
+        try {
+            hv.verify(host, cns, alts);
+            if (shouldFail) {
+                Assert.fail("HostnameVerifier should not allow [" + host + "] to match "
+                        +Arrays.toString(cns)
+                        +" or "
+                        +Arrays.toString(alts));
+            }
+        }
+        catch(SSLException e) {
+            if (!shouldFail) {
+                Assert.fail("HostnameVerifier should have allowed [" + host + "] to match "
+                        +Arrays.toString(cns)
+                        +" or "
+                        +Arrays.toString(alts));
+            }
+        }
+    }
+
+    @Test
+    // Check standard wildcard matching
+    public void testMatching() {
+        String cns[] = {};
+        String alt[] = {};
+        X509HostnameVerifier bhv = new BrowserCompatHostnameVerifier();
+        X509HostnameVerifier shv = new StrictHostnameVerifier();
+        checkMatching(bhv, "a.b.c", cns, alt, true); // empty
+        checkMatching(shv, "a.b.c", cns, alt, true); // empty
+
+        cns = new String []{"*.b.c"};
+        checkMatching(bhv, "a.b.c", cns, alt, false); // OK
+        checkMatching(shv, "a.b.c", cns, alt, false); // OK
+
+        checkMatching(bhv, "s.a.b.c", cns, alt, false); // OK
+        checkMatching(shv, "s.a.b.c", cns, alt, true); // subdomain not OK
+
+        cns = new String []{};
+        alt = new String []{"dummy", "*.b.c"}; // check matches against all alts
+        checkMatching(bhv, "a.b.c", cns, alt, false); // OK
+        checkMatching(shv, "a.b.c", cns, alt, false); // OK
+
+        checkMatching(bhv, "s.a.b.c", cns, alt, false); // OK
+        checkMatching(shv, "s.a.b.c", cns, alt, true); // subdomain not OK
+
+        alt = new String []{"*.gov.uk"};
+        checkMatching(bhv, "a.gov.uk", cns, alt, true); // Bad 2TLD
+        checkMatching(shv, "a.gov.uk", cns, alt, true); // Bad 2TLD
+
+        checkMatching(bhv, "s.a.gov.uk", cns, alt, true); // Bad 2TLD
+        checkMatching(shv, "s.a.gov.uk", cns, alt, true); // Bad 2TLD/no subdomain allowed
+
+        alt = new String []{"*.gov.com"};
+        checkMatching(bhv, "a.gov.com", cns, alt, false); // OK, gov not 2TLD here
+        checkMatching(shv, "a.gov.com", cns, alt, false); // OK, gov not 2TLD here
+
+        checkMatching(bhv, "s.a.gov.com", cns, alt, false); // OK, gov not 2TLD here
+        checkMatching(shv, "s.a.gov.com", cns, alt, true); // no subdomain allowed
+
+        cns = new String []{"a*.gov.uk"}; // 2TLD check applies to wildcards
+        checkMatching(bhv, "a.gov.uk", cns, alt, true); // Bad 2TLD
+        checkMatching(shv, "a.gov.uk", cns, alt, true); // Bad 2TLD
+
+        checkMatching(bhv, "s.a.gov.uk", cns, alt, true); // Bad 2TLD
+        checkMatching(shv, "s.a.gov.uk", cns, alt, true); // Bad 2TLD/no subdomain allowed
+
+    }
+
+    @Test
+    public void HTTPCLIENT_1097() {
+        String cns[];
+        String alt[] = {};
+        X509HostnameVerifier bhv = new BrowserCompatHostnameVerifier();
+        X509HostnameVerifier shv = new StrictHostnameVerifier();
+
+        cns = new String []{"a*.b.c"}; // component part
+        checkMatching(bhv, "a.b.c", cns, alt, false); // OK
+        checkMatching(shv, "a.b.c", cns, alt, false); // OK
+
+        checkMatching(bhv, "a.a.b.c", cns, alt, false); // OK
+        checkMatching(shv, "a.a.b.c", cns, alt, true); // subdomain not OK
+
+        checkWildcard("s*.co.uk", false); // 2 character TLD, invalid 2TLD
+        checkWildcard("s*.gov.uk", false); // 2 character TLD, invalid 2TLD
+        checkWildcard("s*.gouv.uk", false); // 2 character TLD, invalid 2TLD
+    }
+
+    // Helper
+    private void checkWildcard(String host, boolean isOK) {
+        Assert.assertTrue(host+" should be "+isOK, isOK==AbstractVerifier.acceptableCountryWildcard(host));
+    }
+
+    @Test
+    // Various checks of 2TLDs
+    public void testacceptableCountryWildcards() {
+        checkWildcard("*.co.org", true); // Not a 2 character TLD
+        checkWildcard("s*.co.org", true); // Not a 2 character TLD
+        checkWildcard("*.co.uk", false); // 2 character TLD, invalid 2TLD
+        checkWildcard("*.gov.uk", false); // 2 character TLD, invalid 2TLD
+        checkWildcard("*.gouv.uk", false); // 2 character TLD, invalid 2TLD
+        checkWildcard("*.a.co.uk", true); // 2 character TLD, invalid 2TLD, but using subdomain
+        checkWildcard("s*.a.co.uk", true); // 2 character TLD, invalid 2TLD, but using subdomain
+    }
 }
diff --git a/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLSocketFactory.java b/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLSocketFactory.java
index 31af6d9..3ffdf4a 100644
--- a/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLSocketFactory.java
+++ b/httpclient/src/test/java/org/apache/http/conn/ssl/TestSSLSocketFactory.java
@@ -108,6 +108,7 @@ public class TestSSLSocketFactory extends BasicServerTestBase {
         this.localServer.registerDefaultHandlers();
 
         this.localServer.start();
+        this.httpclient = new DefaultHttpClient();
     }
 
     @Override
@@ -150,12 +151,11 @@ public class TestSSLSocketFactory extends BasicServerTestBase {
         SSLSocketFactory socketFactory = new SSLSocketFactory(this.clientSSLContext, hostVerifier);
         Scheme https = new Scheme("https", 443, socketFactory);
 
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.getConnectionManager().getSchemeRegistry().register(https);
+        this.httpclient.getConnectionManager().getSchemeRegistry().register(https);
 
         HttpHost target = getServerHttp();
         HttpGet httpget = new HttpGet("/random/100");
-        HttpResponse response = httpclient.execute(target, httpget);
+        HttpResponse response = this.httpclient.execute(target, httpget);
         Assert.assertEquals(200, response.getStatusLine().getStatusCode());
         Assert.assertTrue(hostVerifier.isFired());
     }
@@ -170,12 +170,11 @@ public class TestSSLSocketFactory extends BasicServerTestBase {
                 SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
 
         Scheme https = new Scheme("https", 443, socketFactory);
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.getConnectionManager().getSchemeRegistry().register(https);
+        this.httpclient.getConnectionManager().getSchemeRegistry().register(https);
 
         HttpHost target = getServerHttp();
         HttpGet httpget = new HttpGet("/random/100");
-        httpclient.execute(target, httpget);
+        this.httpclient.execute(target, httpget);
     }
 
     @Test
@@ -194,12 +193,11 @@ public class TestSSLSocketFactory extends BasicServerTestBase {
         }, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
 
         Scheme https = new Scheme("https", 443, socketFactory);
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.getConnectionManager().getSchemeRegistry().register(https);
+        this.httpclient.getConnectionManager().getSchemeRegistry().register(https);
 
         HttpHost target = getServerHttp();
         HttpGet httpget = new HttpGet("/random/100");
-        HttpResponse response = httpclient.execute(target, httpget);
+        HttpResponse response = this.httpclient.execute(target, httpget);
         Assert.assertEquals(200, response.getStatusLine().getStatusCode());
     }
 
diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestBasicScheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestBasicScheme.java
index dbfe182..96f1029 100644
--- a/httpclient/src/test/java/org/apache/http/impl/auth/TestBasicScheme.java
+++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestBasicScheme.java
@@ -35,6 +35,8 @@ import org.apache.http.auth.MalformedChallengeException;
 import org.apache.http.auth.UsernamePasswordCredentials;
 import org.apache.http.message.BasicHeader;
 import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
 import org.apache.http.util.EncodingUtils;
 import org.junit.Assert;
 import org.junit.Test;
@@ -76,7 +78,8 @@ public class TestBasicScheme {
         authscheme.processChallenge(challenge);
 
         HttpRequest request = new BasicHttpRequest("GET", "/");
-        Header authResponse = authscheme.authenticate(creds, request);
+        HttpContext context = new BasicHttpContext();
+        Header authResponse = authscheme.authenticate(creds, request, context);
 
         String expected = "Basic " + EncodingUtils.getAsciiString(
             Base64.encodeBase64(EncodingUtils.getAsciiBytes("testuser:testpass")));
@@ -98,7 +101,8 @@ public class TestBasicScheme {
         authscheme.processChallenge(challenge);
 
         HttpRequest request = new BasicHttpRequest("GET", "/");
-        Header authResponse = authscheme.authenticate(creds, request);
+        HttpContext context = new BasicHttpContext();
+        Header authResponse = authscheme.authenticate(creds, request, context);
 
         String expected = "Basic " + EncodingUtils.getAsciiString(
             Base64.encodeBase64(EncodingUtils.getAsciiBytes("testuser:testpass")));
diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java
index f4dcf61..d1f581f 100644
--- a/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java
+++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java
@@ -26,20 +26,31 @@
 
 package org.apache.http.impl.auth;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.http.Header;
 import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpRequest;
 import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthenticationException;
 import org.apache.http.auth.Credentials;
 import org.apache.http.auth.AUTH;
 import org.apache.http.auth.MalformedChallengeException;
 import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.StringEntity;
 import org.apache.http.message.BasicHeader;
 import org.apache.http.message.BasicHeaderValueParser;
+import org.apache.http.message.BasicHttpEntityEnclosingRequest;
 import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -49,14 +60,14 @@ import org.junit.Test;
 public class TestDigestScheme {
 
     @Test(expected=MalformedChallengeException.class)
-    public void testDigestAuthenticationWithNoRealm() throws Exception {
+    public void testDigestAuthenticationEmptyChallenge1() throws Exception {
         Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, "Digest");
         AuthScheme authscheme = new DigestScheme();
         authscheme.processChallenge(authChallenge);
     }
 
     @Test(expected=MalformedChallengeException.class)
-    public void testDigestAuthenticationWithNoRealm2() throws Exception {
+    public void testDigestAuthenticationEmptyChallenge2() throws Exception {
         Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, "Digest ");
         AuthScheme authscheme = new DigestScheme();
         authscheme.processChallenge(authChallenge);
@@ -69,8 +80,11 @@ public class TestDigestScheme {
         HttpRequest request = new BasicHttpRequest("Simple", "/");
         Credentials cred = new UsernamePasswordCredentials("username","password");
         DigestScheme authscheme = new DigestScheme();
+        HttpContext context = new BasicHttpContext();
         authscheme.processChallenge(authChallenge);
-        Header authResponse = authscheme.authenticate(cred, request);
+        Header authResponse = authscheme.authenticate(cred, request, context);
+        Assert.assertTrue(authscheme.isComplete());
+        Assert.assertFalse(authscheme.isConnectionBased());
 
         Map<String, String> table = parseAuthResponse(authResponse);
         Assert.assertEquals("username", table.get("username"));
@@ -87,8 +101,9 @@ public class TestDigestScheme {
         HttpRequest request = new BasicHttpRequest("Simple", "/");
         Credentials cred = new UsernamePasswordCredentials("username","password");
         DigestScheme authscheme = new DigestScheme();
+        HttpContext context = new BasicHttpContext();
         authscheme.processChallenge(authChallenge);
-        Header authResponse = authscheme.authenticate(cred, request);
+        Header authResponse = authscheme.authenticate(cred, request, context);
 
         Map<String, String> table = parseAuthResponse(authResponse);
         Assert.assertEquals("username", table.get("username"));
@@ -99,6 +114,47 @@ public class TestDigestScheme {
     }
 
     @Test
+    public void testDigestAuthenticationInvalidInput() throws Exception {
+        String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
+        Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
+        HttpRequest request = new BasicHttpRequest("Simple", "/");
+        Credentials cred = new UsernamePasswordCredentials("username","password");
+        DigestScheme authscheme = new DigestScheme();
+        HttpContext context = new BasicHttpContext();
+        authscheme.processChallenge(authChallenge);
+        try {
+            authscheme.authenticate(null, request, context);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (IllegalArgumentException ex) {
+        }
+        try {
+            authscheme.authenticate(cred, null, context);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (IllegalArgumentException ex) {
+        }
+    }
+
+    @Test
+    public void testDigestAuthenticationOverrideParameter() throws Exception {
+        String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
+        Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
+        HttpRequest request = new BasicHttpRequest("Simple", "/");
+        Credentials cred = new UsernamePasswordCredentials("username","password");
+        DigestScheme authscheme = new DigestScheme();
+        HttpContext context = new BasicHttpContext();
+        authscheme.processChallenge(authChallenge);
+        authscheme.overrideParamter("realm", "other realm");
+        Header authResponse = authscheme.authenticate(cred, request, context);
+
+        Map<String, String> table = parseAuthResponse(authResponse);
+        Assert.assertEquals("username", table.get("username"));
+        Assert.assertEquals("other realm", table.get("realm"));
+        Assert.assertEquals("/", table.get("uri"));
+        Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
+        Assert.assertEquals("3f211de10463cbd055ab4cd9c5158eac", table.get("response"));
+    }
+
+    @Test
     public void testDigestAuthenticationWithSHA() throws Exception {
         String challenge = "Digest realm=\"realm1\", " +
                 "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
@@ -106,9 +162,10 @@ public class TestDigestScheme {
         Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
         HttpRequest request = new BasicHttpRequest("Simple", "/");
         Credentials cred = new UsernamePasswordCredentials("username","password");
+        HttpContext context = new BasicHttpContext();
         DigestScheme authscheme = new DigestScheme();
         authscheme.processChallenge(authChallenge);
-        Header authResponse = authscheme.authenticate(cred, request);
+        Header authResponse = authscheme.authenticate(cred, request, context);
 
         Map<String, String> table = parseAuthResponse(authResponse);
         Assert.assertEquals("username", table.get("username"));
@@ -124,9 +181,10 @@ public class TestDigestScheme {
         Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
         HttpRequest request = new BasicHttpRequest("Simple", "/?param=value");
         Credentials cred = new UsernamePasswordCredentials("username","password");
+        HttpContext context = new BasicHttpContext();
         DigestScheme authscheme = new DigestScheme();
         authscheme.processChallenge(authChallenge);
-        Header authResponse = authscheme.authenticate(cred, request);
+        Header authResponse = authscheme.authenticate(cred, request, context);
 
         Map<String, String> table = parseAuthResponse(authResponse);
         Assert.assertEquals("username", table.get("username"));
@@ -145,9 +203,10 @@ public class TestDigestScheme {
 
         Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge1);
         HttpRequest request = new BasicHttpRequest("Simple", "/");
+        HttpContext context = new BasicHttpContext();
         DigestScheme authscheme = new DigestScheme();
         authscheme.processChallenge(authChallenge);
-        Header authResponse = authscheme.authenticate(cred, request);
+        Header authResponse = authscheme.authenticate(cred, request, context);
 
         Map<String, String> table = parseAuthResponse(authResponse);
         Assert.assertEquals("username", table.get("username"));
@@ -159,7 +218,7 @@ public class TestDigestScheme {
         authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge2);
         DigestScheme authscheme2 = new DigestScheme();
         authscheme2.processChallenge(authChallenge);
-        authResponse = authscheme2.authenticate(cred2, request);
+        authResponse = authscheme2.authenticate(cred2, request, context);
 
         table = parseAuthResponse(authResponse);
         Assert.assertEquals("uname2", table.get("username"));
@@ -169,6 +228,32 @@ public class TestDigestScheme {
         Assert.assertEquals("0283edd9ef06a38b378b3b74661391e9", table.get("response"));
     }
 
+    @Test(expected=AuthenticationException.class)
+    public void testDigestAuthenticationNoRealm() throws Exception {
+        String challenge = "Digest no-realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
+        Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
+        HttpContext context = new BasicHttpContext();
+        DigestScheme authscheme = new DigestScheme();
+        authscheme.processChallenge(authChallenge);
+
+        Credentials cred = new UsernamePasswordCredentials("username","password");
+        HttpRequest request = new BasicHttpRequest("Simple", "/");
+        authscheme.authenticate(cred, request, context);
+    }
+
+    @Test(expected=AuthenticationException.class)
+    public void testDigestAuthenticationNoNonce() throws Exception {
+        String challenge = "Digest realm=\"realm1\", no-nonce=\"f2a3f18799759d4f1a1c068b92b573cb\"";
+        Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
+        HttpContext context = new BasicHttpContext();
+        DigestScheme authscheme = new DigestScheme();
+        authscheme.processChallenge(authChallenge);
+
+        Credentials cred = new UsernamePasswordCredentials("username","password");
+        HttpRequest request = new BasicHttpRequest("Simple", "/");
+        authscheme.authenticate(cred, request, context);
+    }
+
     /**
      * Test digest authentication using the MD5-sess algorithm.
      */
@@ -192,10 +277,11 @@ public class TestDigestScheme {
 
         Credentials cred = new UsernamePasswordCredentials(username, password);
         HttpRequest request = new BasicHttpRequest("Simple", "/");
+        HttpContext context = new BasicHttpContext();
 
         DigestScheme authscheme = new DigestScheme();
         authscheme.processChallenge(authChallenge);
-        Header authResponse = authscheme.authenticate(cred, request);
+        Header authResponse = authscheme.authenticate(cred, request, context);
         String response = authResponse.getValue();
 
         Assert.assertTrue(response.indexOf("nc=00000001") > 0); // test for quotes
@@ -238,10 +324,11 @@ public class TestDigestScheme {
         Credentials cred = new UsernamePasswordCredentials(username, password);
 
         HttpRequest request = new BasicHttpRequest("Simple", "/");
+        HttpContext context = new BasicHttpContext();
 
         DigestScheme authscheme = new DigestScheme();
         authscheme.processChallenge(authChallenge);
-        Header authResponse = authscheme.authenticate(cred, request);
+        Header authResponse = authscheme.authenticate(cred, request, context);
 
         Map<String, String> table = parseAuthResponse(authResponse);
         Assert.assertEquals(username, table.get("username"));
@@ -257,13 +344,15 @@ public class TestDigestScheme {
     }
 
     /**
-     * Test digest authentication with invalud qop value
+     * Test digest authentication with unknown qop value
      */
-    @Test(expected=MalformedChallengeException.class)
-    public void testDigestAuthenticationMD5SessInvalidQop() throws Exception {
+    @Test(expected=AuthenticationException.class)
+    public void testDigestAuthenticationMD5SessUnknownQop() throws Exception {
         // Example using Digest auth with MD5-sess
 
         String realm="realm";
+        String username="username";
+        String password="password";
         String nonce="e273f1776275974f1a120d8b92c5b3cb";
 
         String challenge="Digest realm=\"" + realm + "\", "
@@ -271,12 +360,47 @@ public class TestDigestScheme {
             + "opaque=\"SomeString\", "
             + "stale=false, "
             + "algorithm=MD5-sess, "
-            + "qop=\"jakarta\""; // jakarta is an invalid qop value
+            + "qop=\"stuff\"";
 
         Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
 
-        AuthScheme authscheme = new DigestScheme();
+        DigestScheme authscheme = new DigestScheme();
+        authscheme.processChallenge(authChallenge);
+
+        Credentials cred = new UsernamePasswordCredentials(username, password);
+        HttpRequest request = new BasicHttpRequest("Simple", "/");
+        HttpContext context = new BasicHttpContext();
+        authscheme.authenticate(cred, request, context);
+    }
+
+    /**
+     * Test digest authentication with unknown qop value
+     */
+    @Test(expected=AuthenticationException.class)
+    public void testDigestAuthenticationUnknownAlgo() throws Exception {
+        // Example using Digest auth with MD5-sess
+
+        String realm="realm";
+        String username="username";
+        String password="password";
+        String nonce="e273f1776275974f1a120d8b92c5b3cb";
+
+        String challenge="Digest realm=\"" + realm + "\", "
+            + "nonce=\"" + nonce + "\", "
+            + "opaque=\"SomeString\", "
+            + "stale=false, "
+            + "algorithm=stuff, "
+            + "qop=\"auth\"";
+
+        Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
+
+        DigestScheme authscheme = new DigestScheme();
         authscheme.processChallenge(authChallenge);
+
+        Credentials cred = new UsernamePasswordCredentials(username, password);
+        HttpRequest request = new BasicHttpRequest("Simple", "/");
+        HttpContext context = new BasicHttpContext();
+        authscheme.authenticate(cred, request, context);
     }
 
     @Test
@@ -308,28 +432,191 @@ public class TestDigestScheme {
     public void testDigestNouceCount() throws Exception {
         String challenge1 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth";
         Header authChallenge1 = new BasicHeader(AUTH.WWW_AUTH, challenge1);
-        HttpRequest request = new BasicHttpRequest("Simple", "/");
+        HttpRequest request = new BasicHttpRequest("GET", "/");
         Credentials cred = new UsernamePasswordCredentials("username","password");
+        HttpContext context = new BasicHttpContext();
         DigestScheme authscheme = new DigestScheme();
         authscheme.processChallenge(authChallenge1);
-        Header authResponse1 = authscheme.authenticate(cred, request);
+        Header authResponse1 = authscheme.authenticate(cred, request, context);
         Map<String, String> table1 = parseAuthResponse(authResponse1);
         Assert.assertEquals("00000001", table1.get("nc"));
-        Header authResponse2 = authscheme.authenticate(cred, request);
+        Header authResponse2 = authscheme.authenticate(cred, request, context);
         Map<String, String> table2 = parseAuthResponse(authResponse2);
         Assert.assertEquals("00000002", table2.get("nc"));
         String challenge2 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth";
         Header authChallenge2 = new BasicHeader(AUTH.WWW_AUTH, challenge2);
         authscheme.processChallenge(authChallenge2);
-        Header authResponse3 = authscheme.authenticate(cred, request);
+        Header authResponse3 = authscheme.authenticate(cred, request, context);
         Map<String, String> table3 = parseAuthResponse(authResponse3);
         Assert.assertEquals("00000003", table3.get("nc"));
         String challenge3 = "Digest realm=\"realm1\", nonce=\"e273f1776275974f1a120d8b92c5b3cb\", qop=auth";
         Header authChallenge3 = new BasicHeader(AUTH.WWW_AUTH, challenge3);
         authscheme.processChallenge(authChallenge3);
-        Header authResponse4 = authscheme.authenticate(cred, request);
+        Header authResponse4 = authscheme.authenticate(cred, request, context);
+        Map<String, String> table4 = parseAuthResponse(authResponse4);
+        Assert.assertEquals("00000001", table4.get("nc"));
+    }
+
+    @Test
+    public void testDigestMD5SessA1AndCnonceConsistency() throws Exception {
+        String challenge1 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " +
+                "charset=utf-8, realm=\"subnet.domain.com\"";
+        Header authChallenge1 = new BasicHeader(AUTH.WWW_AUTH, challenge1);
+        HttpRequest request = new BasicHttpRequest("GET", "/");
+        Credentials cred = new UsernamePasswordCredentials("username","password");
+        HttpContext context = new BasicHttpContext();
+        DigestScheme authscheme = new DigestScheme();
+        authscheme.processChallenge(authChallenge1);
+        Header authResponse1 = authscheme.authenticate(cred, request, context);
+        Map<String, String> table1 = parseAuthResponse(authResponse1);
+        Assert.assertEquals("00000001", table1.get("nc"));
+        String cnonce1 = authscheme.getCnonce();
+        String sessionKey1 = authscheme.getA1();
+
+        Header authResponse2 = authscheme.authenticate(cred, request, context);
+        Map<String, String> table2 = parseAuthResponse(authResponse2);
+        Assert.assertEquals("00000002", table2.get("nc"));
+        String cnonce2 = authscheme.getCnonce();
+        String sessionKey2 = authscheme.getA1();
+
+        Assert.assertEquals(cnonce1, cnonce2);
+        Assert.assertEquals(sessionKey1, sessionKey2);
+
+        String challenge2 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " +
+            "charset=utf-8, realm=\"subnet.domain.com\"";
+        Header authChallenge2 = new BasicHeader(AUTH.WWW_AUTH, challenge2);
+        authscheme.processChallenge(authChallenge2);
+        Header authResponse3 = authscheme.authenticate(cred, request, context);
+        Map<String, String> table3 = parseAuthResponse(authResponse3);
+        Assert.assertEquals("00000003", table3.get("nc"));
+
+        String cnonce3 = authscheme.getCnonce();
+        String sessionKey3 = authscheme.getA1();
+
+        Assert.assertEquals(cnonce1, cnonce3);
+        Assert.assertEquals(sessionKey1, sessionKey3);
+
+        String challenge3 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"fedcba0987654321\", " +
+            "charset=utf-8, realm=\"subnet.domain.com\"";
+        Header authChallenge3 = new BasicHeader(AUTH.WWW_AUTH, challenge3);
+        authscheme.processChallenge(authChallenge3);
+        Header authResponse4 = authscheme.authenticate(cred, request, context);
         Map<String, String> table4 = parseAuthResponse(authResponse4);
         Assert.assertEquals("00000001", table4.get("nc"));
+
+        String cnonce4 = authscheme.getCnonce();
+        String sessionKey4 = authscheme.getA1();
+
+        Assert.assertFalse(cnonce1.equals(cnonce4));
+        Assert.assertFalse(sessionKey1.equals(sessionKey4));
+    }
+
+    @Test
+    public void testHttpEntityDigest() throws Exception {
+        HttpEntityDigester digester = new HttpEntityDigester(MessageDigest.getInstance("MD5"));
+        Assert.assertNull(digester.getDigest());
+        digester.write('a');
+        digester.write('b');
+        digester.write('c');
+        digester.write(0xe4);
+        digester.write(0xf6);
+        digester.write(0xfc);
+        digester.write(new byte[] { 'a', 'b', 'c'});
+        Assert.assertNull(digester.getDigest());
+        digester.close();
+        Assert.assertEquals("acd2b59cd01c7737d8069015584c6cac", DigestScheme.encode(digester.getDigest()));
+        try {
+            digester.write('a');
+            Assert.fail("IOException should have been thrown");
+        } catch (IOException ex) {
+        }
+        try {
+            digester.write(new byte[] { 'a', 'b', 'c'});
+            Assert.fail("IOException should have been thrown");
+        } catch (IOException ex) {
+        }
+    }
+
+    @Test
+    public void testDigestAuthenticationQopAuthInt() throws Exception {
+        String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
+                "qop=\"auth,auth-int\"";
+        Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
+        HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
+        request.setEntity(new StringEntity("abc\u00e4\u00f6\u00fcabc", HTTP.DEF_CONTENT_CHARSET));
+        Credentials cred = new UsernamePasswordCredentials("username","password");
+        DigestScheme authscheme = new DigestScheme();
+        HttpContext context = new BasicHttpContext();
+        authscheme.processChallenge(authChallenge);
+        Header authResponse = authscheme.authenticate(cred, request, context);
+
+        Assert.assertEquals("Post:/:acd2b59cd01c7737d8069015584c6cac", authscheme.getA2());
+
+        Map<String, String> table = parseAuthResponse(authResponse);
+        Assert.assertEquals("username", table.get("username"));
+        Assert.assertEquals("realm1", table.get("realm"));
+        Assert.assertEquals("/", table.get("uri"));
+        Assert.assertEquals("auth-int", table.get("qop"));
+        Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
+    }
+
+    @Test
+    public void testDigestAuthenticationQopAuthIntNullEntity() throws Exception {
+        String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
+                "qop=\"auth,auth-int\"";
+        Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
+        HttpRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
+        Credentials cred = new UsernamePasswordCredentials("username","password");
+        DigestScheme authscheme = new DigestScheme();
+        HttpContext context = new BasicHttpContext();
+        authscheme.processChallenge(authChallenge);
+        Header authResponse = authscheme.authenticate(cred, request, context);
+
+        Assert.assertEquals("Post:/:d41d8cd98f00b204e9800998ecf8427e", authscheme.getA2());
+
+        Map<String, String> table = parseAuthResponse(authResponse);
+        Assert.assertEquals("username", table.get("username"));
+        Assert.assertEquals("realm1", table.get("realm"));
+        Assert.assertEquals("/", table.get("uri"));
+        Assert.assertEquals("auth-int", table.get("qop"));
+        Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
+    }
+
+    @Test
+    public void testDigestAuthenticationQopAuthOrAuthIntNonRepeatableEntity() throws Exception {
+        String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
+                "qop=\"auth,auth-int\"";
+        Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
+        HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
+        request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1));
+        Credentials cred = new UsernamePasswordCredentials("username","password");
+        DigestScheme authscheme = new DigestScheme();
+        HttpContext context = new BasicHttpContext();
+        authscheme.processChallenge(authChallenge);
+        Header authResponse = authscheme.authenticate(cred, request, context);
+
+        Assert.assertEquals("Post:/", authscheme.getA2());
+
+        Map<String, String> table = parseAuthResponse(authResponse);
+        Assert.assertEquals("username", table.get("username"));
+        Assert.assertEquals("realm1", table.get("realm"));
+        Assert.assertEquals("/", table.get("uri"));
+        Assert.assertEquals("auth", table.get("qop"));
+        Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce"));
+    }
+
+    @Test(expected=AuthenticationException.class)
+    public void testDigestAuthenticationQopIntOnlyNonRepeatableEntity() throws Exception {
+        String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " +
+                "qop=\"auth-int\"";
+        Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge);
+        HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/");
+        request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1));
+        Credentials cred = new UsernamePasswordCredentials("username","password");
+        DigestScheme authscheme = new DigestScheme();
+        HttpContext context = new BasicHttpContext();
+        authscheme.processChallenge(authChallenge);
+        authscheme.authenticate(cred, request, context);
     }
 
 }
diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestNegotiateScheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestNegotiateScheme.java
deleted file mode 100644
index 0102259..0000000
--- a/httpclient/src/test/java/org/apache/http/impl/auth/TestNegotiateScheme.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-package org.apache.http.impl.auth;
-
-import java.io.IOException;
-import java.security.Principal;
-
-import org.apache.http.HttpException;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.auth.AuthScheme;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.Credentials;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.params.AuthPolicy;
-import org.apache.http.client.params.ClientPNames;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.localserver.BasicServerTestBase;
-import org.apache.http.localserver.LocalTestServer;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.protocol.HttpRequestHandler;
-import org.apache.http.util.EntityUtils;
-
-import org.ietf.jgss.GSSContext;
-import org.ietf.jgss.GSSCredential;
-import org.ietf.jgss.GSSManager;
-import org.ietf.jgss.GSSName;
-import org.ietf.jgss.Oid;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Matchers;
-import org.mockito.Mockito;
-
-/**
- * Tests for {@link NegotiateScheme}.
- */
-public class TestNegotiateScheme extends BasicServerTestBase {
-
-    @Before
-    public void setUp() throws Exception {
-        localServer = new LocalTestServer(null, null);
-
-        localServer.registerDefaultHandlers();
-        localServer.start();
-    }
-
-    /**
-     * This service will continue to ask for authentication.
-     */
-    private static class PleaseNegotiateService implements HttpRequestHandler {
-
-        public void handle(
-                final HttpRequest request,
-                final HttpResponse response,
-                final HttpContext context) throws HttpException, IOException {
-            response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
-            response.addHeader(new BasicHeader("WWW-Authenticate", "Negotiate blablabla"));
-            response.addHeader(new BasicHeader("Connection", "Keep-Alive"));
-            response.setEntity(new StringEntity("auth required "));
-        }
-    }
-
-    /**
-     * NegotatieScheme with a custom GSSManager that does not require any Jaas or
-     * Kerberos configuration.
-     *
-     */
-    private static class NegotiateSchemeWithMockGssManager extends NegotiateScheme {
-
-        GSSManager manager = Mockito.mock(GSSManager.class);
-        GSSName name = Mockito.mock(GSSName.class);
-        GSSContext context = Mockito.mock(GSSContext.class);
-
-        NegotiateSchemeWithMockGssManager() throws Exception {
-            super(null, true);
-            Mockito.when(context.initSecContext(
-                    Matchers.any(byte[].class), Matchers.anyInt(), Matchers.anyInt()))
-                    .thenReturn("12345678".getBytes());
-            Mockito.when(manager.createName(
-                    Matchers.any(String.class), Matchers.any(Oid.class)))
-                    .thenReturn(name);
-            Mockito.when(manager.createContext(
-                    Matchers.any(GSSName.class), Matchers.any(Oid.class),
-                    Matchers.any(GSSCredential.class), Matchers.anyInt()))
-                    .thenReturn(context);
-        }
-
-        @Override
-        protected GSSManager getManager() {
-            return manager;
-        }
-
-    }
-
-    private static class UseJaasCredentials implements Credentials {
-
-        public String getPassword() {
-            return null;
-        }
-
-        public Principal getUserPrincipal() {
-            return null;
-        }
-
-    }
-
-    private static class NegotiateSchemeFactoryWithMockGssManager extends NegotiateSchemeFactory {
-
-        NegotiateSchemeWithMockGssManager scheme;
-
-        NegotiateSchemeFactoryWithMockGssManager() throws Exception {
-            scheme = new NegotiateSchemeWithMockGssManager();
-        }
-
-        @Override
-        public AuthScheme newInstance(HttpParams params) {
-            return scheme;
-        }
-
-    }
-
-    /**
-     * Tests that the client will stop connecting to the server if
-     * the server still keep asking for a valid ticket.
-     */
-    @Test
-    public void testDontTryToAuthenticateEndlessly() throws Exception {
-        int port = this.localServer.getServiceAddress().getPort();
-        this.localServer.register("*", new PleaseNegotiateService());
-
-        HttpHost target = new HttpHost("localhost", port);
-        DefaultHttpClient client = new DefaultHttpClient();
-
-        NegotiateSchemeFactory nsf = new NegotiateSchemeFactoryWithMockGssManager();
-
-        client.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);
-
-        Credentials use_jaas_creds = new UseJaasCredentials();
-        client.getCredentialsProvider().setCredentials(
-                new AuthScope(null, -1, null), use_jaas_creds);
-        client.getParams().setParameter(ClientPNames.DEFAULT_HOST, target);
-
-        String s = "/path";
-        HttpGet httpget = new HttpGet(s);
-        HttpResponse response = client.execute(httpget);
-        EntityUtils.consume(response.getEntity());
-
-        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
-    }
-
-    /**
-     * Javadoc specifies that {@link GSSContext#initSecContext(byte[], int, int)} can return null
-     * if no token is generated. Client should be able to deal with this response.
-     */
-    @Test
-    public void testNoTokenGeneratedError() throws Exception {
-        int port = this.localServer.getServiceAddress().getPort();
-        this.localServer.register("*", new PleaseNegotiateService());
-
-        HttpHost target = new HttpHost("localhost", port);
-        DefaultHttpClient client = new DefaultHttpClient();
-
-        NegotiateSchemeFactoryWithMockGssManager nsf = new NegotiateSchemeFactoryWithMockGssManager();
-
-        client.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);
-
-        Credentials use_jaas_creds = new UseJaasCredentials();
-        client.getCredentialsProvider().setCredentials(
-                new AuthScope(null, -1, null), use_jaas_creds);
-        client.getParams().setParameter(ClientPNames.DEFAULT_HOST, target);
-
-        String s = "/path";
-        HttpGet httpget = new HttpGet(s);
-        HttpResponse response = client.execute(httpget);
-        EntityUtils.consume(response.getEntity());
-
-        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
-    }
-
-}
diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestRFC2617Scheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestRFC2617Scheme.java
index 8953628..04c10d5 100644
--- a/httpclient/src/test/java/org/apache/http/impl/auth/TestRFC2617Scheme.java
+++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestRFC2617Scheme.java
@@ -73,11 +73,13 @@ public class TestRFC2617Scheme {
         authscheme.processChallenge(header);
 
         Assert.assertEquals("test", authscheme.getSchemeName());
+        Assert.assertEquals("TEST", authscheme.toString());
         Assert.assertEquals("realm1", authscheme.getParameter("realm"));
         Assert.assertEquals(null, authscheme.getParameter("test"));
         Assert.assertEquals("stuff", authscheme.getParameter("test1"));
         Assert.assertEquals("stuff, stuff", authscheme.getParameter("test2"));
         Assert.assertEquals("\"crap", authscheme.getParameter("test3"));
+        Assert.assertEquals(null, authscheme.getParameter(null));
     }
 
     @Test
@@ -87,12 +89,23 @@ public class TestRFC2617Scheme {
         buffer.append(" WWW-Authenticate:    Test       realm=\"realm1\"");
         Header header = new BufferedHeader(buffer);
 
+
         authscheme.processChallenge(header);
 
         Assert.assertEquals("test", authscheme.getSchemeName());
         Assert.assertEquals("realm1", authscheme.getParameter("realm"));
     }
 
+    @Test
+    public void testNullHeader() throws Exception {
+        TestAuthScheme authscheme = new TestAuthScheme();
+        try {
+            authscheme.processChallenge(null);
+            Assert.fail("IllegalArgumentException should have been thrown");
+        } catch (IllegalArgumentException ex) {
+        }
+    }
+
     @Test(expected=MalformedChallengeException.class)
     public void testInvalidHeader() throws Exception {
         TestAuthScheme authscheme = new TestAuthScheme();
@@ -101,6 +114,13 @@ public class TestRFC2617Scheme {
     }
 
     @Test(expected=MalformedChallengeException.class)
+    public void testInvalidSchemeName() throws Exception {
+        TestAuthScheme authscheme = new TestAuthScheme();
+        Header header = new BasicHeader(AUTH.WWW_AUTH, "Not-a-Test realm=\"realm1\"");
+        authscheme.processChallenge(header);
+    }
+
+    @Test(expected=MalformedChallengeException.class)
     public void testEmptyHeader() throws Exception {
         TestAuthScheme authscheme = new TestAuthScheme();
         Header header = new BasicHeader(AUTH.WWW_AUTH, "Test    ");
diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestSPNegoScheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestSPNegoScheme.java
new file mode 100644
index 0000000..b3a80f5
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestSPNegoScheme.java
@@ -0,0 +1,209 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.auth;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.localserver.BasicServerTestBase;
+import org.apache.http.localserver.LocalTestServer;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.util.EntityUtils;
+
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.Mockito;
+
+/**
+ * Tests for {@link NegotiateScheme}.
+ */
+public class TestSPNegoScheme extends BasicServerTestBase {
+
+    @Before
+    public void setUp() throws Exception {
+        this.localServer = new LocalTestServer(null, null);
+
+        this.localServer.registerDefaultHandlers();
+        this.localServer.start();
+        this.httpclient = new DefaultHttpClient();
+    }
+
+    /**
+     * This service will continue to ask for authentication.
+     */
+    private static class PleaseNegotiateService implements HttpRequestHandler {
+
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
+            response.addHeader(new BasicHeader("WWW-Authenticate", "Negotiate blablabla"));
+            response.addHeader(new BasicHeader("Connection", "Keep-Alive"));
+            response.setEntity(new StringEntity("auth required "));
+        }
+    }
+
+    /**
+     * NegotatieScheme with a custom GSSManager that does not require any Jaas or
+     * Kerberos configuration.
+     *
+     */
+    private static class NegotiateSchemeWithMockGssManager extends SPNegoScheme {
+
+        GSSManager manager = Mockito.mock(GSSManager.class);
+        GSSName name = Mockito.mock(GSSName.class);
+        GSSContext context = Mockito.mock(GSSContext.class);
+
+        NegotiateSchemeWithMockGssManager() throws Exception {
+            super(true);
+            Mockito.when(context.initSecContext(
+                    Matchers.any(byte[].class), Matchers.anyInt(), Matchers.anyInt()))
+                    .thenReturn("12345678".getBytes());
+            Mockito.when(manager.createName(
+                    Matchers.any(String.class), Matchers.any(Oid.class)))
+                    .thenReturn(name);
+            Mockito.when(manager.createContext(
+                    Matchers.any(GSSName.class), Matchers.any(Oid.class),
+                    Matchers.any(GSSCredential.class), Matchers.anyInt()))
+                    .thenReturn(context);
+        }
+
+        @Override
+        protected GSSManager getManager() {
+            return manager;
+        }
+
+    }
+
+    private static class UseJaasCredentials implements Credentials {
+
+        public String getPassword() {
+            return null;
+        }
+
+        public Principal getUserPrincipal() {
+            return null;
+        }
+
+    }
+
+    private static class NegotiateSchemeFactoryWithMockGssManager extends SPNegoSchemeFactory {
+
+        NegotiateSchemeWithMockGssManager scheme;
+
+        NegotiateSchemeFactoryWithMockGssManager() throws Exception {
+            scheme = new NegotiateSchemeWithMockGssManager();
+        }
+
+        @Override
+        public AuthScheme newInstance(HttpParams params) {
+            return scheme;
+        }
+
+    }
+
+    /**
+     * Tests that the client will stop connecting to the server if
+     * the server still keep asking for a valid ticket.
+     */
+    @Test
+    public void testDontTryToAuthenticateEndlessly() throws Exception {
+        int port = this.localServer.getServiceAddress().getPort();
+        this.localServer.register("*", new PleaseNegotiateService());
+
+        HttpHost target = new HttpHost("localhost", port);
+
+        SPNegoSchemeFactory nsf = new NegotiateSchemeFactoryWithMockGssManager();
+
+        this.httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);
+
+        Credentials use_jaas_creds = new UseJaasCredentials();
+        this.httpclient.getCredentialsProvider().setCredentials(
+                new AuthScope(null, -1, null), use_jaas_creds);
+        this.httpclient.getParams().setParameter(ClientPNames.DEFAULT_HOST, target);
+
+        String s = "/path";
+        HttpGet httpget = new HttpGet(s);
+        HttpResponse response = this.httpclient.execute(httpget);
+        EntityUtils.consume(response.getEntity());
+
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
+    }
+
+    /**
+     * Javadoc specifies that {@link GSSContext#initSecContext(byte[], int, int)} can return null
+     * if no token is generated. Client should be able to deal with this response.
+     */
+    @Test
+    public void testNoTokenGeneratedError() throws Exception {
+        int port = this.localServer.getServiceAddress().getPort();
+        this.localServer.register("*", new PleaseNegotiateService());
+
+        HttpHost target = new HttpHost("localhost", port);
+
+        NegotiateSchemeFactoryWithMockGssManager nsf = new NegotiateSchemeFactoryWithMockGssManager();
+
+        this.httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);
+
+        Credentials use_jaas_creds = new UseJaasCredentials();
+        this.httpclient.getCredentialsProvider().setCredentials(
+                new AuthScope(null, -1, null), use_jaas_creds);
+        this.httpclient.getParams().setParameter(ClientPNames.DEFAULT_HOST, target);
+
+        String s = "/path";
+        HttpGet httpget = new HttpGet(s);
+        HttpResponse response = this.httpclient.execute(httpget);
+        EntityUtils.consume(response.getEntity());
+
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/DummyHttpClient.java b/httpclient/src/test/java/org/apache/http/impl/client/DummyHttpClient.java
new file mode 100644
index 0000000..6717a3f
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/DummyHttpClient.java
@@ -0,0 +1,130 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.impl.conn.SingleClientConnManager;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+ at SuppressWarnings("deprecation")
+public class DummyHttpClient implements HttpClient {
+
+    private HttpParams params = new BasicHttpParams();
+    private ClientConnectionManager connManager = new SingleClientConnManager();
+    private HttpRequest request;
+    private HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP",1,1), HttpStatus.SC_OK, "OK");
+    
+    public void setParams(HttpParams params) {
+        this.params = params;
+    }
+    
+    public HttpParams getParams() {
+        return params;
+    }
+
+    public ClientConnectionManager getConnectionManager() {
+        return connManager;
+    }
+    
+    public void setConnectionManager(ClientConnectionManager ccm) {
+        connManager = ccm;
+    }
+    
+    public void setResponse(HttpResponse resp) {
+        response = resp;
+    }
+    
+    public HttpRequest getCapturedRequest() {
+        return request;
+    }
+
+    public HttpResponse execute(HttpUriRequest request) throws IOException,
+            ClientProtocolException {
+        this.request = request;
+        return response;
+    }
+
+    public HttpResponse execute(HttpUriRequest request, HttpContext context)
+            throws IOException, ClientProtocolException {
+        this.request = request;
+        return response;
+    }
+
+    public HttpResponse execute(HttpHost target, HttpRequest request)
+            throws IOException, ClientProtocolException {
+        this.request = request;
+        return response;
+    }
+
+    public HttpResponse execute(HttpHost target, HttpRequest request,
+            HttpContext context) throws IOException, ClientProtocolException {
+        this.request = request;
+        return response;
+    }
+
+    public <T> T execute(HttpUriRequest request,
+            ResponseHandler<? extends T> responseHandler) throws IOException,
+            ClientProtocolException {
+        this.request = request;
+        return responseHandler.handleResponse(response);
+    }
+
+    public <T> T execute(HttpUriRequest request,
+            ResponseHandler<? extends T> responseHandler, HttpContext context)
+            throws IOException, ClientProtocolException {
+        this.request = request;
+        return responseHandler.handleResponse(response);
+    }
+
+    public <T> T execute(HttpHost target, HttpRequest request,
+            ResponseHandler<? extends T> responseHandler) throws IOException,
+            ClientProtocolException {
+        this.request = request;
+        return responseHandler.handleResponse(response);
+    }
+
+    public <T> T execute(HttpHost target, HttpRequest request,
+            ResponseHandler<? extends T> responseHandler, HttpContext context)
+            throws IOException, ClientProtocolException {
+        this.request = request;
+        return responseHandler.handleResponse(response);
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/MockClock.java b/httpclient/src/test/java/org/apache/http/impl/client/MockClock.java
new file mode 100644
index 0000000..ac639ef
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/MockClock.java
@@ -0,0 +1,40 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+public class MockClock implements Clock {
+
+    private long t = System.currentTimeMillis();
+
+    public long getCurrentTime() {
+        return t;
+    }
+
+    public void setCurrentTime(long now) {
+        t = now;
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/MockConnPoolControl.java b/httpclient/src/test/java/org/apache/http/impl/client/MockConnPoolControl.java
new file mode 100644
index 0000000..ca4fe9a
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/MockConnPoolControl.java
@@ -0,0 +1,116 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.pool.ConnPoolControl;
+import org.apache.http.pool.PoolStats;
+
+public final class MockConnPoolControl implements ConnPoolControl<HttpRoute> {
+
+    private final ConcurrentHashMap<HttpRoute, Integer> maxPerHostMap;
+
+    private volatile int totalMax;
+    private volatile int defaultMax;
+
+    public MockConnPoolControl() {
+        super();
+        this.maxPerHostMap = new ConcurrentHashMap<HttpRoute, Integer>();
+        this.totalMax = 20;
+        this.defaultMax = 2;
+    }
+
+    public void setMaxTotal(int max) {
+        this.totalMax = max;
+    }
+
+    public int getMaxTotal() {
+        return this.totalMax;
+    }
+
+    public PoolStats getTotalStats() {
+        return new PoolStats(-1, -1, -1, this.totalMax);
+    }
+
+    public PoolStats getStats(final HttpRoute route) {
+        return new PoolStats(-1, -1, -1, getMaxPerRoute(route));
+    }
+
+    public int getDefaultMaxPerRoute() {
+        return this.defaultMax;
+    }
+
+    public void setDefaultMaxPerRoute(int max) {
+        if (max < 1) {
+            throw new IllegalArgumentException
+                ("The maximum must be greater than 0.");
+        }
+        this.defaultMax = max;
+    }
+
+    public void setMaxPerRoute(final HttpRoute route, int max) {
+        if (route == null) {
+            throw new IllegalArgumentException
+                ("HTTP route may not be null.");
+        }
+        if (max < 1) {
+            throw new IllegalArgumentException
+                ("The maximum must be greater than 0.");
+        }
+        this.maxPerHostMap.put(route, Integer.valueOf(max));
+    }
+
+    public int getMaxPerRoute(final HttpRoute route) {
+        if (route == null) {
+            throw new IllegalArgumentException
+                ("HTTP route may not be null.");
+        }
+        Integer max = this.maxPerHostMap.get(route);
+        if (max != null) {
+            return max.intValue();
+        } else {
+            return this.defaultMax;
+        }
+    }
+
+    public void setMaxForRoutes(final Map<HttpRoute, Integer> map) {
+        if (map == null) {
+            return;
+        }
+        this.maxPerHostMap.clear();
+        this.maxPerHostMap.putAll(map);
+    }
+
+    @Override
+    public String toString() {
+        return this.maxPerHostMap.toString();
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java b/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java
new file mode 100644
index 0000000..18a5826
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestAIMDBackoffManager.java
@@ -0,0 +1,174 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import static org.junit.Assert.*;
+
+import java.util.Random;
+
+import org.apache.http.HttpHost;
+import org.apache.http.client.BackoffManager;
+import org.apache.http.conn.routing.HttpRoute;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestAIMDBackoffManager {
+
+    private AIMDBackoffManager impl;
+    private MockConnPoolControl connPerRoute;
+    private HttpRoute route;
+    private MockClock clock;
+
+    @Before
+    public void setUp() {
+        connPerRoute = new MockConnPoolControl();
+        route = new HttpRoute(new HttpHost("localhost:80"));
+        clock = new MockClock();
+        impl = new AIMDBackoffManager(connPerRoute, clock);
+        impl.setPerHostConnectionCap(10);
+    }
+
+    @Test
+    public void isABackoffManager() {
+        assertTrue(impl instanceof BackoffManager);
+    }
+
+    @Test
+    public void halvesConnectionsOnBackoff() {
+        connPerRoute.setMaxPerRoute(route, 4);
+        impl.backOff(route);
+        assertEquals(2, connPerRoute.getMaxPerRoute(route));
+    }
+
+    @Test
+    public void doesNotBackoffBelowOneConnection() {
+        connPerRoute.setMaxPerRoute(route, 1);
+        impl.backOff(route);
+        assertEquals(1, connPerRoute.getMaxPerRoute(route));
+    }
+
+    @Test
+    public void increasesByOneOnProbe() {
+        connPerRoute.setMaxPerRoute(route, 2);
+        impl.probe(route);
+        assertEquals(3, connPerRoute.getMaxPerRoute(route));
+    }
+
+    @Test
+    public void doesNotIncreaseBeyondPerHostMaxOnProbe() {
+        connPerRoute.setDefaultMaxPerRoute(5);
+        connPerRoute.setMaxPerRoute(route, 5);
+        impl.setPerHostConnectionCap(5);
+        impl.probe(route);
+        assertEquals(5, connPerRoute.getMaxPerRoute(route));
+    }
+
+    @Test
+    public void backoffDoesNotAdjustDuringCoolDownPeriod() {
+        connPerRoute.setMaxPerRoute(route, 4);
+        long now = System.currentTimeMillis();
+        clock.setCurrentTime(now);
+        impl.backOff(route);
+        long max = connPerRoute.getMaxPerRoute(route);
+        clock.setCurrentTime(now + 1);
+        impl.backOff(route);
+        assertEquals(max, connPerRoute.getMaxPerRoute(route));
+    }
+
+    @Test
+    public void backoffStillAdjustsAfterCoolDownPeriod() {
+        connPerRoute.setMaxPerRoute(route, 8);
+        long now = System.currentTimeMillis();
+        clock.setCurrentTime(now);
+        impl.backOff(route);
+        long max = connPerRoute.getMaxPerRoute(route);
+        clock.setCurrentTime(now + 10 * 1000L);
+        impl.backOff(route);
+        assertTrue(max == 1 || max > connPerRoute.getMaxPerRoute(route));
+    }
+
+    @Test
+    public void probeDoesNotAdjustDuringCooldownPeriod() {
+        connPerRoute.setMaxPerRoute(route, 4);
+        long now = System.currentTimeMillis();
+        clock.setCurrentTime(now);
+        impl.probe(route);
+        long max = connPerRoute.getMaxPerRoute(route);
+        clock.setCurrentTime(now + 1);
+        impl.probe(route);
+        assertEquals(max, connPerRoute.getMaxPerRoute(route));
+    }
+
+    @Test
+    public void probeStillAdjustsAfterCoolDownPeriod() {
+        connPerRoute.setMaxPerRoute(route, 8);
+        long now = System.currentTimeMillis();
+        clock.setCurrentTime(now);
+        impl.probe(route);
+        long max = connPerRoute.getMaxPerRoute(route);
+        clock.setCurrentTime(now + 10 * 1000L);
+        impl.probe(route);
+        assertTrue(max < connPerRoute.getMaxPerRoute(route));
+    }
+
+    @Test
+    public void willBackoffImmediatelyEvenAfterAProbe() {
+        connPerRoute.setMaxPerRoute(route, 8);
+        long now = System.currentTimeMillis();
+        clock.setCurrentTime(now);
+        impl.probe(route);
+        long max = connPerRoute.getMaxPerRoute(route);
+        clock.setCurrentTime(now + 1);
+        impl.backOff(route);
+        assertTrue(connPerRoute.getMaxPerRoute(route) < max);
+    }
+
+    @Test
+    public void backOffFactorIsConfigurable() {
+        connPerRoute.setMaxPerRoute(route, 10);
+        impl.setBackoffFactor(0.9);
+        impl.backOff(route);
+        assertEquals(9, connPerRoute.getMaxPerRoute(route));
+    }
+
+    @Test
+    public void coolDownPeriodIsConfigurable() {
+        long cd = new Random().nextLong() / 2;
+        if (cd < 0) cd *= -1;
+        if (cd < 1) cd++;
+        long now = System.currentTimeMillis();
+        impl.setCooldownMillis(cd);
+        clock.setCurrentTime(now);
+        impl.probe(route);
+        int max0 = connPerRoute.getMaxPerRoute(route);
+        clock.setCurrentTime(now);
+        impl.probe(route);
+        assertEquals(max0, connPerRoute.getMaxPerRoute(route));
+        clock.setCurrentTime(now + cd + 1);
+        impl.probe(route);
+        assertTrue(max0 < connPerRoute.getMaxPerRoute(route));
+    }
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestAuthenticationStrategy.java b/httpclient/src/test/java/org/apache/http/impl/client/TestAuthenticationStrategy.java
new file mode 100644
index 0000000..4500fd7
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestAuthenticationStrategy.java
@@ -0,0 +1,433 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.http.impl.client;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthOption;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.auth.params.AuthPNames;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.auth.BasicSchemeFactory;
+import org.apache.http.impl.auth.DigestScheme;
+import org.apache.http.impl.auth.DigestSchemeFactory;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * Simple tests for {@link AuthenticationStrategyImpl}.
+ */
+public class TestAuthenticationStrategy {
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testIsAuthenticationRequestedInvalidInput() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpHost host = new HttpHost("localhost", 80);
+        HttpContext context = new BasicHttpContext();
+        authStrategy.isAuthenticationRequested(host, null, context);
+    }
+
+    @Test
+    public void testTargetAuthRequested() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        HttpHost host = new HttpHost("localhost", 80);
+        HttpContext context = new BasicHttpContext();
+        Assert.assertTrue(authStrategy.isAuthenticationRequested(host, response, context));
+    }
+
+    @Test
+    public void testProxyAuthRequested() throws Exception {
+        ProxyAuthenticationStrategy authStrategy = new ProxyAuthenticationStrategy();
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, "UNAUTHORIZED");
+        HttpHost host = new HttpHost("localhost", 80);
+        HttpContext context = new BasicHttpContext();
+        Assert.assertTrue(authStrategy.isAuthenticationRequested(host, response, context));
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testGetChallengesInvalidInput() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpHost host = new HttpHost("localhost", 80);
+        HttpContext context = new BasicHttpContext();
+        authStrategy.getChallenges(host, null, context);
+    }
+
+    @Test
+    public void testGetChallenges() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpContext context = new BasicHttpContext();
+        HttpHost host = new HttpHost("localhost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        Header h1 = new BasicHeader(AUTH.WWW_AUTH, "  Basic  realm=\"test\"");
+        Header h2 = new BasicHeader(AUTH.WWW_AUTH, "\t\tDigest   realm=\"realm1\", nonce=\"1234\"");
+        Header h3 = new BasicHeader(AUTH.WWW_AUTH, "WhatEver realm=\"realm1\", stuff=\"1234\"");
+        response.addHeader(h1);
+        response.addHeader(h2);
+        response.addHeader(h3);
+
+        Map<String, Header> challenges = authStrategy.getChallenges(host, response, context);
+
+        Assert.assertNotNull(challenges);
+        Assert.assertEquals(3, challenges.size());
+        Assert.assertSame(h1, challenges.get("basic"));
+        Assert.assertSame(h2, challenges.get("digest"));
+        Assert.assertSame(h3, challenges.get("whatever"));
+    }
+
+    @Test
+    public void testSelectInvalidInput() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        Map<String, Header> challenges = new HashMap<String, Header>();
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        HttpHost authhost = new HttpHost("locahost", 80);
+        HttpContext context = new BasicHttpContext();
+        try {
+            authStrategy.select(null, authhost, response, context);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ex) {
+        }
+        try {
+            authStrategy.select(challenges, null, response, context);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ex) {
+        }
+        try {
+            authStrategy.select(challenges, authhost, null, context);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ex) {
+        }
+        try {
+            authStrategy.select(challenges, authhost, response, null);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ex) {
+        }
+    }
+
+    @Test
+    public void testSelectNoSchemeRegistry() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        HttpHost authhost = new HttpHost("locahost", 80);
+        HttpContext context = new BasicHttpContext();
+
+        Map<String, Header> challenges = new HashMap<String, Header>();
+        challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\""));
+        challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\""));
+
+        Queue<AuthOption> options = authStrategy.select(challenges, authhost, response, context);
+        Assert.assertNotNull(options);
+        Assert.assertEquals(0, options.size());
+    }
+
+    @Test
+    public void testSelectNoCredentialsProvider() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        HttpHost authhost = new HttpHost("locahost", 80);
+        HttpContext context = new BasicHttpContext();
+
+        Map<String, Header> challenges = new HashMap<String, Header>();
+        challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\""));
+        challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\""));
+
+        AuthSchemeRegistry authSchemeRegistry = new AuthSchemeRegistry();
+        authSchemeRegistry.register("basic", new BasicSchemeFactory());
+        authSchemeRegistry.register("digest", new DigestSchemeFactory());
+        context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, authSchemeRegistry);
+
+        Queue<AuthOption> options = authStrategy.select(challenges, authhost, response, context);
+        Assert.assertNotNull(options);
+        Assert.assertEquals(0, options.size());
+    }
+
+    @Test
+    public void testNoCredentials() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        HttpHost authhost = new HttpHost("locahost", 80);
+        HttpContext context = new BasicHttpContext();
+
+        Map<String, Header> challenges = new HashMap<String, Header>();
+        challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"realm1\""));
+        challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm2\", nonce=\"1234\""));
+
+        AuthSchemeRegistry authSchemeRegistry = new AuthSchemeRegistry();
+        authSchemeRegistry.register("basic", new BasicSchemeFactory());
+        authSchemeRegistry.register("digest", new DigestSchemeFactory());
+        context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, authSchemeRegistry);
+
+        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        context.setAttribute(ClientContext.CREDS_PROVIDER, credentialsProvider);
+
+        Queue<AuthOption> options = authStrategy.select(challenges, authhost, response, context);
+        Assert.assertNotNull(options);
+        Assert.assertEquals(0, options.size());
+    }
+
+    @Test
+    public void testCredentialsFound() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        HttpHost authhost = new HttpHost("somehost", 80);
+        HttpContext context = new BasicHttpContext();
+
+        Map<String, Header> challenges = new HashMap<String, Header>();
+        challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"realm1\""));
+        challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm2\", nonce=\"1234\""));
+
+        AuthSchemeRegistry authSchemeRegistry = new AuthSchemeRegistry();
+        authSchemeRegistry.register("basic", new BasicSchemeFactory());
+        authSchemeRegistry.register("digest", new DigestSchemeFactory());
+        context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, authSchemeRegistry);
+
+        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(new AuthScope("somehost", 80, "realm2"),
+                new UsernamePasswordCredentials("user", "pwd"));
+        context.setAttribute(ClientContext.CREDS_PROVIDER, credentialsProvider);
+
+        Queue<AuthOption> options = authStrategy.select(challenges, authhost, response, context);
+        Assert.assertNotNull(options);
+        Assert.assertEquals(1, options.size());
+        AuthOption option = options.remove();
+        Assert.assertTrue(option.getAuthScheme() instanceof DigestScheme);
+    }
+
+    @Test
+    public void testUnsupportedScheme() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        HttpHost authhost = new HttpHost("somehost", 80);
+        HttpContext context = new BasicHttpContext();
+
+        Map<String, Header> challenges = new HashMap<String, Header>();
+        challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"realm1\""));
+        challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm2\", nonce=\"1234\""));
+        challenges.put("whatever", new BasicHeader(AUTH.WWW_AUTH, "Whatever realm=\"realm3\""));
+
+        AuthSchemeRegistry authSchemeRegistry = new AuthSchemeRegistry();
+        authSchemeRegistry.register("basic", new BasicSchemeFactory());
+        authSchemeRegistry.register("digest", new DigestSchemeFactory());
+        context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, authSchemeRegistry);
+
+        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(new AuthScope("somehost", 80),
+                new UsernamePasswordCredentials("user", "pwd"));
+        context.setAttribute(ClientContext.CREDS_PROVIDER, credentialsProvider);
+
+        Queue<AuthOption> options = authStrategy.select(challenges, authhost, response, context);
+        Assert.assertNotNull(options);
+        Assert.assertEquals(2, options.size());
+        AuthOption option1 = options.remove();
+        Assert.assertTrue(option1.getAuthScheme() instanceof DigestScheme);
+        AuthOption option2 = options.remove();
+        Assert.assertTrue(option2.getAuthScheme() instanceof BasicScheme);
+    }
+
+    @Test
+    public void testCustomAuthPreference() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        response.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF,
+                Arrays.asList(new String[] {AuthPolicy.BASIC } ));
+        HttpHost authhost = new HttpHost("somehost", 80);
+        HttpContext context = new BasicHttpContext();
+
+        Map<String, Header> challenges = new HashMap<String, Header>();
+        challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"realm1\""));
+        challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm2\", nonce=\"1234\""));
+
+        AuthSchemeRegistry authSchemeRegistry = new AuthSchemeRegistry();
+        authSchemeRegistry.register("basic", new BasicSchemeFactory());
+        authSchemeRegistry.register("digest", new DigestSchemeFactory());
+        context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, authSchemeRegistry);
+
+        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(new AuthScope("somehost", 80),
+                new UsernamePasswordCredentials("user", "pwd"));
+        context.setAttribute(ClientContext.CREDS_PROVIDER, credentialsProvider);
+
+        Queue<AuthOption> options = authStrategy.select(challenges, authhost, response, context);
+        Assert.assertNotNull(options);
+        Assert.assertEquals(1, options.size());
+        AuthOption option1 = options.remove();
+        Assert.assertTrue(option1.getAuthScheme() instanceof BasicScheme);
+    }
+
+    @Test
+    public void testAuthSucceededInvalidInput() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpHost authhost = new HttpHost("locahost", 80);
+        BasicScheme authScheme = new BasicScheme();
+        HttpContext context = new BasicHttpContext();
+        try {
+            authStrategy.authSucceeded(null, authScheme, context);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ex) {
+        }
+        try {
+            authStrategy.authSucceeded(authhost, null, context);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ex) {
+        }
+        try {
+            authStrategy.authSucceeded(authhost, authScheme, null);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ex) {
+        }
+    }
+
+    @Test
+    public void testAuthSucceeded() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpHost authhost = new HttpHost("somehost", 80);
+        BasicScheme authScheme = new BasicScheme();
+        authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test"));
+
+        AuthCache authCache = Mockito.mock(AuthCache.class);
+
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ClientContext.AUTH_CACHE, authCache);
+
+        authStrategy.authSucceeded(authhost, authScheme, context);
+        Mockito.verify(authCache).put(authhost, authScheme);
+    }
+
+    @Test
+    public void testAuthSucceededNoCache() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpHost authhost = new HttpHost("somehost", 80);
+        BasicScheme authScheme = new BasicScheme();
+        authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test"));
+
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ClientContext.AUTH_CACHE, null);
+
+        authStrategy.authSucceeded(authhost, authScheme, context);
+        AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
+        Assert.assertNotNull(authCache);
+    }
+
+    @Test
+    public void testAuthScemeNotCompleted() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpHost authhost = new HttpHost("somehost", 80);
+        BasicScheme authScheme = new BasicScheme();
+
+        AuthCache authCache = Mockito.mock(AuthCache.class);
+
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ClientContext.AUTH_CACHE, authCache);
+
+        authStrategy.authSucceeded(authhost, authScheme, context);
+        Mockito.verify(authCache, Mockito.never()).put(authhost, authScheme);
+    }
+
+    @Test
+    public void testAuthScemeNonCacheable() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpHost authhost = new HttpHost("somehost", 80);
+        AuthScheme authScheme = Mockito.mock(AuthScheme.class);
+        Mockito.when(authScheme.isComplete()).thenReturn(true);
+        Mockito.when(authScheme.getSchemeName()).thenReturn("whatever");
+
+        AuthCache authCache = Mockito.mock(AuthCache.class);
+
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ClientContext.AUTH_CACHE, authCache);
+
+        authStrategy.authSucceeded(authhost, authScheme, context);
+        Mockito.verify(authCache, Mockito.never()).put(authhost, authScheme);
+    }
+
+    @Test
+    public void testAuthFailedInvalidInput() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpHost authhost = new HttpHost("locahost", 80);
+        BasicScheme authScheme = new BasicScheme();
+        HttpContext context = new BasicHttpContext();
+        try {
+            authStrategy.authFailed(null, authScheme, context);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ex) {
+        }
+        try {
+            authStrategy.authFailed(authhost, authScheme, null);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException ex) {
+        }
+    }
+
+    @Test
+    public void testAuthFailed() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpHost authhost = new HttpHost("somehost", 80);
+        BasicScheme authScheme = new BasicScheme();
+        authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test"));
+
+        AuthCache authCache = Mockito.mock(AuthCache.class);
+
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ClientContext.AUTH_CACHE, authCache);
+
+        authStrategy.authFailed(authhost, authScheme, context);
+        Mockito.verify(authCache).remove(authhost);
+    }
+
+    @Test
+    public void testAuthFailedNoCache() throws Exception {
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+        HttpHost authhost = new HttpHost("somehost", 80);
+        BasicScheme authScheme = new BasicScheme();
+
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ClientContext.AUTH_CACHE, null);
+
+        authStrategy.authFailed(authhost, authScheme, context);
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestAutoRetryHttpClient.java b/httpclient/src/test/java/org/apache/http/impl/client/TestAutoRetryHttpClient.java
new file mode 100644
index 0000000..5eada02
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestAutoRetryHttpClient.java
@@ -0,0 +1,122 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.HttpClient;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestAutoRetryHttpClient{
+
+    private AutoRetryHttpClient impl;
+
+    private HttpClient mockBackend;
+
+    private HttpHost host;
+
+    @Before
+    public void setUp() {
+        mockBackend = mock(HttpClient.class);
+        host = new HttpHost("foo.example.com");
+    }
+
+    @Test
+    public void testDefaultRetryConfig(){
+        DefaultServiceUnavailableRetryStrategy retryStrategy = new DefaultServiceUnavailableRetryStrategy();
+        HttpContext context = new BasicHttpContext();
+        HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 503, "Oppsie");
+        assertTrue(retryStrategy.retryRequest(response1, 1, context));
+        HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 502, "Oppsie");
+        assertFalse(retryStrategy.retryRequest(response2, 1, context));
+        assertEquals(1000, retryStrategy.getRetryInterval());
+    }
+
+    @Test
+    public void testNoAutoRetry() throws java.io.IOException{
+        DefaultServiceUnavailableRetryStrategy retryStrategy = new DefaultServiceUnavailableRetryStrategy(2, 100);
+
+        impl = new AutoRetryHttpClient(mockBackend,retryStrategy);
+
+        HttpRequest req1 = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
+        HttpResponse resp1 = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+                HttpStatus.SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
+        HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+                HttpStatus.SC_OK, "OK");
+
+        when(mockBackend.execute(host, req1,(HttpContext)null)).thenReturn(resp1).thenReturn(resp2);
+
+        HttpResponse result =  impl.execute(host, req1);
+
+        verify(mockBackend,times(1)).execute(host, req1,(HttpContext)null);
+
+        assertEquals(resp1,result);
+        assertEquals(500,result.getStatusLine().getStatusCode());
+    }
+
+    @Test
+    public void testMultipleAutoRetry() throws java.io.IOException{
+        DefaultServiceUnavailableRetryStrategy retryStrategy = new DefaultServiceUnavailableRetryStrategy(5, 100);
+
+        impl = new AutoRetryHttpClient(mockBackend,retryStrategy);
+
+        HttpRequest req1 = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1);
+        HttpResponse resp1 = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+                HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+        HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+                HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+        HttpResponse resp3 = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+                HttpStatus.SC_OK, "OK");
+
+        when(mockBackend.execute(host, req1,(HttpContext)null)).thenReturn(resp1).thenReturn(resp2).thenReturn(resp3);
+
+        HttpResponse result =  impl.execute(host, req1);
+
+        verify(mockBackend,times(3)).execute(host, req1,(HttpContext)null);
+
+        assertEquals(resp3,result);
+        assertEquals(200,result.getStatusLine().getStatusCode());
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestBasicResponseHandler.java b/httpclient/src/test/java/org/apache/http/impl/client/TestBasicResponseHandler.java
new file mode 100644
index 0000000..2595cb3
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestBasicResponseHandler.java
@@ -0,0 +1,85 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.InputStream;
+
+import junit.framework.Assert;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpResponseException;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.message.BasicStatusLine;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * Unit tests for {@link BasicResponseHandler}.
+ */
+public class TestBasicResponseHandler {
+
+    @Test
+    public void testSuccessfulResponse() throws Exception {
+        StatusLine sl = new BasicStatusLine(HttpVersion.HTTP_1_1, 200, "OK");
+        HttpResponse response = Mockito.mock(HttpResponse.class);
+        HttpEntity entity = new StringEntity("stuff");
+        Mockito.when(response.getStatusLine()).thenReturn(sl);
+        Mockito.when(response.getEntity()).thenReturn(entity);
+
+        BasicResponseHandler handler = new BasicResponseHandler();
+        String s = handler.handleResponse(response);
+        Assert.assertEquals("stuff", s);
+    }
+
+    @Test
+    public void testUnsuccessfulResponse() throws Exception {
+        InputStream instream = Mockito.mock(InputStream.class);
+        HttpEntity entity = Mockito.mock(HttpEntity.class);
+        Mockito.when(entity.isStreaming()).thenReturn(true);
+        Mockito.when(entity.getContent()).thenReturn(instream);
+        StatusLine sl = new BasicStatusLine(HttpVersion.HTTP_1_1, 404, "Not Found");
+        HttpResponse response = Mockito.mock(HttpResponse.class);
+        Mockito.when(response.getStatusLine()).thenReturn(sl);
+        Mockito.when(response.getEntity()).thenReturn(entity);
+
+        BasicResponseHandler handler = new BasicResponseHandler();
+        try {
+            handler.handleResponse(response);
+            Assert.fail("HttpResponseException expected");
+        } catch (HttpResponseException ex) {
+            Assert.assertEquals(404, ex.getStatusCode());
+            Assert.assertEquals("Not Found", ex.getMessage());
+        }
+        Mockito.verify(entity).getContent();
+        Mockito.verify(instream).close();
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthentication.java b/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthentication.java
index 5caa4c4..39813d6 100644
--- a/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthentication.java
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthentication.java
@@ -28,8 +28,10 @@ package org.apache.http.impl.client;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 
+import org.apache.http.Consts;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpStatus;
@@ -52,7 +54,6 @@ import org.apache.http.localserver.ResponseBasicUnauthorized;
 import org.apache.http.params.CoreProtocolPNames;
 import org.apache.http.protocol.BasicHttpContext;
 import org.apache.http.protocol.BasicHttpProcessor;
-import org.apache.http.protocol.HTTP;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.protocol.HttpExpectationVerifier;
 import org.apache.http.protocol.HttpRequestHandler;
@@ -80,7 +81,8 @@ public class TestClientAuthentication extends BasicServerTestBase {
         httpproc.addInterceptor(new RequestBasicAuth());
         httpproc.addInterceptor(new ResponseBasicUnauthorized());
 
-        localServer = new LocalTestServer(httpproc, null);
+        this.localServer = new LocalTestServer(httpproc, null);
+        this.httpclient = new DefaultHttpClient();
     }
 
     static class AuthHandler implements HttpRequestHandler {
@@ -94,7 +96,7 @@ public class TestClientAuthentication extends BasicServerTestBase {
                 response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
             } else {
                 response.setStatusCode(HttpStatus.SC_OK);
-                StringEntity entity = new StringEntity("success", HTTP.ASCII);
+                StringEntity entity = new StringEntity("success", Consts.ASCII);
                 response.setEntity(entity);
             }
         }
@@ -153,17 +155,17 @@ public class TestClientAuthentication extends BasicServerTestBase {
 
     @Test
     public void testBasicAuthenticationNoCreds() throws Exception {
-        localServer.register("*", new AuthHandler());
-        localServer.start();
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
 
         TestCredentialsProvider credsProvider = new TestCredentialsProvider(null);
 
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.setCredentialsProvider(credsProvider);
+
+        this.httpclient.setCredentialsProvider(credsProvider);
 
         HttpGet httpget = new HttpGet("/");
 
-        HttpResponse response = httpclient.execute(getServerHttp(), httpget);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget);
         HttpEntity entity = response.getEntity();
         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
         Assert.assertNotNull(entity);
@@ -175,18 +177,18 @@ public class TestClientAuthentication extends BasicServerTestBase {
 
     @Test
     public void testBasicAuthenticationFailure() throws Exception {
-        localServer.register("*", new AuthHandler());
-        localServer.start();
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
 
         TestCredentialsProvider credsProvider = new TestCredentialsProvider(
                 new UsernamePasswordCredentials("test", "all-wrong"));
 
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.setCredentialsProvider(credsProvider);
+
+        this.httpclient.setCredentialsProvider(credsProvider);
 
         HttpGet httpget = new HttpGet("/");
 
-        HttpResponse response = httpclient.execute(getServerHttp(), httpget);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget);
         HttpEntity entity = response.getEntity();
         Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
         Assert.assertNotNull(entity);
@@ -198,18 +200,18 @@ public class TestClientAuthentication extends BasicServerTestBase {
 
     @Test
     public void testBasicAuthenticationSuccess() throws Exception {
-        localServer.register("*", new AuthHandler());
-        localServer.start();
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
 
         TestCredentialsProvider credsProvider = new TestCredentialsProvider(
                 new UsernamePasswordCredentials("test", "test"));
 
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.setCredentialsProvider(credsProvider);
+
+        this.httpclient.setCredentialsProvider(credsProvider);
 
         HttpGet httpget = new HttpGet("/");
 
-        HttpResponse response = httpclient.execute(getServerHttp(), httpget);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget);
         HttpEntity entity = response.getEntity();
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
         Assert.assertNotNull(entity);
@@ -228,16 +230,16 @@ public class TestClientAuthentication extends BasicServerTestBase {
         httpproc.addInterceptor(new ResponseConnControl());
         httpproc.addInterceptor(new RequestBasicAuth());
         httpproc.addInterceptor(new ResponseBasicUnauthorized());
-        localServer = new LocalTestServer(
+        this.localServer = new LocalTestServer(
                 httpproc, null, null, new AuthExpectationVerifier(), null, null);
-        localServer.register("*", new AuthHandler());
-        localServer.start();
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
 
         TestCredentialsProvider credsProvider = new TestCredentialsProvider(
                 new UsernamePasswordCredentials("test", "test"));
 
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.setCredentialsProvider(credsProvider);
+
+        this.httpclient.setCredentialsProvider(credsProvider);
 
         HttpPut httpput = new HttpPut("/");
         httpput.setEntity(new InputStreamEntity(
@@ -246,7 +248,7 @@ public class TestClientAuthentication extends BasicServerTestBase {
                         -1));
         httpput.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, true);
 
-        HttpResponse response = httpclient.execute(getServerHttp(), httpput);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpput);
         HttpEntity entity = response.getEntity();
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
         Assert.assertNotNull(entity);
@@ -254,14 +256,14 @@ public class TestClientAuthentication extends BasicServerTestBase {
 
     @Test(expected=ClientProtocolException.class)
     public void testBasicAuthenticationFailureOnNonRepeatablePutDontExpectContinue() throws Exception {
-        localServer.register("*", new AuthHandler());
-        localServer.start();
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
 
         TestCredentialsProvider credsProvider = new TestCredentialsProvider(
                 new UsernamePasswordCredentials("test", "test"));
 
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.setCredentialsProvider(credsProvider);
+
+        this.httpclient.setCredentialsProvider(credsProvider);
 
         HttpPut httpput = new HttpPut("/");
         httpput.setEntity(new InputStreamEntity(
@@ -271,7 +273,7 @@ public class TestClientAuthentication extends BasicServerTestBase {
         httpput.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
 
         try {
-            httpclient.execute(getServerHttp(), httpput);
+            this.httpclient.execute(getServerHttp(), httpput);
             Assert.fail("ClientProtocolException should have been thrown");
         } catch (ClientProtocolException ex) {
             Throwable cause = ex.getCause();
@@ -283,19 +285,19 @@ public class TestClientAuthentication extends BasicServerTestBase {
 
     @Test
     public void testBasicAuthenticationSuccessOnRepeatablePost() throws Exception {
-        localServer.register("*", new AuthHandler());
-        localServer.start();
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
 
         TestCredentialsProvider credsProvider = new TestCredentialsProvider(
                 new UsernamePasswordCredentials("test", "test"));
 
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.setCredentialsProvider(credsProvider);
+
+        this.httpclient.setCredentialsProvider(credsProvider);
 
         HttpPost httppost = new HttpPost("/");
-        httppost.setEntity(new StringEntity("some important stuff", HTTP.ISO_8859_1));
+        httppost.setEntity(new StringEntity("some important stuff", Consts.ASCII));
 
-        HttpResponse response = httpclient.execute(getServerHttp(), httppost);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httppost);
         HttpEntity entity = response.getEntity();
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
         Assert.assertNotNull(entity);
@@ -307,14 +309,14 @@ public class TestClientAuthentication extends BasicServerTestBase {
 
     @Test(expected=ClientProtocolException.class)
     public void testBasicAuthenticationFailureOnNonRepeatablePost() throws Exception {
-        localServer.register("*", new AuthHandler());
-        localServer.start();
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
 
         TestCredentialsProvider credsProvider = new TestCredentialsProvider(
                 new UsernamePasswordCredentials("test", "test"));
 
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.setCredentialsProvider(credsProvider);
+
+        this.httpclient.setCredentialsProvider(credsProvider);
 
         HttpPost httppost = new HttpPost("/");
         httppost.setEntity(new InputStreamEntity(
@@ -322,7 +324,7 @@ public class TestClientAuthentication extends BasicServerTestBase {
                         new byte[] { 0,1,2,3,4,5,6,7,8,9 }), -1));
 
         try {
-            httpclient.execute(getServerHttp(), httppost);
+            this.httpclient.execute(getServerHttp(), httppost);
             Assert.fail("ClientProtocolException should have been thrown");
         } catch (ClientProtocolException ex) {
             Throwable cause = ex.getCause();
@@ -332,20 +334,21 @@ public class TestClientAuthentication extends BasicServerTestBase {
         }
     }
 
-    static class TestTargetAuthenticationHandler extends DefaultTargetAuthenticationHandler {
+    static class TestTargetAuthenticationStrategy extends TargetAuthenticationStrategy {
 
         private int count;
 
-        public TestTargetAuthenticationHandler() {
+        public TestTargetAuthenticationStrategy() {
             super();
             this.count = 0;
         }
 
         @Override
         public boolean isAuthenticationRequested(
+                final HttpHost host,
                 final HttpResponse response,
                 final HttpContext context) {
-            boolean res = super.isAuthenticationRequested(response, context);
+            boolean res = super.isAuthenticationRequested(host, response, context);
             if (res == true) {
                 synchronized (this) {
                     this.count++;
@@ -364,36 +367,65 @@ public class TestClientAuthentication extends BasicServerTestBase {
 
     @Test
     public void testBasicAuthenticationCredentialsCaching() throws Exception {
-        localServer.register("*", new AuthHandler());
-        localServer.start();
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
 
         BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
         credsProvider.setCredentials(AuthScope.ANY,
                 new UsernamePasswordCredentials("test", "test"));
 
-        TestTargetAuthenticationHandler authHandler = new TestTargetAuthenticationHandler();
+        TestTargetAuthenticationStrategy authStrategy = new TestTargetAuthenticationStrategy();
 
-        DefaultHttpClient httpclient = new DefaultHttpClient();
-        httpclient.setCredentialsProvider(credsProvider);
-        httpclient.setTargetAuthenticationHandler(authHandler);
+        this.httpclient.setCredentialsProvider(credsProvider);
+        this.httpclient.setTargetAuthenticationStrategy(authStrategy);
 
         HttpContext context = new BasicHttpContext();
 
         HttpGet httpget = new HttpGet("/");
 
-        HttpResponse response1 = httpclient.execute(getServerHttp(), httpget, context);
+        HttpResponse response1 = this.httpclient.execute(getServerHttp(), httpget, context);
         HttpEntity entity1 = response1.getEntity();
         Assert.assertEquals(HttpStatus.SC_OK, response1.getStatusLine().getStatusCode());
         Assert.assertNotNull(entity1);
         EntityUtils.consume(entity1);
 
-        HttpResponse response2 = httpclient.execute(getServerHttp(), httpget, context);
+        HttpResponse response2 = this.httpclient.execute(getServerHttp(), httpget, context);
         HttpEntity entity2 = response1.getEntity();
         Assert.assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
         Assert.assertNotNull(entity2);
         EntityUtils.consume(entity2);
 
-        Assert.assertEquals(1, authHandler.getCount());
+        Assert.assertEquals(1, authStrategy.getCount());
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRequestSuccess() throws Exception {
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
+
+        HttpHost target = getServerHttp();
+        HttpGet httpget = new HttpGet("http://test:test@" +  target.toHostString() + "/");
+
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget);
+        HttpEntity entity = response.getEntity();
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertNotNull(entity);
+        EntityUtils.consume(entity);
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRequestFailure() throws Exception {
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
+
+        HttpHost target = getServerHttp();
+        HttpGet httpget = new HttpGet("http://test:all-wrong@" +  target.toHostString() + "/");
+
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget);
+        HttpEntity entity = response.getEntity();
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
+        Assert.assertNotNull(entity);
+        EntityUtils.consume(entity);
     }
 
 }
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthenticationFallBack.java b/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthenticationFallBack.java
new file mode 100644
index 0000000..1dcbe2e
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthenticationFallBack.java
@@ -0,0 +1,156 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+
+import org.apache.http.Consts;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.localserver.BasicServerTestBase;
+import org.apache.http.localserver.LocalTestServer;
+import org.apache.http.localserver.RequestBasicAuth;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.protocol.ResponseConnControl;
+import org.apache.http.protocol.ResponseContent;
+import org.apache.http.protocol.ResponseDate;
+import org.apache.http.protocol.ResponseServer;
+import org.apache.http.util.EntityUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestClientAuthenticationFallBack extends BasicServerTestBase {
+
+    public class ResponseBasicUnauthorized implements HttpResponseInterceptor {
+
+        public void process(
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
+                response.addHeader(AUTH.WWW_AUTH, "Digest realm=\"test realm\" invalid");
+                response.addHeader(AUTH.WWW_AUTH, "Basic realm=\"test realm\"");
+            }
+        }
+
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        BasicHttpProcessor httpproc = new BasicHttpProcessor();
+        httpproc.addInterceptor(new ResponseDate());
+        httpproc.addInterceptor(new ResponseServer());
+        httpproc.addInterceptor(new ResponseContent());
+        httpproc.addInterceptor(new ResponseConnControl());
+        httpproc.addInterceptor(new RequestBasicAuth());
+        httpproc.addInterceptor(new ResponseBasicUnauthorized());
+
+        this.localServer = new LocalTestServer(httpproc, null);
+        this.httpclient = new DefaultHttpClient();
+    }
+
+    static class AuthHandler implements HttpRequestHandler {
+
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            String creds = (String) context.getAttribute("creds");
+            if (creds == null || !creds.equals("test:test")) {
+                response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
+            } else {
+                response.setStatusCode(HttpStatus.SC_OK);
+                StringEntity entity = new StringEntity("success", Consts.ASCII);
+                response.setEntity(entity);
+            }
+        }
+
+    }
+
+    static class TestCredentialsProvider implements CredentialsProvider {
+
+        private final Credentials creds;
+        private AuthScope authscope;
+
+        TestCredentialsProvider(final Credentials creds) {
+            super();
+            this.creds = creds;
+        }
+
+        public void clear() {
+        }
+
+        public Credentials getCredentials(AuthScope authscope) {
+            this.authscope = authscope;
+            return this.creds;
+        }
+
+        public void setCredentials(AuthScope authscope, Credentials credentials) {
+        }
+
+        public AuthScope getAuthScope() {
+            return this.authscope;
+        }
+
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccess() throws Exception {
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
+
+        TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test"));
+
+
+        this.httpclient.setCredentialsProvider(credsProvider);
+
+        HttpGet httpget = new HttpGet("/");
+
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget);
+        HttpEntity entity = response.getEntity();
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertNotNull(entity);
+        EntityUtils.consume(entity);
+        AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestClientReauthentication.java b/httpclient/src/test/java/org/apache/http/impl/client/TestClientReauthentication.java
new file mode 100644
index 0000000..7cde9c4
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestClientReauthentication.java
@@ -0,0 +1,162 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.http.impl.client;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.http.Consts;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.localserver.BasicServerTestBase;
+import org.apache.http.localserver.LocalTestServer;
+import org.apache.http.localserver.RequestBasicAuth;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.protocol.ResponseConnControl;
+import org.apache.http.protocol.ResponseContent;
+import org.apache.http.protocol.ResponseDate;
+import org.apache.http.protocol.ResponseServer;
+import org.apache.http.util.EntityUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestClientReauthentication extends BasicServerTestBase {
+
+    public class ResponseBasicUnauthorized implements HttpResponseInterceptor {
+
+        public void process(
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
+                response.addHeader(AUTH.WWW_AUTH, "Basic realm=\"test realm\"");
+            }
+        }
+
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        BasicHttpProcessor httpproc = new BasicHttpProcessor();
+        httpproc.addInterceptor(new ResponseDate());
+        httpproc.addInterceptor(new ResponseServer());
+        httpproc.addInterceptor(new ResponseContent());
+        httpproc.addInterceptor(new ResponseConnControl());
+        httpproc.addInterceptor(new RequestBasicAuth());
+        httpproc.addInterceptor(new ResponseBasicUnauthorized());
+
+        this.localServer = new LocalTestServer(httpproc, null);
+        this.httpclient = new DefaultHttpClient();
+    }
+
+    static class AuthHandler implements HttpRequestHandler {
+
+        private AtomicLong count = new AtomicLong(0);
+
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            String creds = (String) context.getAttribute("creds");
+            if (creds == null || !creds.equals("test:test")) {
+                response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
+            } else {
+                // Make client re-authenticate on each fourth request
+                if (this.count.incrementAndGet() % 4 == 0) {
+                    response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
+                } else {
+                    response.setStatusCode(HttpStatus.SC_OK);
+                    StringEntity entity = new StringEntity("success", Consts.ASCII);
+                    response.setEntity(entity);
+                }
+            }
+        }
+
+    }
+
+    static class TestCredentialsProvider implements CredentialsProvider {
+
+        private final Credentials creds;
+        private AuthScope authscope;
+
+        TestCredentialsProvider(final Credentials creds) {
+            super();
+            this.creds = creds;
+        }
+
+        public void clear() {
+        }
+
+        public Credentials getCredentials(AuthScope authscope) {
+            this.authscope = authscope;
+            return this.creds;
+        }
+
+        public void setCredentials(AuthScope authscope, Credentials credentials) {
+        }
+
+        public AuthScope getAuthScope() {
+            return this.authscope;
+        }
+
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccess() throws Exception {
+        this.localServer.register("*", new AuthHandler());
+        this.localServer.start();
+
+        TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test"));
+
+        this.httpclient.setCredentialsProvider(credsProvider);
+
+        HttpContext context = new BasicHttpContext();
+        for (int i = 0; i < 10; i++) {
+            HttpGet httpget = new HttpGet("/");
+            HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
+            HttpEntity entity = response.getEntity();
+            Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+            Assert.assertNotNull(entity);
+            EntityUtils.consume(entity);
+        }
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestContentCodings.java b/httpclient/src/test/java/org/apache/http/impl/client/TestContentCodings.java
index dff256e..f04393d 100644
--- a/httpclient/src/test/java/org/apache/http/impl/client/TestContentCodings.java
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestContentCodings.java
@@ -49,13 +49,12 @@ import org.apache.http.HttpStatus;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.entity.DeflateDecompressingEntity;
 import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.protocol.RequestAcceptEncoding;
 import org.apache.http.conn.scheme.PlainSocketFactory;
 import org.apache.http.conn.scheme.Scheme;
 import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.entity.InputStreamEntity;
 import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
 import org.apache.http.localserver.ServerTestBase;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.protocol.HttpRequestHandler;
@@ -92,7 +91,7 @@ public class TestContentCodings extends ServerTestBase {
             }
         });
 
-        DefaultHttpClient client = createHttpClient();
+        HttpClient client = new DecompressingHttpClient(new DefaultHttpClient());
 
         HttpGet request = new HttpGet("/some-resource");
         HttpResponse response = client.execute(getServerHttp(), request);
@@ -115,7 +114,7 @@ public class TestContentCodings extends ServerTestBase {
 
         this.localServer.register("*", createDeflateEncodingRequestHandler(entityText, false));
 
-        DefaultHttpClient client = createHttpClient();
+        HttpClient client = new DecompressingHttpClient(new DefaultHttpClient());
 
         HttpGet request = new HttpGet("/some-resource");
         HttpResponse response = client.execute(getServerHttp(), request);
@@ -138,7 +137,8 @@ public class TestContentCodings extends ServerTestBase {
 
         this.localServer.register("*", createDeflateEncodingRequestHandler(entityText, true));
 
-        DefaultHttpClient client = createHttpClient();
+        HttpClient client = new DecompressingHttpClient(new DefaultHttpClient());
+
         HttpGet request = new HttpGet("/some-resource");
         HttpResponse response = client.execute(getServerHttp(), request);
         Assert.assertEquals("The entity text is correctly transported", entityText,
@@ -158,7 +158,8 @@ public class TestContentCodings extends ServerTestBase {
 
         this.localServer.register("*", createGzipEncodingRequestHandler(entityText));
 
-        DefaultHttpClient client = createHttpClient();
+        HttpClient client = new DecompressingHttpClient(new DefaultHttpClient());
+
         HttpGet request = new HttpGet("/some-resource");
         HttpResponse response = client.execute(getServerHttp(), request);
         Assert.assertEquals("The entity text is correctly transported", entityText,
@@ -188,7 +189,7 @@ public class TestContentCodings extends ServerTestBase {
         SchemeRegistry schemeRegistry = new SchemeRegistry();
         schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
 
-        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(schemeRegistry);
+        PoolingClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry);
         cm.setMaxTotal(clients);
 
         final HttpClient httpClient = new DefaultHttpClient(cm);
@@ -224,48 +225,6 @@ public class TestContentCodings extends ServerTestBase {
     }
 
     /**
-     * Checks that we can turn off the new Content-Coding support. The default is that it's on, but that is a change
-     * to existing behaviour and might not be desirable in some situations.
-     *
-     * @throws Exception
-     */
-    @Test
-    public void testCanBeDisabledAtRequestTime() throws Exception {
-        final String entityText = "Hello, this is some plain text coming back.";
-
-        /* Assume that server will see an Accept-Encoding header. */
-        final boolean [] sawAcceptEncodingHeader = { true };
-
-        this.localServer.register("*", new HttpRequestHandler() {
-
-            /**
-             * {@inheritDoc}
-             */
-            public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
-                response.setEntity(new StringEntity(entityText));
-                response.addHeader("Content-Type", "text/plain");
-                Header[] acceptEncodings = request.getHeaders("Accept-Encoding");
-
-                sawAcceptEncodingHeader[0] = acceptEncodings.length > 0;
-            }
-
-        });
-
-        AbstractHttpClient client = createHttpClient();
-        HttpGet request = new HttpGet("/some-resource");
-
-        client.removeRequestInterceptorByClass(RequestAcceptEncoding.class);
-
-        HttpResponse response = client.execute(getServerHttp(), request);
-
-        Assert.assertFalse("The Accept-Encoding header was not there", sawAcceptEncodingHeader[0]);
-        Assert.assertEquals("The entity isn't treated as gzip or zip content", entityText,
-                EntityUtils.toString(response.getEntity()));
-
-        client.getConnectionManager().shutdown();
-    }
-
-    /**
      * Test that the returned {@link HttpEntity} in the response correctly overrides
      * {@link HttpEntity#writeTo(OutputStream)} for gzip-encoding.
      *
@@ -277,7 +236,7 @@ public class TestContentCodings extends ServerTestBase {
 
         this.localServer.register("*", createGzipEncodingRequestHandler(entityText));
 
-        DefaultHttpClient client = createHttpClient();
+        HttpClient client = new DecompressingHttpClient(new DefaultHttpClient());
         HttpGet request = new HttpGet("/some-resource");
         HttpResponse response = client.execute(getServerHttp(), request);
         ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -301,7 +260,8 @@ public class TestContentCodings extends ServerTestBase {
 
         this.localServer.register("*", createDeflateEncodingRequestHandler(entityText, true));
 
-        DefaultHttpClient client = createHttpClient();
+        HttpClient client = new DecompressingHttpClient(new DefaultHttpClient());
+
         HttpGet request = new HttpGet("/some-resource");
         HttpResponse response = client.execute(getServerHttp(), request);
         ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -313,6 +273,36 @@ public class TestContentCodings extends ServerTestBase {
         client.getConnectionManager().shutdown();
     }
 
+    @Test
+    public void gzipResponsesWorkWithBasicResponseHandler() throws Exception {
+        final String entityText = "Hello, this is some plain text coming back.";
+
+        this.localServer.register("*", createGzipEncodingRequestHandler(entityText));
+
+        HttpClient client = new DecompressingHttpClient(new DefaultHttpClient());
+
+        HttpGet request = new HttpGet("/some-resource");
+        String response = client.execute(getServerHttp(), request, new BasicResponseHandler());
+        Assert.assertEquals("The entity text is correctly transported", entityText, response);
+
+        client.getConnectionManager().shutdown();
+    }
+
+    @Test
+    public void deflateResponsesWorkWithBasicResponseHandler() throws Exception {
+        final String entityText = "Hello, this is some plain text coming back.";
+
+        this.localServer.register("*", createDeflateEncodingRequestHandler(entityText, false));
+
+        HttpClient client = new DecompressingHttpClient(new DefaultHttpClient());
+        
+        HttpGet request = new HttpGet("/some-resource");
+        String response = client.execute(getServerHttp(), request, new BasicResponseHandler());
+        Assert.assertEquals("The entity text is correctly transported", entityText, response);
+
+        client.getConnectionManager().shutdown();
+    }
+
     /**
      * Creates a new {@link HttpRequestHandler} that will attempt to provide a deflate stream
      * Content-Coding.
@@ -429,10 +419,6 @@ public class TestContentCodings extends ServerTestBase {
         };
     }
 
-    private DefaultHttpClient createHttpClient() {
-        return new ContentEncodingHttpClient();
-    }
-
     /**
      * Sub-ordinate task passed off to a different thread to be executed.
      *
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestDecompressingHttpClient.java b/httpclient/src/test/java/org/apache/http/impl/client/TestDecompressingHttpClient.java
new file mode 100644
index 0000000..efb8334
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestDecompressingHttpClient.java
@@ -0,0 +1,364 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.http.impl.client;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+ at RunWith(MockitoJUnitRunner.class)
+public class TestDecompressingHttpClient {
+    
+    private DummyHttpClient backend;
+    @Mock private ClientConnectionManager mockConnManager;
+    @Mock private ResponseHandler<Object> mockHandler;
+    private DecompressingHttpClient impl;
+    private HttpUriRequest request;
+    private HttpContext ctx;
+    private HttpHost host;
+    @Mock private HttpResponse mockResponse;
+    @Mock private HttpEntity mockEntity;
+    private Object handled;
+    
+    @Before
+    public void canCreate() {
+        handled = new Object();
+        backend = new DummyHttpClient();
+        impl = new DecompressingHttpClient(backend);
+        request = new HttpGet("http://localhost:8080");
+        ctx = new BasicHttpContext();
+        host = new HttpHost("www.example.com");
+    }
+    
+    @Test
+    public void isAnHttpClient() {
+        assertTrue(impl instanceof HttpClient);
+    }
+    
+    @Test
+    public void usesParamsFromBackend() {
+        HttpParams params = new BasicHttpParams();
+        backend.setParams(params);
+        assertSame(params, impl.getParams());
+    }
+    
+    @Test
+    public void extractsHostNameFromUriRequest() {
+        assertEquals(new HttpHost("www.example.com"), 
+                impl.getHttpHost(new HttpGet("http://www.example.com/")));
+    }
+    
+    @Test
+    public void extractsHostNameAndPortFromUriRequest() {
+        assertEquals(new HttpHost("www.example.com", 8080), 
+                impl.getHttpHost(new HttpGet("http://www.example.com:8080/")));
+    }
+
+    @Test
+    public void extractsIPAddressFromUriRequest() {
+        assertEquals(new HttpHost("10.0.0.1"), 
+                impl.getHttpHost(new HttpGet("http://10.0.0.1/")));
+    }
+
+    @Test
+    public void extractsIPAddressAndPortFromUriRequest() {
+        assertEquals(new HttpHost("10.0.0.1", 8080), 
+                impl.getHttpHost(new HttpGet("http://10.0.0.1:8080/")));
+    }
+
+    @Test
+    public void extractsLocalhostFromUriRequest() {
+        assertEquals(new HttpHost("localhost"), 
+                impl.getHttpHost(new HttpGet("http://localhost/")));
+    }
+
+    @Test
+    public void extractsLocalhostAndPortFromUriRequest() {
+        assertEquals(new HttpHost("localhost", 8080), 
+                impl.getHttpHost(new HttpGet("http://localhost:8080/")));
+    }
+    
+    @Test
+    public void usesConnectionManagerFromBackend() {
+        backend.setConnectionManager(mockConnManager);
+        assertSame(mockConnManager, impl.getConnectionManager());
+    }
+    
+    private void assertAcceptEncodingGzipAndDeflateWereAddedToRequest(HttpRequest captured) {
+        boolean foundGzip = false;
+        boolean foundDeflate = false;
+        for(Header h : captured.getHeaders("Accept-Encoding")) {
+            for(HeaderElement elt : h.getElements()) {
+                if ("gzip".equals(elt.getName())) foundGzip = true;
+                if ("deflate".equals(elt.getName())) foundDeflate = true;
+            }
+        }
+        assertTrue(foundGzip);
+        assertTrue(foundDeflate);
+    }
+    
+    @Test
+    public void addsAcceptEncodingHeaderToHttpUriRequest() throws Exception {
+        impl.execute(request);
+        assertAcceptEncodingGzipAndDeflateWereAddedToRequest(backend.getCapturedRequest());
+    }
+
+    @Test
+    public void addsAcceptEncodingHeaderToHttpUriRequestWithContext() throws Exception {
+        impl.execute(request, ctx);
+        assertAcceptEncodingGzipAndDeflateWereAddedToRequest(backend.getCapturedRequest());
+    }
+    
+    @Test
+    public void addsAcceptEncodingHeaderToHostAndHttpRequest() throws Exception {
+        impl.execute(host, request);
+        assertAcceptEncodingGzipAndDeflateWereAddedToRequest(backend.getCapturedRequest());
+    }
+    
+    @Test
+    public void addsAcceptEncodingHeaderToHostAndHttpRequestWithContext() throws Exception {
+        impl.execute(host, request, ctx);
+        assertAcceptEncodingGzipAndDeflateWereAddedToRequest(backend.getCapturedRequest());
+    }
+    
+    @Test
+    public void addsAcceptEncodingHeaderToUriRequestWithHandler() throws Exception {
+        when(mockHandler.handleResponse(isA(HttpResponse.class))).thenReturn(new Object());
+        impl.execute(request, mockHandler);
+        assertAcceptEncodingGzipAndDeflateWereAddedToRequest(backend.getCapturedRequest());
+    }
+
+    @Test
+    public void addsAcceptEncodingHeaderToUriRequestWithHandlerAndContext() throws Exception {
+        when(mockHandler.handleResponse(isA(HttpResponse.class))).thenReturn(new Object());
+        impl.execute(request, mockHandler, ctx);
+        assertAcceptEncodingGzipAndDeflateWereAddedToRequest(backend.getCapturedRequest());
+    }
+
+    @Test
+    public void addsAcceptEncodingHeaderToRequestWithHostAndHandler() throws Exception {
+        when(mockHandler.handleResponse(isA(HttpResponse.class))).thenReturn(new Object());
+        impl.execute(host, request, mockHandler);
+        assertAcceptEncodingGzipAndDeflateWereAddedToRequest(backend.getCapturedRequest());
+    }
+
+    @Test
+    public void addsAcceptEncodingHeaderToRequestWithHostAndContextAndHandler() throws Exception {
+        when(mockHandler.handleResponse(isA(HttpResponse.class))).thenReturn(new Object());
+        impl.execute(host, request, mockHandler, ctx);
+        assertAcceptEncodingGzipAndDeflateWereAddedToRequest(backend.getCapturedRequest());
+    }
+
+    private void mockResponseHasNoContentEncodingHeaders() {
+        backend.setResponse(mockResponse);
+        when(mockResponse.getAllHeaders()).thenReturn(new Header[]{});
+        when(mockResponse.getHeaders("Content-Encoding")).thenReturn(new Header[]{});
+        when(mockResponse.getFirstHeader("Content-Encoding")).thenReturn(null);
+        when(mockResponse.getLastHeader("Content-Encoding")).thenReturn(null);
+        when(mockResponse.getEntity()).thenReturn(mockEntity);
+    }
+    
+    @Test
+    public void doesNotModifyResponseBodyIfNoContentEncoding() throws Exception {
+        mockResponseHasNoContentEncodingHeaders();
+        assertSame(mockResponse, impl.execute(request));
+        verify(mockResponse, never()).setEntity(any(HttpEntity.class));
+    }
+    
+    @Test
+    public void doesNotModifyResponseBodyIfNoContentEncodingWithContext() throws Exception {
+        mockResponseHasNoContentEncodingHeaders();
+        assertSame(mockResponse, impl.execute(request, ctx));
+        verify(mockResponse, never()).setEntity(any(HttpEntity.class));
+    }
+    
+    @Test
+    public void doesNotModifyResponseBodyIfNoContentEncodingForHostRequest() throws Exception {
+        mockResponseHasNoContentEncodingHeaders();
+        assertSame(mockResponse, impl.execute(host, request));
+        verify(mockResponse, never()).setEntity(any(HttpEntity.class));
+    }
+    
+    @Test
+    public void doesNotModifyResponseBodyIfNoContentEncodingForHostRequestWithContext() throws Exception {
+        mockResponseHasNoContentEncodingHeaders();
+        assertSame(mockResponse, impl.execute(host, request, ctx));
+        verify(mockResponse, never()).setEntity(any(HttpEntity.class));
+    }
+    
+    @Test
+    public void doesNotModifyResponseBodyWithHandlerIfNoContentEncoding() throws Exception {
+        mockResponseHasNoContentEncodingHeaders();
+        when(mockHandler.handleResponse(mockResponse)).thenReturn(handled);
+        assertSame(handled, impl.execute(request, mockHandler));
+        verify(mockResponse, never()).setEntity(any(HttpEntity.class));
+    }
+    
+    @Test
+    public void doesNotModifyResponseBodyWithHandlerAndContextIfNoContentEncoding() throws Exception {
+        mockResponseHasNoContentEncodingHeaders();
+        when(mockHandler.handleResponse(mockResponse)).thenReturn(handled);
+        assertSame(handled, impl.execute(request, mockHandler, ctx));
+        verify(mockResponse, never()).setEntity(any(HttpEntity.class));
+    }
+    
+    @Test
+    public void doesNotModifyResponseBodyWithHostAndHandlerIfNoContentEncoding() throws Exception {
+        mockResponseHasNoContentEncodingHeaders();
+        when(mockHandler.handleResponse(mockResponse)).thenReturn(handled);
+        assertSame(handled, impl.execute(host, request, mockHandler));
+        verify(mockResponse, never()).setEntity(any(HttpEntity.class));
+    }
+    
+    @Test
+    public void doesNotModifyResponseBodyWithHostAndHandlerAndContextIfNoContentEncoding() throws Exception {
+        mockResponseHasNoContentEncodingHeaders();
+        when(mockHandler.handleResponse(mockResponse)).thenReturn(handled);
+        assertSame(handled, impl.execute(host, request, mockHandler, ctx));
+        verify(mockResponse, never()).setEntity(any(HttpEntity.class));
+    }
+    
+    @Test
+    public void successfullyUncompressesContent() throws Exception {
+        final String plainText = "hello\n";
+        HttpResponse response = getGzippedResponse(plainText);
+        backend.setResponse(response);
+        
+        HttpResponse result = impl.execute(request);
+        ByteArrayOutputStream resultBuf = new ByteArrayOutputStream();
+        InputStream is = result.getEntity().getContent();
+        int b;
+        while((b = is.read()) != -1) {
+            resultBuf.write(b);
+        }
+        is.close();
+        assertEquals(plainText, new String(resultBuf.toByteArray()));
+    }
+    
+    @Test
+    public void uncompressedResponseHasUnknownLength() throws Exception {
+        final String plainText = "hello\n";
+        HttpResponse response = getGzippedResponse(plainText);
+        backend.setResponse(response);
+        
+        HttpResponse result = impl.execute(request);
+        HttpEntity entity = result.getEntity();
+        assertEquals(-1, entity.getContentLength());
+        EntityUtils.consume(entity);
+        assertNull(result.getFirstHeader("Content-Length"));
+    }
+
+    @Test
+    public void uncompressedResponseIsNotEncoded() throws Exception {
+        final String plainText = "hello\n";
+        HttpResponse response = getGzippedResponse(plainText);
+        backend.setResponse(response);
+        
+        HttpResponse result = impl.execute(request);
+        assertNull(result.getFirstHeader("Content-Encoding"));
+    }
+    
+    @Test
+    public void uncompressedResponseHasContentMD5Removed() throws Exception {
+        final String plainText = "hello\n";
+        HttpResponse response = getGzippedResponse(plainText);
+        response.setHeader("Content-MD5","a checksum");
+        backend.setResponse(response);
+        
+        HttpResponse result = impl.execute(request);
+        assertNull(result.getFirstHeader("Content-MD5"));
+    }
+    
+    @Test
+    public void unencodedResponseRetainsContentMD5() throws Exception {
+        final String plainText = "hello\n";
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+        response.setHeader("Content-MD5","a checksum");
+        response.setEntity(new ByteArrayEntity(plainText.getBytes()));
+        backend.setResponse(response);
+        
+        HttpResponse result = impl.execute(request);
+        assertNotNull(result.getFirstHeader("Content-MD5"));
+    }
+    
+    @Test
+    public void passesThroughTheBodyOfAPOST() throws Exception {
+    	when(mockHandler.handleResponse(isA(HttpResponse.class))).thenReturn(new Object());
+    	HttpPost post = new HttpPost("http://localhost:8080/");
+    	post.setEntity(new ByteArrayEntity("hello".getBytes()));
+    	impl.execute(host, post, mockHandler, ctx);
+    	assertNotNull(((HttpEntityEnclosingRequest)backend.getCapturedRequest()).getEntity());
+    }
+    
+    private HttpResponse getGzippedResponse(final String plainText)
+            throws IOException {
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+        response.setHeader("Content-Encoding","gzip");
+        response.setHeader("Content-Type","text/plain");
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        GZIPOutputStream gos = new GZIPOutputStream(buf);
+        gos.write(plainText.getBytes());
+        gos.close();
+        ByteArrayEntity body = new ByteArrayEntity(buf.toByteArray());
+        body.setContentEncoding("gzip");
+        body.setContentType("text/plain");
+        response.setHeader("Content-Length", "" + (int)body.getContentLength());
+        response.setEntity(body);
+        return response;
+    }
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultBackoffStrategy.java b/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultBackoffStrategy.java
new file mode 100644
index 0000000..015e8e2
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultBackoffStrategy.java
@@ -0,0 +1,88 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import static org.junit.Assert.*;
+
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.client.ConnectionBackoffStrategy;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.message.BasicHttpResponse;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class TestDefaultBackoffStrategy {
+
+    private DefaultBackoffStrategy impl;
+
+    @Before
+    public void setUp() {
+        impl = new DefaultBackoffStrategy();
+    }
+
+    @Test
+    public void isABackoffStrategy() {
+        assertTrue(impl instanceof ConnectionBackoffStrategy);
+    }
+
+    @Test
+    public void backsOffForSocketTimeouts() {
+        assertTrue(impl.shouldBackoff(new SocketTimeoutException()));
+    }
+
+    @Test
+    public void backsOffForConnectionTimeouts() {
+        assertTrue(impl.shouldBackoff(new ConnectException()));
+    }
+
+    @Test
+    public void doesNotBackOffForConnectionManagerTimeout() {
+        assertFalse(impl.shouldBackoff(new ConnectionPoolTimeoutException()));
+    }
+
+    @Test
+    public void backsOffForServiceUnavailable() {
+        HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+                HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+        assertTrue(impl.shouldBackoff(resp));
+    }
+
+    @Test
+    public void doesNotBackOffForNon503StatusCodes() {
+        for(int i = 100; i <= 599; i++) {
+            if (i == HttpStatus.SC_SERVICE_UNAVAILABLE) continue;
+            HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+                    i, "Foo");
+            assertFalse(impl.shouldBackoff(resp));
+        }
+    }
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultClientRequestDirector.java b/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultClientRequestDirector.java
index 149a859..22340a9 100644
--- a/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultClientRequestDirector.java
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultClientRequestDirector.java
@@ -27,10 +27,6 @@ package org.apache.http.impl.client;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.net.ConnectException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.http.Header;
 import org.apache.http.HttpClientConnection;
@@ -40,34 +36,16 @@ import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequestInterceptor;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpStatus;
-import org.apache.http.ProtocolVersion;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.HttpRequestRetryHandler;
 import org.apache.http.client.NonRepeatableRequestException;
-import org.apache.http.client.methods.AbortableHttpRequest;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.client.params.ClientPNames;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.ClientConnectionRequest;
-import org.apache.http.conn.ConnectionPoolTimeoutException;
-import org.apache.http.conn.ConnectionReleaseTrigger;
-import org.apache.http.conn.ManagedClientConnection;
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.entity.InputStreamEntity;
 import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.conn.ClientConnAdapterMockup;
-import org.apache.http.impl.conn.SingleClientConnManager;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
 import org.apache.http.localserver.BasicServerTestBase;
 import org.apache.http.localserver.LocalTestServer;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.mockup.SocketFactoryMockup;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.BasicHttpContext;
 import org.apache.http.protocol.ExecutionContext;
 import org.apache.http.protocol.HttpContext;
@@ -85,530 +63,151 @@ public class TestDefaultClientRequestDirector extends BasicServerTestBase {
 
     @Before
     public void setUp() throws Exception {
-        localServer = new LocalTestServer(null, null);
-        localServer.registerDefaultHandlers();
-        localServer.start();
+        this.localServer = new LocalTestServer(null, null);
+        this.localServer.registerDefaultHandlers();
+        this.localServer.start();
+        this.httpclient = new DefaultHttpClient();
     }
 
-    /**
-     * Tests that if abort is called on an {@link AbortableHttpRequest} while
-     * {@link DefaultRequestDirector} is allocating a connection, that the
-     * connection is properly aborted.
-     */
-    @Test
-    public void testAbortInAllocate() throws Exception {
-        CountDownLatch connLatch = new CountDownLatch(1);
-        CountDownLatch awaitLatch = new CountDownLatch(1);
-        final ConMan conMan = new ConMan(connLatch, awaitLatch);
-        final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
-        final CountDownLatch getLatch = new CountDownLatch(1);
-        final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
-        final HttpContext context = new BasicHttpContext();
-        final HttpGet httpget = new HttpGet("http://www.example.com/a");
-
-        new Thread(new Runnable() {
-            public void run() {
-                try {
-                    client.execute(httpget, context);
-                } catch(Throwable t) {
-                    throwableRef.set(t);
-                } finally {
-                    getLatch.countDown();
-                }
-            }
-        }).start();
-
-        Assert.assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS));
-
-        httpget.abort();
-
-        Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
-        Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
-                throwableRef.get() instanceof IOException);
-        Assert.assertTrue("cause should be InterruptedException, was: " + throwableRef.get().getCause(),
-                throwableRef.get().getCause() instanceof InterruptedException);
-    }
-
-    /**
-     * Tests that an abort called after the connection has been retrieved
-     * but before a release trigger is set does still abort the request.
-     */
-    @Test
-    public void testAbortAfterAllocateBeforeRequest() throws Exception {
-        this.localServer.register("*", new BasicService());
-
-        CountDownLatch releaseLatch = new CountDownLatch(1);
-        SchemeRegistry registry = new SchemeRegistry();
-        registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
-
-        SingleClientConnManager conMan = new SingleClientConnManager(registry);
-        final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
-        final CountDownLatch getLatch = new CountDownLatch(1);
-        final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
-        final HttpContext context = new BasicHttpContext();
-        final HttpGet httpget = new CustomGet("a", releaseLatch);
-
-        new Thread(new Runnable() {
-            public void run() {
-                try {
-                    client.execute(getServerHttp(), httpget, context);
-                } catch(Throwable t) {
-                    throwableRef.set(t);
-                } finally {
-                    getLatch.countDown();
-                }
-            }
-        }).start();
-
-        Thread.sleep(100); // Give it a little time to proceed to release...
-
-        httpget.abort();
-
-        releaseLatch.countDown();
-
-        Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
-        Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
-                throwableRef.get() instanceof IOException);
-    }
-
-    /**
-     * Tests that an abort called completely before execute
-     * still aborts the request.
-     */
-    @Test
-    public void testAbortBeforeExecute() throws Exception {
-        this.localServer.register("*", new BasicService());
-
-        SchemeRegistry registry = new SchemeRegistry();
-        registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
-
-        SingleClientConnManager conMan = new SingleClientConnManager(registry);
-        final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
-        final CountDownLatch getLatch = new CountDownLatch(1);
-        final CountDownLatch startLatch = new CountDownLatch(1);
-        final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
-        final HttpContext context = new BasicHttpContext();
-        final HttpGet httpget = new HttpGet("a");
-
-        new Thread(new Runnable() {
-            public void run() {
-                try {
-                    try {
-                        if(!startLatch.await(1, TimeUnit.SECONDS))
-                            throw new RuntimeException("Took too long to start!");
-                    } catch(InterruptedException interrupted) {
-                        throw new RuntimeException("Never started!", interrupted);
-                    }
-                    client.execute(getServerHttp(), httpget, context);
-                } catch(Throwable t) {
-                    throwableRef.set(t);
-                } finally {
-                    getLatch.countDown();
-                }
-            }
-        }).start();
-
-        httpget.abort();
-        startLatch.countDown();
-
-        Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
-        Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
-                throwableRef.get() instanceof IOException);
-    }
-
-    /**
-     * Tests that an abort called after a redirect has found a new host
-     * still aborts in the correct place (while trying to get the new
-     * host's route, not while doing the subsequent request).
-     */
-    @Test
-    public void testAbortAfterRedirectedRoute() throws Exception {
-        final int port = this.localServer.getServiceAddress().getPort();
-        this.localServer.register("*", new BasicRedirectService(port));
-
-        SchemeRegistry registry = new SchemeRegistry();
-        registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
-
-        CountDownLatch connLatch = new CountDownLatch(1);
-        CountDownLatch awaitLatch = new CountDownLatch(1);
-        ConnMan4 conMan = new ConnMan4(registry, connLatch, awaitLatch);
-        final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
-        final CountDownLatch getLatch = new CountDownLatch(1);
-        final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
-        final HttpContext context = new BasicHttpContext();
-        final HttpGet httpget = new HttpGet("a");
-
-        new Thread(new Runnable() {
-            public void run() {
-                try {
-                    HttpHost host = new HttpHost("127.0.0.1", port);
-                    client.execute(host, httpget, context);
-                } catch(Throwable t) {
-                    throwableRef.set(t);
-                } finally {
-                    getLatch.countDown();
-                }
-            }
-        }).start();
-
-        Assert.assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS));
-
-        httpget.abort();
-
-        Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
-        Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
-                throwableRef.get() instanceof IOException);
-        Assert.assertTrue("cause should be InterruptedException, was: " + throwableRef.get().getCause(),
-                throwableRef.get().getCause() instanceof InterruptedException);
-    }
-
-
-    /**
-     * Tests that if a socket fails to connect, the allocated connection is
-     * properly released back to the connection manager.
-     */
-    @Test
-    public void testSocketConnectFailureReleasesConnection() throws Exception {
-        final ConnMan2 conMan = new ConnMan2();
-        final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
-        final HttpContext context = new BasicHttpContext();
-        final HttpGet httpget = new HttpGet("http://www.example.com/a");
-
-        try {
-            client.execute(httpget, context);
-            Assert.fail("expected IOException");
-        } catch(IOException expected) {}
-
-        Assert.assertNotNull(conMan.allocatedConnection);
-        Assert.assertSame(conMan.allocatedConnection, conMan.releasedConnection);
-    }
-
-    @Test
-    public void testRequestFailureReleasesConnection() throws Exception {
-        this.localServer.register("*", new ThrowingService());
-
-        SchemeRegistry registry = new SchemeRegistry();
-        registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
-
-        ConnMan3 conMan = new ConnMan3(registry);
-        DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
-        HttpGet httpget = new HttpGet("/a");
-
-        try {
-            client.execute(getServerHttp(), httpget);
-            Assert.fail("expected IOException");
-        } catch (IOException expected) {}
+    private static class SimpleService implements HttpRequestHandler {
 
-        Assert.assertNotNull(conMan.allocatedConnection);
-        Assert.assertSame(conMan.allocatedConnection, conMan.releasedConnection);
-    }
+        public SimpleService() {
+            super();
+        }
 
-    private static class ThrowingService implements HttpRequestHandler {
         public void handle(
                 final HttpRequest request,
                 final HttpResponse response,
                 final HttpContext context) throws HttpException, IOException {
-            throw new IOException();
-        }
-    }
-
-    private static class BasicService implements HttpRequestHandler {
-        public void handle(final HttpRequest request,
-                final HttpResponse response,
-                final HttpContext context) throws HttpException, IOException {
-            response.setStatusCode(200);
-            response.setEntity(new StringEntity("Hello World"));
-        }
-    }
-
-   private static class BasicRedirectService implements HttpRequestHandler {
-        private int statuscode = HttpStatus.SC_SEE_OTHER;
-        private int port;
-
-        public BasicRedirectService(int port) {
-            this.port = port;
-        }
-
-        public void handle(final HttpRequest request,
-                final HttpResponse response, final HttpContext context)
-                throws HttpException, IOException {
-            ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
-            response.setStatusLine(ver, this.statuscode);
-            response.addHeader(new BasicHeader("Location", "http://localhost:"
-                    + this.port + "/newlocation/"));
-            response.addHeader(new BasicHeader("Connection", "close"));
-        }
-    }
-
-    private static class ConnMan4 extends ThreadSafeClientConnManager {
-        private final CountDownLatch connLatch;
-        private final CountDownLatch awaitLatch;
-
-        public ConnMan4(SchemeRegistry schreg,
-                CountDownLatch connLatch, CountDownLatch awaitLatch) {
-            super(schreg);
-            this.connLatch = connLatch;
-            this.awaitLatch = awaitLatch;
-        }
-
-        @Override
-        public ClientConnectionRequest requestConnection(HttpRoute route, Object state) {
-            // If this is the redirect route, stub the return value
-            // so-as to pretend the host is waiting on a slot...
-            if(route.getTargetHost().getHostName().equals("localhost")) {
-                final Thread currentThread = Thread.currentThread();
-
-                return new ClientConnectionRequest() {
-
-                    public void abortRequest() {
-                        currentThread.interrupt();
-                    }
-
-                    public ManagedClientConnection getConnection(
-                            long timeout, TimeUnit tunit)
-                            throws InterruptedException,
-                            ConnectionPoolTimeoutException {
-                        connLatch.countDown(); // notify waiter that we're getting a connection
-
-                        // zero usually means sleep forever, but CountDownLatch doesn't interpret it that way.
-                        if(timeout == 0)
-                            timeout = Integer.MAX_VALUE;
-
-                        if(!awaitLatch.await(timeout, tunit))
-                            throw new ConnectionPoolTimeoutException();
-
-                        return new ClientConnAdapterMockup(ConnMan4.this);
-                    }
-                };
-            } else {
-                return super.requestConnection(route, state);
-            }
+            response.setStatusCode(HttpStatus.SC_OK);
+            StringEntity entity = new StringEntity("Whatever");
+            response.setEntity(entity);
         }
     }
 
+    @Test
+    public void testDefaultHostAtClientLevel() throws Exception {
+        int port = this.localServer.getServiceAddress().getPort();
+        this.localServer.register("*", new SimpleService());
 
-    private static class ConnMan3 extends SingleClientConnManager {
-        private ManagedClientConnection allocatedConnection;
-        private ManagedClientConnection releasedConnection;
-
-        public ConnMan3(SchemeRegistry schreg) {
-            super(schreg);
-        }
-
-        @Override
-        public ManagedClientConnection getConnection(HttpRoute route, Object state) {
-            allocatedConnection = super.getConnection(route, state);
-            return allocatedConnection;
-        }
+        HttpHost target = new HttpHost("localhost", port);
 
-        @Override
-        public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
-            releasedConnection = conn;
-            super.releaseConnection(conn, validDuration, timeUnit);
-        }
+        this.httpclient.getParams().setParameter(ClientPNames.DEFAULT_HOST, target);
 
+        String s = "/path";
+        HttpGet httpget = new HttpGet(s);
 
+        HttpResponse response = this.httpclient.execute(httpget);
+        EntityUtils.consume(response.getEntity());
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
     }
 
-    static class ConnMan2 implements ClientConnectionManager {
-
-        private ManagedClientConnection allocatedConnection;
-        private ManagedClientConnection releasedConnection;
-
-        public ConnMan2() {
-        }
-
-        public void closeIdleConnections(long idletime, TimeUnit tunit) {
-            throw new UnsupportedOperationException("just a mockup");
-        }
-
-        public void closeExpiredConnections() {
-            throw new UnsupportedOperationException("just a mockup");
-        }
-
-        public ManagedClientConnection getConnection(HttpRoute route) {
-            throw new UnsupportedOperationException("just a mockup");
-        }
-
-        public ManagedClientConnection getConnection(HttpRoute route,
-                long timeout, TimeUnit tunit) {
-            throw new UnsupportedOperationException("just a mockup");
-        }
+    @Test
+    public void testDefaultHostHeader() throws Exception {
+        int port = this.localServer.getServiceAddress().getPort();
+        String hostname = getServerHttp().getHostName();
+        this.localServer.register("*", new SimpleService());
 
-        public ClientConnectionRequest requestConnection(
-                final HttpRoute route,
-                final Object state) {
-
-            return new ClientConnectionRequest() {
-
-                public void abortRequest() {
-                    throw new UnsupportedOperationException("just a mockup");
-                }
-
-                public ManagedClientConnection getConnection(
-                        long timeout, TimeUnit unit)
-                        throws InterruptedException,
-                        ConnectionPoolTimeoutException {
-                    allocatedConnection = new ClientConnAdapterMockup(ConnMan2.this) {
-                        @Override
-                        public void open(HttpRoute route, HttpContext context,
-                                HttpParams params) throws IOException {
-                            throw new ConnectException();
-                        }
-                    };
-                    return allocatedConnection;
-                }
-            };
-        }
+        HttpContext context = new BasicHttpContext();
 
-        public HttpParams getParams() {
-            throw new UnsupportedOperationException("just a mockup");
-        }
+        String s = "http://localhost:" + port;
+        HttpGet httpget = new HttpGet(s);
 
-        public SchemeRegistry getSchemeRegistry() {
-            SchemeRegistry registry = new SchemeRegistry();
-            registry.register(new Scheme("http", 80, new SocketFactoryMockup(null)));
-            return registry;
-        }
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
+        EntityUtils.consume(response.getEntity());
 
-        public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
-            this.releasedConnection = conn;
-        }
+        HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
+                ExecutionContext.HTTP_REQUEST);
 
-        public void shutdown() {
-            throw new UnsupportedOperationException("just a mockup");
-        }
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        // Check that Host header is generated as expected
+        Header[] headers = reqWrapper.getHeaders("host");
+        Assert.assertNotNull(headers);
+        Assert.assertEquals(1, headers.length);
+        Assert.assertEquals(hostname + ":" + port, headers[0].getValue());
     }
 
-    static class ConMan implements ClientConnectionManager {
-        private final CountDownLatch connLatch;
-        private final CountDownLatch awaitLatch;
-
-        public ConMan(CountDownLatch connLatch, CountDownLatch awaitLatch) {
-            this.connLatch = connLatch;
-            this.awaitLatch = awaitLatch;
-        }
-
-        public void closeIdleConnections(long idletime, TimeUnit tunit) {
-            throw new UnsupportedOperationException("just a mockup");
-        }
-
-        public void closeExpiredConnections() {
-            throw new UnsupportedOperationException("just a mockup");
-        }
-
-        public ManagedClientConnection getConnection(HttpRoute route) {
-            throw new UnsupportedOperationException("just a mockup");
-        }
-
-        public ManagedClientConnection getConnection(HttpRoute route,
-                long timeout, TimeUnit tunit) {
-            throw new UnsupportedOperationException("just a mockup");
-        }
-
-        public ClientConnectionRequest requestConnection(
-                final HttpRoute route,
-                final Object state) {
-
-            final Thread currentThread = Thread.currentThread();
-
-            return new ClientConnectionRequest() {
-
-                public void abortRequest() {
-                    currentThread.interrupt();
-                }
-
-                public ManagedClientConnection getConnection(
-                        long timeout, TimeUnit tunit)
-                        throws InterruptedException,
-                        ConnectionPoolTimeoutException {
-                    connLatch.countDown(); // notify waiter that we're getting a connection
-
-                    // zero usually means sleep forever, but CountDownLatch doesn't interpret it that way.
-                    if(timeout == 0)
-                        timeout = Integer.MAX_VALUE;
-
-                    if(!awaitLatch.await(timeout, tunit))
-                        throw new ConnectionPoolTimeoutException();
+    @Test
+    // HTTPCLIENT-1092
+    public void testVirtualHostHeader() throws Exception {
+        int port = this.localServer.getServiceAddress().getPort();
+        this.localServer.register("*", new SimpleService());
 
-                    return new ClientConnAdapterMockup(ConMan.this);
-                }
-            };
-        }
+        HttpContext context = new BasicHttpContext();
 
-        public HttpParams getParams() {
-            throw new UnsupportedOperationException("just a mockup");
-        }
+        String s = "http://localhost:" + port;
+        HttpGet httpget = new HttpGet(s);
 
-        public SchemeRegistry getSchemeRegistry() {
-            SchemeRegistry registry = new SchemeRegistry();
-            registry.register(new Scheme("http", 80, new SocketFactoryMockup(null)));
-            return registry;
-        }
+        String virtHost = "virtual";
+        httpget.getParams().setParameter(ClientPNames.VIRTUAL_HOST, new HttpHost(virtHost, port));
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
+        EntityUtils.consume(response.getEntity());
 
-        public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
-            throw new UnsupportedOperationException("just a mockup");
-        }
+        HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
+                ExecutionContext.HTTP_REQUEST);
 
-        public void shutdown() {
-            throw new UnsupportedOperationException("just a mockup");
-        }
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        // Check that Host header is generated as expected
+        Header[] headers = reqWrapper.getHeaders("host");
+        Assert.assertNotNull(headers);
+        Assert.assertEquals(1, headers.length);
+        Assert.assertEquals(virtHost+":"+port,headers[0].getValue());
     }
 
-    private static class CustomGet extends HttpGet {
-        private final CountDownLatch releaseTriggerLatch;
-
-        public CustomGet(String uri, CountDownLatch releaseTriggerLatch) {
-            super(uri);
-            this.releaseTriggerLatch = releaseTriggerLatch;
-        }
-
-        @Override
-        public void setReleaseTrigger(ConnectionReleaseTrigger releaseTrigger) throws IOException {
-            try {
-                if(!releaseTriggerLatch.await(1, TimeUnit.SECONDS))
-                    throw new RuntimeException("Waited too long...");
-            } catch(InterruptedException ie) {
-                throw new RuntimeException(ie);
-            }
+    @Test
+    // Test that virtual port is propagated if provided
+    // This is not expected to be used much, if ever
+    // HTTPCLIENT-1092
+    public void testVirtualHostPortHeader() throws Exception {
+        int port = this.localServer.getServiceAddress().getPort();
+        this.localServer.register("*", new SimpleService());
 
-            super.setReleaseTrigger(releaseTrigger);
-        }
+        HttpContext context = new BasicHttpContext();
 
-    }
+        String s = "http://localhost:" + port;
+        HttpGet httpget = new HttpGet(s);
 
-    private static class SimpleService implements HttpRequestHandler {
+        String virtHost = "virtual";
+        int virtPort = 9876;
+        httpget.getParams().setParameter(ClientPNames.VIRTUAL_HOST, new HttpHost(virtHost, virtPort));
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
+        EntityUtils.consume(response.getEntity());
 
-        public SimpleService() {
-            super();
-        }
+        HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
+                ExecutionContext.HTTP_REQUEST);
 
-        public void handle(
-                final HttpRequest request,
-                final HttpResponse response,
-                final HttpContext context) throws HttpException, IOException {
-            response.setStatusCode(HttpStatus.SC_OK);
-            StringEntity entity = new StringEntity("Whatever");
-            response.setEntity(entity);
-        }
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        // Check that Host header is generated as expected
+        Header[] headers = reqWrapper.getHeaders("host");
+        Assert.assertNotNull(headers);
+        Assert.assertEquals(1, headers.length);
+        Assert.assertEquals(virtHost+":"+virtPort,headers[0].getValue());
     }
 
     @Test
-    public void testDefaultHostAtClientLevel() throws Exception {
+    public void testClientLevelVirtualHostHeader() throws Exception {
         int port = this.localServer.getServiceAddress().getPort();
         this.localServer.register("*", new SimpleService());
 
-        HttpHost target = new HttpHost("localhost", port);
-
-        DefaultHttpClient client = new DefaultHttpClient();
-        client.getParams().setParameter(ClientPNames.DEFAULT_HOST, target);
+        HttpContext context = new BasicHttpContext();
 
-        String s = "/path";
+        String s = "http://localhost:" + port;
         HttpGet httpget = new HttpGet(s);
 
-        HttpResponse response = client.execute(httpget);
+        String virtHost = "virtual";
+        this.httpclient.getParams().setParameter(ClientPNames.VIRTUAL_HOST, new HttpHost(virtHost, port));
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
+
+        HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
+                ExecutionContext.HTTP_REQUEST);
+
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        // Check that Host header is generated as expected
+        Header[] headers = reqWrapper.getHeaders("host");
+        Assert.assertNotNull(headers);
+        Assert.assertEquals(1, headers.length);
+        Assert.assertEquals(virtHost+":"+port,headers[0].getValue());
     }
 
     @Test
@@ -619,14 +218,13 @@ public class TestDefaultClientRequestDirector extends BasicServerTestBase {
         HttpHost target1 = new HttpHost("whatever", 80);
         HttpHost target2 = new HttpHost("localhost", port);
 
-        DefaultHttpClient client = new DefaultHttpClient();
-        client.getParams().setParameter(ClientPNames.DEFAULT_HOST, target1);
+        this.httpclient.getParams().setParameter(ClientPNames.DEFAULT_HOST, target1);
 
         String s = "/path";
         HttpGet httpget = new HttpGet(s);
         httpget.getParams().setParameter(ClientPNames.DEFAULT_HOST, target2);
 
-        HttpResponse response = client.execute(httpget);
+        HttpResponse response = this.httpclient.execute(httpget);
         EntityUtils.consume(response.getEntity());
         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
     }
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultHttpRequestRetryHandler.java b/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultHttpRequestRetryHandler.java
new file mode 100644
index 0000000..9ad045e
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultHttpRequestRetryHandler.java
@@ -0,0 +1,113 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import org.apache.http.conn.ConnectTimeoutException;
+import java.io.IOException;
+import java.net.UnknownHostException;
+
+import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.ExecutionContext;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mock;
+import org.junit.Assert;
+import org.junit.Test;
+
+
+ at SuppressWarnings("boxing") // test class
+public class TestDefaultHttpRequestRetryHandler {
+
+    @Test
+    public void noRetryOnConnectTimeout() throws Exception {
+        HttpContext context = mock(HttpContext.class);
+        HttpUriRequest request = mock(HttpUriRequest.class);
+
+        DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler();
+        Assert.assertEquals(3, retryHandler.getRetryCount());
+
+        when(request.isAborted()).thenReturn(Boolean.FALSE);
+        when(context.getAttribute(ExecutionContext.HTTP_REQUEST)).thenReturn(request);
+
+        Assert.assertFalse(retryHandler.retryRequest(new ConnectTimeoutException(), 1, context));
+    }
+
+    @Test
+    public void noRetryOnUnknownHost() throws Exception {
+        HttpContext context = mock(HttpContext.class);
+        HttpUriRequest request = mock(HttpUriRequest.class);
+
+        DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler();
+
+        when(request.isAborted()).thenReturn(Boolean.FALSE);
+        when(context.getAttribute(ExecutionContext.HTTP_REQUEST)).thenReturn(request);
+
+        Assert.assertFalse(retryHandler.retryRequest(new UnknownHostException(), 1, context));
+    }
+
+    @Test
+    public void noRetryOnAbortedRequests() throws Exception{
+        HttpContext context = mock(HttpContext.class);
+        HttpUriRequest request = mock(HttpUriRequest.class);
+
+        DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler();
+
+        when(request.isAborted()).thenReturn(Boolean.TRUE);
+        when(context.getAttribute(ExecutionContext.HTTP_REQUEST)).thenReturn(request);
+
+        Assert.assertFalse(retryHandler.retryRequest(new IOException(),3,context));
+    }
+
+    @Test
+    public void retryOnNonAbortedRequests() throws Exception{
+
+        HttpContext context = mock(HttpContext.class);
+        HttpUriRequest request = mock(HttpUriRequest.class);
+
+        DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler();
+
+        when(request.isAborted()).thenReturn(Boolean.FALSE);
+        when(context.getAttribute(ExecutionContext.HTTP_REQUEST)).thenReturn(request);
+
+        Assert.assertTrue(retryHandler.retryRequest(new IOException(),3,context));
+    }
+
+    @Test
+    public void noRetryOnConnectionTimeout() throws Exception{
+
+        HttpContext context = mock(HttpContext.class);
+        HttpUriRequest request = mock(HttpUriRequest.class);
+
+        DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler();
+
+        when(request.isAborted()).thenReturn(false);
+        when(context.getAttribute(ExecutionContext.HTTP_REQUEST)).thenReturn(request);
+
+        Assert.assertFalse(retryHandler.retryRequest(new ConnectTimeoutException(),3,context));
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultRedirectStrategy.java b/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultRedirectStrategy.java
new file mode 100644
index 0000000..63da9be
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestDefaultRedirectStrategy.java
@@ -0,0 +1,348 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+package org.apache.http.impl.client;
+
+import java.net.URI;
+import java.util.List;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolException;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestDefaultRedirectStrategy {
+    
+    @Test
+    public void testIsRedirectable() {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        Assert.assertTrue(redirectStrategy.isRedirectable(HttpGet.METHOD_NAME));
+        Assert.assertTrue(redirectStrategy.isRedirectable(HttpHead.METHOD_NAME));
+        Assert.assertFalse(redirectStrategy.isRedirectable(HttpPut.METHOD_NAME));
+        Assert.assertFalse(redirectStrategy.isRedirectable(HttpPost.METHOD_NAME));
+        Assert.assertFalse(redirectStrategy.isRedirectable(HttpDelete.METHOD_NAME));
+    }
+
+    @Test
+    public void testIsRedirectedMovedTemporary() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "http://localhost/stuff");
+        Assert.assertTrue(redirectStrategy.isRedirected(httpget, response, context));
+        HttpPost httppost = new HttpPost("http://localhost/");
+        Assert.assertFalse(redirectStrategy.isRedirected(httppost, response, context));
+    }
+
+    @Test
+    public void testIsRedirectedMovedTemporaryNoLocation() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        Assert.assertFalse(redirectStrategy.isRedirected(httpget, response, context));
+    }
+
+    @Test
+    public void testIsRedirectedMovedPermanently() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_PERMANENTLY, "Redirect");
+        Assert.assertTrue(redirectStrategy.isRedirected(httpget, response, context));
+        HttpPost httppost = new HttpPost("http://localhost/");
+        Assert.assertFalse(redirectStrategy.isRedirected(httppost, response, context));
+    }
+
+    @Test
+    public void testIsRedirectedTemporaryRedirect() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_TEMPORARY_REDIRECT, "Redirect");
+        Assert.assertTrue(redirectStrategy.isRedirected(httpget, response, context));
+        HttpPost httppost = new HttpPost("http://localhost/");
+        Assert.assertFalse(redirectStrategy.isRedirected(httppost, response, context));
+    }
+
+    @Test
+    public void testIsRedirectedSeeOther() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_SEE_OTHER, "Redirect");
+        Assert.assertTrue(redirectStrategy.isRedirected(httpget, response, context));
+        HttpPost httppost = new HttpPost("http://localhost/");
+        Assert.assertTrue(redirectStrategy.isRedirected(httppost, response, context));
+    }
+
+    @Test
+    public void testIsRedirectedUnknownStatus() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 333, "Redirect");
+        Assert.assertFalse(redirectStrategy.isRedirected(httpget, response, context));
+        HttpPost httppost = new HttpPost("http://localhost/");
+        Assert.assertFalse(redirectStrategy.isRedirected(httppost, response, context));
+    }
+
+    @Test
+    public void testIsRedirectedInvalidInput() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_SEE_OTHER, "Redirect");
+        try {
+            redirectStrategy.isRedirected(null, response, context);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            redirectStrategy.isRedirected(httpget, null, context);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testGetLocationUri() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "http://localhost/stuff");
+        URI uri = redirectStrategy.getLocationURI(httpget, response, context);
+        Assert.assertEquals(URI.create("http://localhost/stuff"), uri);
+    }
+
+    @Test(expected=ProtocolException.class)
+    public void testGetLocationUriMissingHeader() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        redirectStrategy.getLocationURI(httpget, response, context);
+    }
+
+    @Test(expected=ProtocolException.class)
+    public void testGetLocationUriInvalidLocation() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "http://localhost/not valid");
+        redirectStrategy.getLocationURI(httpget, response, context);
+    }
+
+    @Test
+    public void testGetLocationUriRelative() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, new HttpHost("localhost"));
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "/stuff");
+        URI uri = redirectStrategy.getLocationURI(httpget, response, context);
+        Assert.assertEquals(URI.create("http://localhost/stuff"), uri);
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testGetLocationUriRelativeMissingTargetHost() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "/stuff");
+        URI uri = redirectStrategy.getLocationURI(httpget, response, context);
+        Assert.assertEquals(URI.create("http://localhost/stuff"), uri);
+    }
+
+    @Test
+    public void testGetLocationUriRelativeWithFragment() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, new HttpHost("localhost"));
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "/stuff#fragment");
+        URI uri = redirectStrategy.getLocationURI(httpget, response, context);
+        Assert.assertEquals(URI.create("http://localhost/stuff"), uri);
+    }
+
+    @Test
+    public void testGetLocationUriAbsoluteWithFragment() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, new HttpHost("localhost"));
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "http://localhost/stuff#fragment");
+        URI uri = redirectStrategy.getLocationURI(httpget, response, context);
+        Assert.assertEquals(URI.create("http://localhost/stuff"), uri);
+    }
+
+    @Test
+    public void testGetLocationUriNormalized() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, new HttpHost("localhost"));
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "http://localhost/././stuff/../morestuff");
+        URI uri = redirectStrategy.getLocationURI(httpget, response, context);
+        Assert.assertEquals(URI.create("http://localhost/morestuff"), uri);
+    }
+
+    @Test(expected=ProtocolException.class)
+    public void testGetLocationUriRelativeLocationNotAllowed() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, new HttpHost("localhost"));
+        HttpGet httpget = new HttpGet("http://localhost/");
+        httpget.getParams().setParameter(ClientPNames.REJECT_RELATIVE_REDIRECT, Boolean.TRUE);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "/stuff");
+        redirectStrategy.getLocationURI(httpget, response, context);
+    }
+
+    @Test
+    public void testGetLocationUriAllowCircularRedirects() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, new HttpHost("localhost"));
+        HttpGet httpget = new HttpGet("http://localhost/");
+        httpget.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, Boolean.TRUE);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "http://localhost/stuff");
+        URI uri = URI.create("http://localhost/stuff");
+        Assert.assertEquals(uri, redirectStrategy.getLocationURI(httpget, response, context));
+        Assert.assertEquals(uri, redirectStrategy.getLocationURI(httpget, response, context));
+        Assert.assertEquals(uri, redirectStrategy.getLocationURI(httpget, response, context));
+        
+        RedirectLocations redirectLocations = (RedirectLocations) context.getAttribute(
+                DefaultRedirectStrategy.REDIRECT_LOCATIONS);
+        Assert.assertNotNull(redirectLocations);
+        Assert.assertTrue(redirectLocations.contains(uri));
+        List<URI> uris = redirectLocations.getAll();
+        Assert.assertNotNull(uris);
+        Assert.assertEquals(3, uris.size());
+        Assert.assertEquals(uri, uris.get(0));
+        Assert.assertEquals(uri, uris.get(1));
+        Assert.assertEquals(uri, uris.get(2));
+    }
+
+    @Test(expected=ProtocolException.class)
+    public void testGetLocationUriDisallowCircularRedirects() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, new HttpHost("localhost"));
+        HttpGet httpget = new HttpGet("http://localhost/");
+        httpget.getParams().setParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, Boolean.FALSE);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "http://localhost/stuff");
+        URI uri = URI.create("http://localhost/stuff");
+        Assert.assertEquals(uri, redirectStrategy.getLocationURI(httpget, response, context));
+        redirectStrategy.getLocationURI(httpget, response, context);
+    }
+
+    @Test
+    public void testGetLocationUriInvalidInput() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpContext context = new BasicHttpContext();
+        HttpGet httpget = new HttpGet("http://localhost/");
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_MOVED_TEMPORARILY, "Redirect");
+        response.addHeader("Location", "http://localhost/stuff");
+        try {
+            redirectStrategy.getLocationURI(null, response, context);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            redirectStrategy.getLocationURI(httpget, null, context);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException expected) {
+        }
+        try {
+            redirectStrategy.getLocationURI(httpget, response, null);
+            Assert.fail("IllegalArgumentException expected");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testGetRedirectRequest() throws Exception {
+        DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 
+                HttpStatus.SC_SEE_OTHER, "Redirect");
+        response.addHeader("Location", "http://localhost/stuff");
+        HttpContext context1 = new BasicHttpContext();
+        HttpUriRequest redirect1 = redirectStrategy.getRedirect(
+                new HttpGet("http://localhost/"), response, context1);
+        Assert.assertEquals("GET", redirect1.getMethod());
+        HttpContext context2 = new BasicHttpContext();
+        HttpUriRequest redirect2 = redirectStrategy.getRedirect(
+                new HttpPost("http://localhost/"), response, context2);
+        Assert.assertEquals("GET", redirect2.getMethod());
+        HttpContext context3 = new BasicHttpContext();
+        HttpUriRequest redirect3 = redirectStrategy.getRedirect(
+                new HttpHead("http://localhost/"), response, context3);
+        Assert.assertEquals("HEAD", redirect3.getMethod());
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestHttpAuthenticator.java b/httpclient/src/test/java/org/apache/http/impl/client/TestHttpAuthenticator.java
new file mode 100644
index 0000000..91c5899
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestHttpAuthenticator.java
@@ -0,0 +1,353 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import java.util.HashMap;
+import java.util.Queue;
+
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthOption;
+import org.apache.http.auth.AuthProtocolState;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeRegistry;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.MalformedChallengeException;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.AuthenticationStrategy;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.auth.BasicSchemeFactory;
+import org.apache.http.impl.auth.DigestScheme;
+import org.apache.http.impl.auth.DigestSchemeFactory;
+import org.apache.http.impl.auth.NTLMSchemeFactory;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestHttpAuthenticator {
+
+    private AuthenticationStrategy authStrategy;
+    private AuthState authState;
+    private AuthScheme authScheme;
+    private HttpContext context;
+    private HttpHost host;
+    private HttpHost proxy;
+    private Credentials credentials;
+    private BasicCredentialsProvider credentialsProvider;
+    private AuthSchemeRegistry authSchemeRegistry;
+    private AuthCache authCache;
+    private HttpAuthenticator httpAuthenticator;
+
+    @Before
+    public void setUp() throws Exception {
+        this.authStrategy = Mockito.mock(AuthenticationStrategy.class);
+        this.authState = new AuthState();
+        this.authScheme = new BasicScheme();
+        this.authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test"));
+        this.context = new BasicHttpContext();
+        this.host = new HttpHost("localhost", 80);
+        this.proxy = new HttpHost("localhost", 8888);
+        this.context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, this.host);
+        this.context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, this.proxy);
+        this.credentials = Mockito.mock(Credentials.class);
+        this.credentialsProvider = new BasicCredentialsProvider();
+        this.credentialsProvider.setCredentials(AuthScope.ANY, this.credentials);
+        this.context.setAttribute(ClientContext.CREDS_PROVIDER, this.credentialsProvider);
+        this.authSchemeRegistry = new AuthSchemeRegistry();
+        this.authSchemeRegistry.register("basic", new BasicSchemeFactory());
+        this.authSchemeRegistry.register("digest", new DigestSchemeFactory());
+        this.authSchemeRegistry.register("ntlm", new NTLMSchemeFactory());
+        this.context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry);
+        this.authCache = Mockito.mock(AuthCache.class);
+        this.context.setAttribute(ClientContext.AUTH_CACHE, this.authCache);
+        this.httpAuthenticator = new HttpAuthenticator();
+    }
+
+    @Test
+    public void testAuthenticationRequested() throws Exception {
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        Mockito.when(this.authStrategy.isAuthenticationRequested(
+                Mockito.any(HttpHost.class),
+                Mockito.any(HttpResponse.class),
+                Mockito.any(HttpContext.class))).thenReturn(Boolean.TRUE);
+
+        Assert.assertTrue(this.httpAuthenticator.isAuthenticationRequested(
+                this.host, response, this.authStrategy, this.authState, this.context));
+
+        Mockito.verify(this.authStrategy).isAuthenticationRequested(this.host, response, this.context);
+    }
+
+    @Test
+    public void testAuthenticationNotRequestedUnchallenged() throws Exception {
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+        Mockito.when(this.authStrategy.isAuthenticationRequested(
+                Mockito.any(HttpHost.class),
+                Mockito.any(HttpResponse.class),
+                Mockito.any(HttpContext.class))).thenReturn(Boolean.FALSE);
+
+        Assert.assertFalse(this.httpAuthenticator.isAuthenticationRequested(
+                this.host, response, this.authStrategy, this.authState, this.context));
+        Assert.assertEquals(AuthProtocolState.UNCHALLENGED, this.authState.getState());
+
+        Mockito.verify(this.authStrategy).isAuthenticationRequested(this.host, response, this.context);
+    }
+
+    @Test
+    public void testAuthenticationNotRequestedSuccess1() throws Exception {
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+        Mockito.when(this.authStrategy.isAuthenticationRequested(
+                Mockito.any(HttpHost.class),
+                Mockito.any(HttpResponse.class),
+                Mockito.any(HttpContext.class))).thenReturn(Boolean.FALSE);
+        this.authState.update(this.authScheme, this.credentials);
+        this.authState.setState(AuthProtocolState.CHALLENGED);
+
+        Assert.assertFalse(this.httpAuthenticator.isAuthenticationRequested(
+                this.host, response, this.authStrategy, this.authState, this.context));
+        Assert.assertEquals(AuthProtocolState.SUCCESS, this.authState.getState());
+
+        Mockito.verify(this.authStrategy).isAuthenticationRequested(this.host, response, this.context);
+        Mockito.verify(this.authStrategy).authSucceeded(this.host, this.authScheme, this.context);
+    }
+
+    @Test
+    public void testAuthenticationNotRequestedSuccess2() throws Exception {
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
+        Mockito.when(this.authStrategy.isAuthenticationRequested(
+                Mockito.any(HttpHost.class),
+                Mockito.any(HttpResponse.class),
+                Mockito.any(HttpContext.class))).thenReturn(Boolean.FALSE);
+        this.authState.update(this.authScheme, this.credentials);
+        this.authState.setState(AuthProtocolState.HANDSHAKE);
+
+        Assert.assertFalse(this.httpAuthenticator.isAuthenticationRequested(
+                this.host, response, this.authStrategy, this.authState, this.context));
+        Assert.assertEquals(AuthProtocolState.SUCCESS, this.authState.getState());
+
+        Mockito.verify(this.authStrategy).isAuthenticationRequested(this.host, response, this.context);
+        Mockito.verify(this.authStrategy).authSucceeded(this.host, this.authScheme, this.context);
+    }
+
+    @Test
+    public void testAuthentication() throws Exception {
+        HttpHost host = new HttpHost("somehost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "whatever realm=\"realm1\", stuff=\"1234\""));
+
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+
+        Assert.assertTrue(this.httpAuthenticator.authenticate(host,
+                response, authStrategy, this.authState, this.context));
+        Assert.assertEquals(AuthProtocolState.CHALLENGED, this.authState.getState());
+
+        Queue<AuthOption> options = this.authState.getAuthOptions();
+        Assert.assertNotNull(options);
+        AuthOption option1 = options.poll();
+        Assert.assertNotNull(option1);
+        Assert.assertEquals("digest", option1.getAuthScheme().getSchemeName());
+        AuthOption option2 = options.poll();
+        Assert.assertNotNull(option2);
+        Assert.assertEquals("basic", option2.getAuthScheme().getSchemeName());
+        Assert.assertNull(options.poll());
+    }
+
+    @Test
+    public void testAuthenticationNoChallenges() throws Exception {
+        HttpHost host = new HttpHost("somehost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+
+        Mockito.when(this.authStrategy.getChallenges(
+                Mockito.any(HttpHost.class),
+                Mockito.any(HttpResponse.class),
+                Mockito.any(HttpContext.class))).thenReturn(new HashMap<String, Header>());
+
+        Assert.assertFalse(this.httpAuthenticator.authenticate(host,
+                response, this.authStrategy, this.authState, this.context));
+    }
+
+    @Test
+    public void testAuthenticationNoSupportedChallenges() throws Exception {
+        HttpHost host = new HttpHost("somehost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "This realm=\"test\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "That realm=\"realm1\", nonce=\"1234\""));
+
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+
+        Assert.assertFalse(this.httpAuthenticator.authenticate(host,
+                response, authStrategy, this.authState, this.context));
+    }
+
+    @Test
+    public void testAuthenticationNoCredentials() throws Exception {
+        HttpHost host = new HttpHost("somehost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\""));
+
+        this.credentialsProvider.clear();
+
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+
+        Assert.assertFalse(this.httpAuthenticator.authenticate(host,
+                response, authStrategy, this.authState, this.context));
+    }
+
+    @Test
+    public void testAuthenticationFailed() throws Exception {
+        HttpHost host = new HttpHost("somehost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\""));
+
+        this.authState.setState(AuthProtocolState.FAILURE);
+
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+
+        Assert.assertFalse(this.httpAuthenticator.authenticate(host,
+                response, authStrategy, this.authState, this.context));
+
+        Assert.assertEquals(AuthProtocolState.FAILURE, this.authState.getState());
+    }
+
+    @Test
+    public void testAuthenticationNoAuthScheme() throws Exception {
+        HttpHost host = new HttpHost("somehost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\""));
+
+        this.authState.setState(AuthProtocolState.CHALLENGED);
+        this.authState.update(this.authScheme, this.credentials);
+
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+
+        Assert.assertFalse(this.httpAuthenticator.authenticate(host,
+                response, authStrategy, this.authState, this.context));
+
+        Assert.assertEquals(AuthProtocolState.FAILURE, this.authState.getState());
+
+        Mockito.verify(this.authCache).remove(host);
+    }
+
+    @Test
+    public void testAuthenticationFailure() throws Exception {
+        HttpHost host = new HttpHost("somehost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "whatever realm=\"realm1\", stuff=\"1234\""));
+
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+
+        this.authState.setState(AuthProtocolState.CHALLENGED);
+        this.authState.update(new BasicScheme(), this.credentials);
+
+        Assert.assertFalse(this.httpAuthenticator.authenticate(host,
+                response, authStrategy, this.authState, this.context));
+        Assert.assertEquals(AuthProtocolState.FAILURE, this.authState.getState());
+        Assert.assertNull(this.authState.getCredentials());
+    }
+
+    @Test
+    public void testAuthenticationHandshaking() throws Exception {
+        HttpHost host = new HttpHost("somehost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", stale=true, nonce=\"1234\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "whatever realm=\"realm1\", stuff=\"1234\""));
+
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+
+        this.authState.setState(AuthProtocolState.CHALLENGED);
+        this.authState.update(new DigestScheme(), this.credentials);
+
+        Assert.assertTrue(this.httpAuthenticator.authenticate(host,
+                response, authStrategy, this.authState, this.context));
+
+        Assert.assertEquals(AuthProtocolState.HANDSHAKE, this.authState.getState());
+    }
+
+    @Test
+    public void testAuthenticationNoMatchingChallenge() throws Exception {
+        HttpHost host = new HttpHost("somehost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\""));
+        response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "whatever realm=\"realm1\", stuff=\"1234\""));
+
+        TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy();
+
+        this.authState.setState(AuthProtocolState.CHALLENGED);
+        this.authState.update(new BasicScheme(), this.credentials);
+
+        Assert.assertTrue(this.httpAuthenticator.authenticate(host,
+                response, authStrategy, this.authState, this.context));
+        Assert.assertEquals(AuthProtocolState.CHALLENGED, this.authState.getState());
+
+        Queue<AuthOption> options = this.authState.getAuthOptions();
+        Assert.assertNotNull(options);
+        AuthOption option1 = options.poll();
+        Assert.assertNotNull(option1);
+        Assert.assertEquals("digest", option1.getAuthScheme().getSchemeName());
+        Assert.assertNull(options.poll());
+    }
+
+    @Test
+    public void testAuthenticationException() throws Exception {
+        HttpHost host = new HttpHost("somehost", 80);
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+
+        this.authState.setState(AuthProtocolState.CHALLENGED);
+
+        Mockito.doThrow(new MalformedChallengeException()).when(this.authStrategy).getChallenges(
+                Mockito.any(HttpHost.class),
+                Mockito.any(HttpResponse.class),
+                Mockito.any(HttpContext.class));
+
+        Assert.assertFalse(this.httpAuthenticator.authenticate(host,
+                response, this.authStrategy, this.authState, this.context));
+
+        Assert.assertEquals(AuthProtocolState.UNCHALLENGED, this.authState.getState());
+        Assert.assertNull(this.authState.getAuthScheme());
+        Assert.assertNull(this.authState.getCredentials());
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestNullBackoffStrategy.java b/httpclient/src/test/java/org/apache/http/impl/client/TestNullBackoffStrategy.java
new file mode 100644
index 0000000..8b53022
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestNullBackoffStrategy.java
@@ -0,0 +1,58 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.client;
+
+import static org.junit.Assert.*;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.message.BasicHttpResponse;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class TestNullBackoffStrategy {
+
+    private NullBackoffStrategy impl;
+
+    @Before
+    public void setUp() {
+        impl = new NullBackoffStrategy();
+    }
+
+    @Test
+    public void doesNotBackoffForThrowables() {
+        assertFalse(impl.shouldBackoff(new Exception()));
+    }
+
+    @Test
+    public void doesNotBackoffForResponses() {
+        HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1,
+                HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+        assertFalse(impl.shouldBackoff(resp));
+    }
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestRequestRetryHandler.java b/httpclient/src/test/java/org/apache/http/impl/client/TestRequestRetryHandler.java
index 40aee5f..2225d50 100644
--- a/httpclient/src/test/java/org/apache/http/impl/client/TestRequestRetryHandler.java
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestRequestRetryHandler.java
@@ -38,8 +38,7 @@ import org.apache.http.conn.ConnectTimeoutException;
 import org.apache.http.conn.scheme.Scheme;
 import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.conn.scheme.SchemeSocketFactory;
-import org.apache.http.impl.conn.SingleClientConnManager;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.HttpContext;
@@ -49,26 +48,16 @@ import org.junit.Test;
 public class TestRequestRetryHandler {
 
     @Test(expected=UnknownHostException.class)
-    public void testUseRetryHandlerInConnectionTimeOutWithThreadSafeClientConnManager()
+    public void testUseRetryHandlerInConnectionTimeOut()
             throws Exception {
 
         SchemeRegistry schemeRegistry = new SchemeRegistry();
         schemeRegistry.register(new Scheme("http", 80, new OppsieSchemeSocketFactory()));
-        ClientConnectionManager connManager = new ThreadSafeClientConnManager(schemeRegistry);
+        ClientConnectionManager connManager = new PoolingClientConnectionManager(schemeRegistry);
 
         assertOnRetry(connManager);
     }
 
-    @Test(expected=UnknownHostException.class)
-    public void testUseRetryHandlerInConnectionTimeOutWithSingleClientConnManager()
-            throws Exception {
-
-        SchemeRegistry schemeRegistry = new SchemeRegistry();
-        schemeRegistry.register(new Scheme("http", 80, new OppsieSchemeSocketFactory()));
-        ClientConnectionManager connManager = new SingleClientConnManager(schemeRegistry);
-        assertOnRetry(connManager);
-    }
-
     protected void assertOnRetry(ClientConnectionManager connManager) throws Exception {
         DefaultHttpClient client = new DefaultHttpClient(connManager);
         TestHttpRequestRetryHandler testRetryHandler = new TestHttpRequestRetryHandler();
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestRequestWrapper.java b/httpclient/src/test/java/org/apache/http/impl/client/TestRequestWrapper.java
index 17bcaac..ab7205a 100644
--- a/httpclient/src/test/java/org/apache/http/impl/client/TestRequestWrapper.java
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestRequestWrapper.java
@@ -52,9 +52,10 @@ public class TestRequestWrapper extends BasicServerTestBase {
 
     @Before
     public void setUp() throws Exception {
-        localServer = new LocalTestServer(null, null);
-        localServer.registerDefaultHandlers();
-        localServer.start();
+        this.localServer = new LocalTestServer(null, null);
+        this.localServer.registerDefaultHandlers();
+        this.localServer.start();
+        this.httpclient = new DefaultHttpClient();
     }
 
     private static class SimpleService implements HttpRequestHandler {
@@ -78,13 +79,13 @@ public class TestRequestWrapper extends BasicServerTestBase {
         int port = this.localServer.getServiceAddress().getPort();
         this.localServer.register("*", new SimpleService());
 
-        DefaultHttpClient client = new DefaultHttpClient();
+
         HttpContext context = new BasicHttpContext();
 
         String s = "http://localhost:" + port + "/path";
         HttpGet httpget = new HttpGet(s);
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
@@ -101,13 +102,12 @@ public class TestRequestWrapper extends BasicServerTestBase {
         int port = this.localServer.getServiceAddress().getPort();
         this.localServer.register("*", new SimpleService());
 
-        DefaultHttpClient client = new DefaultHttpClient();
         HttpContext context = new BasicHttpContext();
 
         String s = "http://localhost:" + port;
         HttpGet httpget = new HttpGet(s);
 
-        HttpResponse response = client.execute(getServerHttp(), httpget, context);
+        HttpResponse response = this.httpclient.execute(getServerHttp(), httpget, context);
         EntityUtils.consume(response.getEntity());
 
         HttpRequest reqWrapper = (HttpRequest) context.getAttribute(
diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestStatefulConnManagement.java b/httpclient/src/test/java/org/apache/http/impl/client/TestStatefulConnManagement.java
index 24b8714..a847e73 100644
--- a/httpclient/src/test/java/org/apache/http/impl/client/TestStatefulConnManagement.java
+++ b/httpclient/src/test/java/org/apache/http/impl/client/TestStatefulConnManagement.java
@@ -37,7 +37,7 @@ import org.apache.http.client.UserTokenHandler;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.conn.ManagedClientConnection;
 import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
 import org.apache.http.localserver.ServerTestBase;
 import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.HttpConnectionParams;
@@ -85,7 +85,7 @@ public class TestStatefulConnManagement extends ServerTestBase {
         HttpParams params = new BasicHttpParams();
         HttpConnectionParams.setConnectionTimeout(params, 10);
 
-        ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(supportedSchemes);
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(supportedSchemes);
         mgr.setMaxTotal(workerCount);
         mgr.setDefaultMaxPerRoute(workerCount);
 
@@ -207,7 +207,7 @@ public class TestStatefulConnManagement extends ServerTestBase {
         this.localServer.register("*", new SimpleService());
 
         // We build a client with 2 max active // connections, and 2 max per route.
-        ThreadSafeClientConnManager connMngr = new ThreadSafeClientConnManager(supportedSchemes);
+        PoolingClientConnectionManager connMngr = new PoolingClientConnectionManager(supportedSchemes);
         connMngr.setMaxTotal(maxConn);
         connMngr.setDefaultMaxPerRoute(maxConn);
 
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/ClientConnAdapterMockup.java b/httpclient/src/test/java/org/apache/http/impl/conn/ClientConnAdapterMockup.java
deleted file mode 100644
index 5f77e3a..0000000
--- a/httpclient/src/test/java/org/apache/http/impl/conn/ClientConnAdapterMockup.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.impl.conn;
-
-import java.io.IOException;
-
-import org.apache.http.HttpHost;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.HttpContext;
-
-
-/**
- * Mockup connection adapter.
- */
-public class ClientConnAdapterMockup extends AbstractClientConnAdapter {
-
-    public ClientConnAdapterMockup(ClientConnectionManager mgr) {
-        super(mgr, null);
-    }
-
-    public void close() {
-    }
-
-    public HttpRoute getRoute() {
-        throw new UnsupportedOperationException("just a mockup");
-    }
-
-    public void layerProtocol(HttpContext context, HttpParams params) {
-        throw new UnsupportedOperationException("just a mockup");
-    }
-
-    public void open(HttpRoute route, HttpContext context, HttpParams params) throws IOException {
-        throw new UnsupportedOperationException("just a mockup");
-    }
-
-    public void shutdown() {
-    }
-
-    public void tunnelTarget(boolean secure, HttpParams params) {
-        throw new UnsupportedOperationException("just a mockup");
-    }
-
-    public void tunnelProxy(HttpHost next, boolean secure, HttpParams params) {
-        throw new UnsupportedOperationException("just a mockup");
-    }
-
-    public Object getState() {
-        throw new UnsupportedOperationException("just a mockup");
-    }
-
-    public void setState(Object state) {
-        throw new UnsupportedOperationException("just a mockup");
-    }
-}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/ConnPoolBench.java b/httpclient/src/test/java/org/apache/http/impl/conn/ConnPoolBench.java
new file mode 100644
index 0000000..60a1a46
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/ConnPoolBench.java
@@ -0,0 +1,173 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpHost;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.params.ConnPerRoute;
+import org.apache.http.conn.params.ConnPerRouteBean;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.conn.tsccm.BasicPoolEntry;
+import org.apache.http.impl.conn.tsccm.ConnPoolByRoute;
+import org.apache.http.impl.conn.tsccm.PoolEntryRequest;
+
+ at SuppressWarnings("deprecation")
+public class ConnPoolBench {
+
+    private final static HttpRoute ROUTE = new HttpRoute(new HttpHost("localhost"));
+
+    public static void main(String[] args) throws Exception {
+        int c = 200;
+        long reps = 100000;
+        oldPool(c, reps);
+        newPool(c, reps);
+    }
+
+    public static void newPool(int c, long reps) throws Exception {
+        Log log = LogFactory.getLog(ConnPoolBench.class);
+
+        HttpConnPool pool = new HttpConnPool(log, c, c * 10, -1, TimeUnit.MILLISECONDS);
+
+        WorkerThread1[] workers = new WorkerThread1[c];
+        for (int i = 0; i < workers.length; i++) {
+            workers[i] = new WorkerThread1(pool, reps);
+        }
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < workers.length; i++) {
+            workers[i].start();
+        }
+        for (int i = 0; i < workers.length; i++) {
+            workers[i].join();
+        }
+        long finish = System.currentTimeMillis();
+        float totalTimeSec = (float) (finish - start) / 1000;
+        System.out.print("Concurrency level:\t");
+        System.out.println(c);
+        System.out.print("Total operations:\t");
+        System.out.println(c * reps);
+        System.out.print("Time taken for tests:\t");
+        System.out.print(totalTimeSec);
+        System.out.println(" seconds");
+    }
+
+    static void oldPool(int c, long reps) throws Exception {
+        ClientConnectionOperator operator = new DefaultClientConnectionOperator(
+                SchemeRegistryFactory.createDefault());
+        ConnPerRoute connPerRoute = new ConnPerRouteBean(c);
+        ConnPoolByRoute pool = new ConnPoolByRoute(operator, connPerRoute, c * 10);
+
+        WorkerThread2[] workers = new WorkerThread2[c];
+        for (int i = 0; i < workers.length; i++) {
+            workers[i] = new WorkerThread2(pool, reps);
+        }
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < workers.length; i++) {
+            workers[i].start();
+        }
+        for (int i = 0; i < workers.length; i++) {
+            workers[i].join();
+        }
+        long finish = System.currentTimeMillis();
+        float totalTimeSec = (float) (finish - start) / 1000;
+        System.out.print("Concurrency level:\t");
+        System.out.println(c);
+        System.out.print("Total operations:\t");
+        System.out.println(c * reps);
+        System.out.print("Time taken for tests:\t");
+        System.out.print(totalTimeSec);
+        System.out.println(" seconds");
+    }
+
+    static class WorkerThread1 extends Thread {
+
+        private final HttpConnPool pool;
+        private final long reps;
+
+        WorkerThread1(final HttpConnPool pool, final long reps) {
+            super();
+            this.pool = pool;
+            this.reps = reps;
+        }
+
+        @Override
+        public void run() {
+            for (long c = 0; c < this.reps; c++) {
+                Future<HttpPoolEntry> future = this.pool.lease(ROUTE, null);
+                try {
+                    HttpPoolEntry entry = future.get(-1, TimeUnit.MILLISECONDS);
+                    this.pool.release(entry, true);
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                } catch (ExecutionException e) {
+                    e.printStackTrace();
+                } catch (TimeoutException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+    }
+
+    static class WorkerThread2 extends Thread {
+
+        private final ConnPoolByRoute pool;
+        private final long reps;
+
+        WorkerThread2(final ConnPoolByRoute pool, final long reps) {
+            super();
+            this.pool = pool;
+            this.reps = reps;
+        }
+
+        @Override
+        public void run() {
+            for (long c = 0; c < this.reps; c++) {
+                PoolEntryRequest request = this.pool.requestPoolEntry(ROUTE, null);
+                BasicPoolEntry entry;
+                try {
+                    entry = request.getPoolEntry(-1, TimeUnit.MILLISECONDS);
+                    this.pool.freeEntry(entry, true, -1, TimeUnit.MILLISECONDS);
+                } catch (ConnectionPoolTimeoutException e) {
+                    e.printStackTrace();
+                } catch (InterruptedException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+    }
+
+}
+
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/ExecReqThread.java b/httpclient/src/test/java/org/apache/http/impl/conn/ExecReqThread.java
index 3beafcd..0c3574d 100644
--- a/httpclient/src/test/java/org/apache/http/impl/conn/ExecReqThread.java
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/ExecReqThread.java
@@ -111,10 +111,9 @@ public class ExecReqThread extends GetConnThread {
 
             doConsumeResponse();
 
-        } catch (Throwable dart) {
-            dart.printStackTrace(System.out);
+        } catch (Exception ex) {
             if (exception != null)
-                exception = dart;
+                exception = ex;
 
         } finally {
             conn_manager.releaseConnection(connection, -1, null);
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/GetConnThread.java b/httpclient/src/test/java/org/apache/http/impl/conn/GetConnThread.java
index e9d3f33..b474f59 100644
--- a/httpclient/src/test/java/org/apache/http/impl/conn/GetConnThread.java
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/GetConnThread.java
@@ -47,7 +47,7 @@ public class GetConnThread extends Thread {
     protected final ClientConnectionRequest conn_request;
 
     protected volatile ManagedClientConnection connection;
-    protected volatile Throwable               exception;
+    protected volatile Exception               exception;
 
     /**
      * Creates a new thread for requesting a connection from the given manager.
@@ -81,8 +81,8 @@ public class GetConnThread extends Thread {
         try {
             connection = conn_request.getConnection
                 (conn_timeout, TimeUnit.MILLISECONDS);
-        } catch (Throwable dart) {
-            exception = dart;
+        } catch (Exception ex) {
+            exception = ex;
         }
         // terminate
     }
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestAbortHandling.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestAbortHandling.java
new file mode 100644
index 0000000..14c2424
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestAbortHandling.java
@@ -0,0 +1,510 @@
+/*
+ * ====================================================================
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.client.methods.AbortableHttpRequest;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.ConnectionReleaseTrigger;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.DefaultRequestDirector;
+import org.apache.http.impl.client.RequestWrapper;
+import org.apache.http.localserver.BasicServerTestBase;
+import org.apache.http.localserver.LocalTestServer;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.mockup.SocketFactoryMockup;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ *  Tests for Abort handling.
+ */
+public class TestAbortHandling extends BasicServerTestBase {
+
+    @Before
+    public void setUp() throws Exception {
+        this.localServer = new LocalTestServer(null, null);
+        this.localServer.registerDefaultHandlers();
+        this.localServer.start();
+        this.httpclient = new DefaultHttpClient();
+    }
+
+    @Test
+    public void testAbortRetry_HTTPCLIENT_1120() throws Exception {
+        int port = this.localServer.getServiceAddress().getPort();
+        final CountDownLatch wait = new CountDownLatch(1);
+
+        this.localServer.register("*", new HttpRequestHandler(){
+            public void handle(HttpRequest request, HttpResponse response,
+                    HttpContext context) throws HttpException, IOException {
+                try {
+                    wait.countDown(); // trigger abort
+                    Thread.sleep(2000); // allow time for abort to happen
+                    response.setStatusCode(HttpStatus.SC_OK);
+                    StringEntity entity = new StringEntity("Whatever");
+                    response.setEntity(entity);
+                } catch (Exception e) {
+                    response.setStatusCode(HttpStatus.SC_REQUEST_TIMEOUT);
+                }
+            }});
+
+        String s = "http://localhost:" + port + "/path";
+        final HttpGet httpget = new HttpGet(s);
+
+        Thread t = new Thread() {
+             @Override
+            public void run(){
+                 try {
+                    wait.await();
+                } catch (InterruptedException e) {
+                }
+                 httpget.abort();
+             }
+        };
+
+        t.start();
+
+        HttpContext context = new BasicHttpContext();
+        try {
+            this.httpclient.execute(getServerHttp(), httpget, context);
+        } catch (IllegalStateException e) {
+        } catch (IOException e) {
+        }
+
+        HttpRequest reqWrapper = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
+        Assert.assertNotNull("Request should exist",reqWrapper);
+        Assert.assertEquals(1,((RequestWrapper) reqWrapper).getExecCount());
+    }
+
+    /**
+     * Tests that if abort is called on an {@link AbortableHttpRequest} while
+     * {@link DefaultRequestDirector} is allocating a connection, that the
+     * connection is properly aborted.
+     */
+    @Test
+    public void testAbortInAllocate() throws Exception {
+        CountDownLatch connLatch = new CountDownLatch(1);
+        CountDownLatch awaitLatch = new CountDownLatch(1);
+        final ConMan conMan = new ConMan(connLatch, awaitLatch);
+        final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
+        final CountDownLatch getLatch = new CountDownLatch(1);
+        final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
+        final HttpContext context = new BasicHttpContext();
+        final HttpGet httpget = new HttpGet("http://www.example.com/a");
+
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    client.execute(httpget, context);
+                } catch(Throwable t) {
+                    throwableRef.set(t);
+                } finally {
+                    getLatch.countDown();
+                }
+            }
+        }).start();
+
+        Assert.assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS));
+
+        httpget.abort();
+
+        Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
+                throwableRef.get() instanceof IOException);
+        Assert.assertTrue("cause should be InterruptedException, was: " + throwableRef.get().getCause(),
+                throwableRef.get().getCause() instanceof InterruptedException);
+    }
+
+    /**
+     * Tests that an abort called after the connection has been retrieved
+     * but before a release trigger is set does still abort the request.
+     */
+    @Test
+    public void testAbortAfterAllocateBeforeRequest() throws Exception {
+        this.localServer.register("*", new BasicService());
+
+        CountDownLatch releaseLatch = new CountDownLatch(1);
+        SchemeRegistry registry = new SchemeRegistry();
+        registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
+
+        PoolingClientConnectionManager conMan = new PoolingClientConnectionManager(registry);
+        final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
+        final CountDownLatch getLatch = new CountDownLatch(1);
+        final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
+        final HttpContext context = new BasicHttpContext();
+        final HttpGet httpget = new CustomGet("a", releaseLatch);
+
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    client.execute(getServerHttp(), httpget, context);
+                } catch(Throwable t) {
+                    throwableRef.set(t);
+                } finally {
+                    getLatch.countDown();
+                }
+            }
+        }).start();
+
+        Thread.sleep(100); // Give it a little time to proceed to release...
+
+        httpget.abort();
+
+        releaseLatch.countDown();
+
+        Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
+                throwableRef.get() instanceof IOException);
+    }
+
+    /**
+     * Tests that an abort called completely before execute
+     * still aborts the request.
+     */
+    @Test
+    public void testAbortBeforeExecute() throws Exception {
+        this.localServer.register("*", new BasicService());
+
+        SchemeRegistry registry = new SchemeRegistry();
+        registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
+
+        PoolingClientConnectionManager conMan = new PoolingClientConnectionManager(registry);
+        final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
+        final CountDownLatch getLatch = new CountDownLatch(1);
+        final CountDownLatch startLatch = new CountDownLatch(1);
+        final DefaultHttpClient client = new DefaultHttpClient(conMan, new BasicHttpParams());
+        final HttpContext context = new BasicHttpContext();
+        final HttpGet httpget = new HttpGet("a");
+
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    try {
+                        if(!startLatch.await(1, TimeUnit.SECONDS))
+                            throw new RuntimeException("Took too long to start!");
+                    } catch(InterruptedException interrupted) {
+                        throw new RuntimeException("Never started!", interrupted);
+                    }
+                    client.execute(getServerHttp(), httpget, context);
+                } catch(Throwable t) {
+                    throwableRef.set(t);
+                } finally {
+                    getLatch.countDown();
+                }
+            }
+        }).start();
+
+        httpget.abort();
+        startLatch.countDown();
+
+        Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
+                throwableRef.get() instanceof IOException);
+    }
+
+    /**
+     * Tests that an abort called after a redirect has found a new host
+     * still aborts in the correct place (while trying to get the new
+     * host's route, not while doing the subsequent request).
+     */
+    @Test
+    public void testAbortAfterRedirectedRoute() throws Exception {
+        final int port = this.localServer.getServiceAddress().getPort();
+        this.localServer.register("*", new BasicRedirectService(port));
+
+        SchemeRegistry registry = new SchemeRegistry();
+        registry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
+
+        CountDownLatch connLatch = new CountDownLatch(1);
+        CountDownLatch awaitLatch = new CountDownLatch(1);
+        ConnMan4 conMan = new ConnMan4(registry, connLatch, awaitLatch);
+        final AtomicReference<Throwable> throwableRef = new AtomicReference<Throwable>();
+        final CountDownLatch getLatch = new CountDownLatch(1);
+        final DefaultHttpClient client  = new DefaultHttpClient(conMan, new BasicHttpParams());
+        final HttpContext context = new BasicHttpContext();
+        final HttpGet httpget = new HttpGet("a");
+
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    HttpHost host = new HttpHost("127.0.0.1", port);
+                    client.execute(host, httpget, context);
+                } catch(Throwable t) {
+                    throwableRef.set(t);
+                } finally {
+                    getLatch.countDown();
+                }
+            }
+        }).start();
+
+        Assert.assertTrue("should have tried to get a connection", connLatch.await(1, TimeUnit.SECONDS));
+
+        httpget.abort();
+
+        Assert.assertTrue("should have finished get request", getLatch.await(1, TimeUnit.SECONDS));
+        Assert.assertTrue("should be instanceof IOException, was: " + throwableRef.get(),
+                throwableRef.get() instanceof IOException);
+        Assert.assertTrue("cause should be InterruptedException, was: " + throwableRef.get().getCause(),
+                throwableRef.get().getCause() instanceof InterruptedException);
+    }
+
+
+    /**
+     * Tests that if a socket fails to connect, the allocated connection is
+     * properly released back to the connection manager.
+     */
+    @Test
+    public void testSocketConnectFailureReleasesConnection() throws Exception {
+        ManagedClientConnection conn = Mockito.mock(ManagedClientConnection.class);
+        Mockito.doThrow(new ConnectException()).when(conn).open(
+                Mockito.any(HttpRoute.class),
+                Mockito.any(HttpContext.class),
+                Mockito.any(HttpParams.class));
+        ClientConnectionRequest connrequest = Mockito.mock(ClientConnectionRequest.class);
+        Mockito.when(connrequest.getConnection(
+                Mockito.anyInt(), Mockito.any(TimeUnit.class))).thenReturn(conn);
+        ClientConnectionManager connmgr = Mockito.mock(ClientConnectionManager.class);
+
+        SchemeRegistry schemeRegistry = SchemeRegistryFactory.createDefault();
+
+        Mockito.when(connmgr.requestConnection(
+                Mockito.any(HttpRoute.class), Mockito.any())).thenReturn(connrequest);
+        Mockito.when(connmgr.getSchemeRegistry()).thenReturn(schemeRegistry);
+
+        final DefaultHttpClient client = new DefaultHttpClient(connmgr, new BasicHttpParams());
+        final HttpContext context = new BasicHttpContext();
+        final HttpGet httpget = new HttpGet("http://www.example.com/a");
+
+        try {
+            client.execute(httpget, context);
+            Assert.fail("expected IOException");
+        } catch(IOException expected) {}
+
+        Mockito.verify(conn).abortConnection();
+    }
+
+    private static class BasicService implements HttpRequestHandler {
+        public void handle(final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            response.setStatusCode(200);
+            response.setEntity(new StringEntity("Hello World"));
+        }
+    }
+
+   private static class BasicRedirectService implements HttpRequestHandler {
+        private int statuscode = HttpStatus.SC_SEE_OTHER;
+        private int port;
+
+        public BasicRedirectService(int port) {
+            this.port = port;
+        }
+
+        public void handle(final HttpRequest request,
+                final HttpResponse response, final HttpContext context)
+                throws HttpException, IOException {
+            ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+            response.setStatusLine(ver, this.statuscode);
+            response.addHeader(new BasicHeader("Location", "http://localhost:"
+                    + this.port + "/newlocation/"));
+            response.addHeader(new BasicHeader("Connection", "close"));
+        }
+    }
+
+    private static class ConnMan4 extends PoolingClientConnectionManager {
+        private final CountDownLatch connLatch;
+        private final CountDownLatch awaitLatch;
+
+        public ConnMan4(SchemeRegistry schreg,
+                CountDownLatch connLatch, CountDownLatch awaitLatch) {
+            super(schreg);
+            this.connLatch = connLatch;
+            this.awaitLatch = awaitLatch;
+        }
+
+        @Override
+        public ClientConnectionRequest requestConnection(HttpRoute route, Object state) {
+            // If this is the redirect route, stub the return value
+            // so-as to pretend the host is waiting on a slot...
+            if(route.getTargetHost().getHostName().equals("localhost")) {
+                final Thread currentThread = Thread.currentThread();
+
+                return new ClientConnectionRequest() {
+
+                    public void abortRequest() {
+                        currentThread.interrupt();
+                    }
+
+                    public ManagedClientConnection getConnection(
+                            long timeout, TimeUnit tunit)
+                            throws InterruptedException,
+                            ConnectionPoolTimeoutException {
+                        connLatch.countDown(); // notify waiter that we're getting a connection
+
+                        // zero usually means sleep forever, but CountDownLatch doesn't interpret it that way.
+                        if(timeout == 0)
+                            timeout = Integer.MAX_VALUE;
+
+                        if(!awaitLatch.await(timeout, tunit))
+                            throw new ConnectionPoolTimeoutException();
+
+                        return Mockito.mock(ManagedClientConnection.class);
+                    }
+                };
+            } else {
+                return super.requestConnection(route, state);
+            }
+        }
+    }
+
+
+    static class ConMan implements ClientConnectionManager {
+        private final CountDownLatch connLatch;
+        private final CountDownLatch awaitLatch;
+
+        public ConMan(CountDownLatch connLatch, CountDownLatch awaitLatch) {
+            this.connLatch = connLatch;
+            this.awaitLatch = awaitLatch;
+        }
+
+        public void closeIdleConnections(long idletime, TimeUnit tunit) {
+            throw new UnsupportedOperationException("just a mockup");
+        }
+
+        public void closeExpiredConnections() {
+            throw new UnsupportedOperationException("just a mockup");
+        }
+
+        public ManagedClientConnection getConnection(HttpRoute route) {
+            throw new UnsupportedOperationException("just a mockup");
+        }
+
+        public ManagedClientConnection getConnection(HttpRoute route,
+                long timeout, TimeUnit tunit) {
+            throw new UnsupportedOperationException("just a mockup");
+        }
+
+        public ClientConnectionRequest requestConnection(
+                final HttpRoute route,
+                final Object state) {
+
+            final Thread currentThread = Thread.currentThread();
+
+            return new ClientConnectionRequest() {
+
+                public void abortRequest() {
+                    currentThread.interrupt();
+                }
+
+                public ManagedClientConnection getConnection(
+                        long timeout, TimeUnit tunit)
+                        throws InterruptedException,
+                        ConnectionPoolTimeoutException {
+                    connLatch.countDown(); // notify waiter that we're getting a connection
+
+                    // zero usually means sleep forever, but CountDownLatch doesn't interpret it that way.
+                    if(timeout == 0)
+                        timeout = Integer.MAX_VALUE;
+
+                    if(!awaitLatch.await(timeout, tunit))
+                        throw new ConnectionPoolTimeoutException();
+
+                    return Mockito.mock(ManagedClientConnection.class);
+                }
+            };
+        }
+
+        public HttpParams getParams() {
+            throw new UnsupportedOperationException("just a mockup");
+        }
+
+        public SchemeRegistry getSchemeRegistry() {
+            SchemeRegistry registry = new SchemeRegistry();
+            registry.register(new Scheme("http", 80, new SocketFactoryMockup(null)));
+            return registry;
+        }
+
+        public void releaseConnection(ManagedClientConnection conn, long validDuration, TimeUnit timeUnit) {
+            throw new UnsupportedOperationException("just a mockup");
+        }
+
+        public void shutdown() {
+            throw new UnsupportedOperationException("just a mockup");
+        }
+    }
+
+    private static class CustomGet extends HttpGet {
+        private final CountDownLatch releaseTriggerLatch;
+
+        public CustomGet(String uri, CountDownLatch releaseTriggerLatch) {
+            super(uri);
+            this.releaseTriggerLatch = releaseTriggerLatch;
+        }
+
+        @Override
+        public void setReleaseTrigger(ConnectionReleaseTrigger releaseTrigger) throws IOException {
+            try {
+                if(!releaseTriggerLatch.await(1, TimeUnit.SECONDS))
+                    throw new RuntimeException("Waited too long...");
+            } catch(InterruptedException ie) {
+                throw new RuntimeException(ie);
+            }
+
+            super.setReleaseTrigger(releaseTrigger);
+        }
+
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicConnManager.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicConnManager.java
new file mode 100644
index 0000000..6bba101
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestBasicConnManager.java
@@ -0,0 +1,214 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.localserver.ServerTestBase;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.util.EntityUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestBasicConnManager extends ServerTestBase {
+
+    public BasicClientConnectionManager createConnManager(SchemeRegistry schreg) {
+        if (schreg == null)
+            schreg = supportedSchemes;
+        return new BasicClientConnectionManager(schreg);
+    }
+
+    /**
+     * Tests that SCM can still connect to the same host after
+     * a connection was aborted.
+     */
+    @Test
+    public void testOpenAfterAbort() throws Exception {
+        BasicClientConnectionManager mgr = createConnManager(null);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+
+        ManagedClientConnection conn = mgr.getConnection(route, null);
+        conn.abortConnection();
+
+        conn = mgr.getConnection(route, null);
+        Assert.assertFalse("connection should have been closed", conn.isOpen());
+        conn.open(route, httpContext, defaultParams);
+
+        mgr.releaseConnection(conn, -1, null);
+        mgr.shutdown();
+    }
+
+    /**
+     * Tests releasing with time limits.
+     */
+    @Test
+    public void testReleaseConnectionWithTimeLimits() throws Exception {
+
+        BasicClientConnectionManager mgr = createConnManager(null);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+        final int      rsplen = 8;
+        final String      uri = "/random/" + rsplen;
+
+        HttpRequest request =
+            new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
+
+        ManagedClientConnection conn = mgr.getConnection(route, null);
+        conn.open(route, httpContext, defaultParams);
+
+        // a new context is created for each testcase, no need to reset
+        HttpResponse response = Helper.execute(
+                request, conn, target,
+                httpExecutor, httpProcessor, defaultParams, httpContext);
+
+        Assert.assertEquals("wrong status in first response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        byte[] data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of first response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        // release connection without marking for re-use
+        // expect the next connection obtained to be closed
+        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
+        conn = mgr.getConnection(route, null);
+        Assert.assertFalse("connection should have been closed", conn.isOpen());
+
+        // repeat the communication, no need to prepare the request again
+        conn.open(route, httpContext, defaultParams);
+        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+        response = httpExecutor.execute(request, conn, httpContext);
+        httpExecutor.postProcess(response, httpProcessor, httpContext);
+
+        Assert.assertEquals("wrong status in second response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of second response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        // release connection after marking it for re-use
+        // expect the next connection obtained to be open
+        conn.markReusable();
+        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
+        conn =  mgr.getConnection(route, null);
+        Assert.assertTrue("connection should have been open", conn.isOpen());
+
+        // repeat the communication, no need to prepare the request again
+        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+        response = httpExecutor.execute(request, conn, httpContext);
+        httpExecutor.postProcess(response, httpProcessor, httpContext);
+
+        Assert.assertEquals("wrong status in third response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of third response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        conn.markReusable();
+        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
+        Thread.sleep(150);
+        conn =  mgr.getConnection(route, null);
+        Assert.assertTrue("connection should have been closed", !conn.isOpen());
+
+        // repeat the communication, no need to prepare the request again
+        conn.open(route, httpContext, defaultParams);
+        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+        response = httpExecutor.execute(request, conn, httpContext);
+        httpExecutor.postProcess(response, httpProcessor, httpContext);
+
+        Assert.assertEquals("wrong status in third response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of fourth response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testCloseExpiredConnections() throws Exception {
+
+        BasicClientConnectionManager mgr = createConnManager(null);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+
+        ManagedClientConnection conn =  mgr.getConnection(route, null);
+        conn.open(route, httpContext, defaultParams);
+        conn.markReusable();
+        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
+
+        mgr.closeExpiredConnections();
+
+        conn = mgr.getConnection(route, null);
+        Assert.assertTrue(conn.isOpen());
+        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
+
+        Thread.sleep(150);
+        mgr.closeExpiredConnections();
+        conn = mgr.getConnection(route, null);
+        Assert.assertFalse(conn.isOpen());
+
+        mgr.shutdown();
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testAlreadyLeased() throws Exception {
+
+        BasicClientConnectionManager mgr = createConnManager(null);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+
+        ManagedClientConnection conn =  mgr.getConnection(route, null);
+        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
+
+        mgr.getConnection(route, null);
+        mgr.getConnection(route, null);
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java
new file mode 100644
index 0000000..26c0763
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultClientConnectOperator.java
@@ -0,0 +1,85 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.http.impl.conn;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+import org.apache.http.conn.DnsResolver;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestDefaultClientConnectOperator {
+
+    @Test
+    public void testCustomDnsResolver() throws Exception {
+        DnsResolver dnsResolver = mock(DnsResolver.class);
+        InetAddress[] firstAddress = translateIp("192.168.1.1");
+        when(dnsResolver.resolve("somehost.example.com")).thenReturn(firstAddress);
+
+        InetAddress[] secondAddress = translateIp("192.168.12.16");
+        when(dnsResolver.resolve("otherhost.example.com")).thenReturn(secondAddress);
+
+        DefaultClientConnectionOperator operator = new DefaultClientConnectionOperator(
+                SchemeRegistryFactory.createDefault(), dnsResolver);
+
+        Assert.assertArrayEquals(firstAddress, operator.resolveHostname("somehost.example.com"));
+        Assert.assertArrayEquals(secondAddress, operator.resolveHostname("otherhost.example.com"));
+    }
+
+    @Test(expected=UnknownHostException.class)
+    public void testDnsResolverUnknownHost() throws Exception {
+        DnsResolver dnsResolver = mock(DnsResolver.class);
+        when(dnsResolver.resolve("unknown.example.com")).thenThrow(new UnknownHostException());
+
+        DefaultClientConnectionOperator operator = new DefaultClientConnectionOperator(
+                SchemeRegistryFactory.createDefault(), dnsResolver);
+        operator.resolveHostname("unknown.example.com");
+    }
+
+    @Test
+    public void testDefaultLocalHost() throws Exception {
+        DefaultClientConnectionOperator operator = new DefaultClientConnectionOperator(
+                SchemeRegistryFactory.createDefault());
+        operator.resolveHostname("localhost");
+    }
+
+    private InetAddress[] translateIp(String ip) throws UnknownHostException {
+        String[] ipParts = ip.split("\\.");
+
+        byte[] byteIpAddress = new byte[4];
+        for (int i = 0; i < 4; i++) {
+            byteIpAddress[i] = Integer.decode(ipParts[i]).byteValue();
+        }
+        return new InetAddress[] { InetAddress.getByAddress(byteIpAddress) };
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultHttpResponseParser.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultHttpResponseParser.java
new file mode 100644
index 0000000..4286a56
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultHttpResponseParser.java
@@ -0,0 +1,139 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.ProtocolException;
+import org.apache.http.impl.DefaultHttpResponseFactory;
+import org.apache.http.io.HttpMessageParser;
+import org.apache.http.io.SessionInputBuffer;
+import org.apache.http.message.BasicLineParser;
+import org.apache.http.mockup.SessionInputBufferMockup;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.util.CharArrayBuffer;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for <code>DefaultResponseParser</code>.
+ */
+public class TestDefaultHttpResponseParser {
+
+    @Test
+    public void testResponseParsingWithSomeGarbage() throws Exception {
+        String s =
+            "garbage\r\n" +
+            "garbage\r\n" +
+            "more garbage\r\n" +
+            "HTTP/1.1 200 OK\r\n" +
+            "header1: value1\r\n" +
+            "header2: value2\r\n" +
+            "\r\n";
+
+        HttpParams params = new BasicHttpParams();
+        SessionInputBuffer inbuffer = new SessionInputBufferMockup(s, "US-ASCII", params);
+        HttpMessageParser<HttpResponse> parser = new DefaultHttpResponseParser(
+                inbuffer,
+                BasicLineParser.DEFAULT,
+                new DefaultHttpResponseFactory(),
+                params);
+
+        HttpResponse response = (HttpResponse) parser.parse();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpVersion.HTTP_1_1, response.getProtocolVersion());
+        Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+
+        Header[] headers = response.getAllHeaders();
+        Assert.assertNotNull(headers);
+        Assert.assertEquals(2, headers.length);
+        Assert.assertEquals("header1", headers[0].getName());
+        Assert.assertEquals("header2", headers[1].getName());
+    }
+
+    @Test(expected=ProtocolException.class)
+    public void testResponseParsingWithTooMuchGarbage() throws Exception {
+        String s =
+            "garbage\r\n" +
+            "garbage\r\n" +
+            "more garbage\r\n" +
+            "HTTP/1.1 200 OK\r\n" +
+            "header1: value1\r\n" +
+            "header2: value2\r\n" +
+            "\r\n";
+
+        HttpParams params = new BasicHttpParams();
+        SessionInputBuffer inbuffer = new SessionInputBufferMockup(s, "US-ASCII", params);
+        HttpMessageParser<HttpResponse> parser = new DefaultHttpResponseParser(
+                inbuffer,
+                BasicLineParser.DEFAULT,
+                new DefaultHttpResponseFactory(),
+                params) {
+
+                    @Override
+                    protected boolean reject(final CharArrayBuffer line, int count) {
+                        return count >= 2;
+                    }
+
+        };
+        parser.parse();
+    }
+
+    @Test(expected=NoHttpResponseException.class)
+    public void testResponseParsingNoResponse() throws Exception {
+        HttpParams params = new BasicHttpParams();
+        SessionInputBuffer inbuffer = new SessionInputBufferMockup("", "US-ASCII", params);
+        HttpMessageParser<HttpResponse> parser = new DefaultHttpResponseParser(
+                inbuffer,
+                BasicLineParser.DEFAULT,
+                new DefaultHttpResponseFactory(),
+                params);
+        parser.parse();
+    }
+
+    @Test(expected=ProtocolException.class)
+    public void testResponseParsingOnlyGarbage() throws Exception {
+        String s =
+            "garbage\r\n" +
+            "garbage\r\n" +
+            "more garbage\r\n" +
+            "a lot more garbage\r\n";
+        HttpParams params = new BasicHttpParams();
+        SessionInputBuffer inbuffer = new SessionInputBufferMockup(s, "US-ASCII", params);
+        HttpMessageParser<HttpResponse> parser = new DefaultHttpResponseParser(
+                inbuffer,
+                BasicLineParser.DEFAULT,
+                new DefaultHttpResponseFactory(),
+                params);
+        parser.parse();
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultResponseParser.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultResponseParser.java
deleted file mode 100644
index 2bd9e9b..0000000
--- a/httpclient/src/test/java/org/apache/http/impl/conn/TestDefaultResponseParser.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.impl.conn;
-
-import org.apache.http.Header;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpVersion;
-import org.apache.http.NoHttpResponseException;
-import org.apache.http.ProtocolException;
-import org.apache.http.conn.params.ConnConnectionPNames;
-import org.apache.http.impl.DefaultHttpResponseFactory;
-import org.apache.http.io.HttpMessageParser;
-import org.apache.http.io.SessionInputBuffer;
-import org.apache.http.message.BasicLineParser;
-import org.apache.http.mockup.SessionInputBufferMockup;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpParams;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Tests for <code>DefaultResponseParser</code>.
- */
-public class TestDefaultResponseParser {
-
-    @Test
-    public void testResponseParsingWithSomeGarbage() throws Exception {
-        String s =
-            "garbage\r\n" +
-            "garbage\r\n" +
-            "more garbage\r\n" +
-            "HTTP/1.1 200 OK\r\n" +
-            "header1: value1\r\n" +
-            "header2: value2\r\n" +
-            "\r\n";
-
-        HttpParams params = new BasicHttpParams();
-        SessionInputBuffer inbuffer = new SessionInputBufferMockup(s, "US-ASCII", params);
-        HttpMessageParser parser = new DefaultResponseParser(
-                inbuffer,
-                BasicLineParser.DEFAULT,
-                new DefaultHttpResponseFactory(),
-                params);
-
-        HttpResponse response = (HttpResponse) parser.parse();
-        Assert.assertNotNull(response);
-        Assert.assertEquals(HttpVersion.HTTP_1_1, response.getProtocolVersion());
-        Assert.assertEquals(200, response.getStatusLine().getStatusCode());
-
-        Header[] headers = response.getAllHeaders();
-        Assert.assertNotNull(headers);
-        Assert.assertEquals(2, headers.length);
-        Assert.assertEquals("header1", headers[0].getName());
-        Assert.assertEquals("header2", headers[1].getName());
-    }
-
-    @Test(expected=ProtocolException.class)
-    public void testResponseParsingWithTooMuchGarbage() throws Exception {
-        String s =
-            "garbage\r\n" +
-            "garbage\r\n" +
-            "more garbage\r\n" +
-            "HTTP/1.1 200 OK\r\n" +
-            "header1: value1\r\n" +
-            "header2: value2\r\n" +
-            "\r\n";
-
-        HttpParams params = new BasicHttpParams();
-        params.setParameter(ConnConnectionPNames.MAX_STATUS_LINE_GARBAGE, Integer.valueOf(2));
-        SessionInputBuffer inbuffer = new SessionInputBufferMockup(s, "US-ASCII", params);
-        HttpMessageParser parser = new DefaultResponseParser(
-                inbuffer,
-                BasicLineParser.DEFAULT,
-                new DefaultHttpResponseFactory(),
-                params);
-        parser.parse();
-    }
-
-    @Test(expected=NoHttpResponseException.class)
-    public void testResponseParsingNoResponse() throws Exception {
-        HttpParams params = new BasicHttpParams();
-        SessionInputBuffer inbuffer = new SessionInputBufferMockup("", "US-ASCII", params);
-        HttpMessageParser parser = new DefaultResponseParser(
-                inbuffer,
-                BasicLineParser.DEFAULT,
-                new DefaultHttpResponseFactory(),
-                params);
-        parser.parse();
-    }
-
-    @Test(expected=ProtocolException.class)
-    public void testResponseParsingOnlyGarbage() throws Exception {
-        String s =
-            "garbage\r\n" +
-            "garbage\r\n" +
-            "more garbage\r\n" +
-            "a lot more garbage\r\n";
-        HttpParams params = new BasicHttpParams();
-        SessionInputBuffer inbuffer = new SessionInputBufferMockup(s, "US-ASCII", params);
-        HttpMessageParser parser = new DefaultResponseParser(
-                inbuffer,
-                BasicLineParser.DEFAULT,
-                new DefaultHttpResponseFactory(),
-                params);
-        parser.parse();
-    }
-
-}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestIdleConnectionEviction.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestIdleConnectionEviction.java
index 80d83e3..62dd2bc 100644
--- a/httpclient/src/test/java/org/apache/http/impl/conn/TestIdleConnectionEviction.java
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestIdleConnectionEviction.java
@@ -41,7 +41,6 @@ import org.apache.http.conn.scheme.PlainSocketFactory;
 import org.apache.http.conn.scheme.Scheme;
 import org.apache.http.conn.scheme.SchemeRegistry;
 import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
 import org.apache.http.localserver.LocalTestServer;
 import org.apache.http.localserver.ServerTestBase;
 import org.apache.http.params.BasicHttpParams;
@@ -69,7 +68,7 @@ public class TestIdleConnectionEviction extends ServerTestBase {
         SchemeRegistry schemeRegistry = new SchemeRegistry();
         schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
 
-        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(schemeRegistry);
+        PoolingClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry);
         cm.setDefaultMaxPerRoute(10);
         cm.setMaxTotal(50);
 
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManager.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManager.java
new file mode 100644
index 0000000..52fa9ac
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManager.java
@@ -0,0 +1,790 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionOperator;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.OperatedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.scheme.SchemeSocketFactory;
+import org.apache.http.localserver.ServerTestBase;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Tests for <code>PoolingClientConnectionManager</code> that do require a server
+ * to communicate with.
+ */
+public class TestPoolingConnManager extends ServerTestBase {
+
+    /**
+     * Tests executing several requests in parallel.
+     */
+    @Test
+    public void testParallelRequests() throws Exception {
+        // 3.x: TestHttpConnectionManager.testGetFromMultipleThreads
+
+        final int COUNT = 8; // adjust to execute more requests
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(COUNT/2);
+        mgr.setDefaultMaxPerRoute(COUNT/2);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+        final int      rsplen = 8;
+        final String      uri = "/random/" + rsplen;
+
+        ExecReqThread[] threads = new ExecReqThread [COUNT];
+        for (int i=0; i<COUNT; i++) {
+
+            HttpRequest request = new BasicHttpRequest
+                ("GET", uri, HttpVersion.HTTP_1_1);
+
+            ExecReqThread.RequestSpec ertrs = new ExecReqThread.RequestSpec();
+            ertrs.executor = httpExecutor;
+            ertrs.processor = httpProcessor;
+            ertrs.context = new BasicHttpContext(null);
+            ertrs.params = defaultParams;
+
+            ertrs.context.setAttribute
+                (ExecutionContext.HTTP_TARGET_HOST, target);
+            ertrs.context.setAttribute
+                (ExecutionContext.HTTP_REQUEST, request);
+
+            threads[i] = new ExecReqThread(mgr, route, 5000L, ertrs);
+        }
+
+        for (int i=0; i<threads.length; i++) {
+            threads[i].start();
+        }
+
+        for (int i=0; i<threads.length; i++) {
+            threads[i].join(10000);
+            Assert.assertNull("exception in thread " + i,
+                       threads[i].getException());
+            Assert.assertNotNull("no response in thread " + i,
+                          threads[i].getResponse());
+            Assert.assertEquals("wrong status code in thread " + i, 200,
+                         threads[i].getResponse()
+                         .getStatusLine().getStatusCode());
+            Assert.assertNotNull("no response data in thread " + i,
+                          threads[i].getResponseData());
+            Assert.assertEquals("wrong length of data in thread" + i, rsplen,
+                         threads[i].getResponseData().length);
+        }
+
+        mgr.shutdown();
+    }
+
+    private static ManagedClientConnection getConnection(
+            final ClientConnectionManager mgr,
+            final HttpRoute route,
+            long timeout,
+            TimeUnit unit) throws ConnectionPoolTimeoutException, InterruptedException {
+        ClientConnectionRequest connRequest = mgr.requestConnection(route, null);
+        return connRequest.getConnection(timeout, unit);
+    }
+
+    private static ManagedClientConnection getConnection(
+            final ClientConnectionManager mgr,
+            final HttpRoute route) throws ConnectionPoolTimeoutException, InterruptedException {
+        ClientConnectionRequest connRequest = mgr.requestConnection(route, null);
+        return connRequest.getConnection(0, null);
+    }
+
+    /**
+     * Tests releasing and re-using a connection after a response is read.
+     */
+    @Test
+    public void testReleaseConnection() throws Exception {
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(1);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+        final int      rsplen = 8;
+        final String      uri = "/random/" + rsplen;
+
+        HttpRequest request =
+            new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
+
+        ManagedClientConnection conn = getConnection(mgr, route);
+        conn.open(route, httpContext, defaultParams);
+
+        // a new context is created for each testcase, no need to reset
+        HttpResponse response = Helper.execute(
+                request, conn, target,
+                httpExecutor, httpProcessor, defaultParams, httpContext);
+
+        Assert.assertEquals("wrong status in first response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        byte[] data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of first response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        // check that there is no auto-release by default
+        try {
+            // this should fail quickly, connection has not been released
+            getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        // release connection without marking for re-use
+        // expect the next connection obtained to be closed
+        mgr.releaseConnection(conn, -1, null);
+        conn = getConnection(mgr, route);
+        Assert.assertFalse("connection should have been closed", conn.isOpen());
+
+        // repeat the communication, no need to prepare the request again
+        conn.open(route, httpContext, defaultParams);
+        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+        response = httpExecutor.execute(request, conn, httpContext);
+        httpExecutor.postProcess(response, httpProcessor, httpContext);
+
+        Assert.assertEquals("wrong status in second response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of second response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        // release connection after marking it for re-use
+        // expect the next connection obtained to be open
+        conn.markReusable();
+        mgr.releaseConnection(conn, -1, null);
+        conn = getConnection(mgr, route);
+        Assert.assertTrue("connection should have been open", conn.isOpen());
+
+        // repeat the communication, no need to prepare the request again
+        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+        response = httpExecutor.execute(request, conn, httpContext);
+        httpExecutor.postProcess(response, httpProcessor, httpContext);
+
+        Assert.assertEquals("wrong status in third response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of third response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        mgr.releaseConnection(conn, -1, null);
+        mgr.shutdown();
+    }
+
+    /**
+     * Tests releasing with time limits.
+     */
+    @Test
+    public void testReleaseConnectionWithTimeLimits() throws Exception {
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(1);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+        final int      rsplen = 8;
+        final String      uri = "/random/" + rsplen;
+
+        HttpRequest request =
+            new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
+
+        ManagedClientConnection conn = getConnection(mgr, route);
+        conn.open(route, httpContext, defaultParams);
+
+        // a new context is created for each testcase, no need to reset
+        HttpResponse response = Helper.execute(
+                request, conn, target,
+                httpExecutor, httpProcessor, defaultParams, httpContext);
+
+        Assert.assertEquals("wrong status in first response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        byte[] data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of first response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        // check that there is no auto-release by default
+        try {
+            // this should fail quickly, connection has not been released
+            getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        // release connection without marking for re-use
+        // expect the next connection obtained to be closed
+        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
+        conn = getConnection(mgr, route);
+        Assert.assertFalse("connection should have been closed", conn.isOpen());
+
+        // repeat the communication, no need to prepare the request again
+        conn.open(route, httpContext, defaultParams);
+        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+        response = httpExecutor.execute(request, conn, httpContext);
+        httpExecutor.postProcess(response, httpProcessor, httpContext);
+
+        Assert.assertEquals("wrong status in second response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of second response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        // release connection after marking it for re-use
+        // expect the next connection obtained to be open
+        conn.markReusable();
+        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
+        conn = getConnection(mgr, route);
+        Assert.assertTrue("connection should have been open", conn.isOpen());
+
+        // repeat the communication, no need to prepare the request again
+        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+        response = httpExecutor.execute(request, conn, httpContext);
+        httpExecutor.postProcess(response, httpProcessor, httpContext);
+
+        Assert.assertEquals("wrong status in third response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of third response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        conn.markReusable();
+        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
+        Thread.sleep(150);
+        conn = getConnection(mgr, route);
+        Assert.assertTrue("connection should have been closed", !conn.isOpen());
+
+        // repeat the communication, no need to prepare the request again
+        conn.open(route, httpContext, defaultParams);
+        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
+        response = httpExecutor.execute(request, conn, httpContext);
+        httpExecutor.postProcess(response, httpProcessor, httpContext);
+
+        Assert.assertEquals("wrong status in third response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+        data = EntityUtils.toByteArray(response.getEntity());
+        Assert.assertEquals("wrong length of fourth response entity",
+                     rsplen, data.length);
+        // ignore data, but it must be read
+
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testCloseExpiredIdleConnections() throws Exception {
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(1);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+
+        ManagedClientConnection conn = getConnection(mgr, route);
+        conn.open(route, httpContext, defaultParams);
+
+        Assert.assertEquals(1, mgr.getTotalStats().getLeased());
+        Assert.assertEquals(1, mgr.getStats(route).getLeased());
+        conn.markReusable();
+        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
+
+        // Released, still active.
+        Assert.assertEquals(1, mgr.getTotalStats().getAvailable());
+        Assert.assertEquals(1, mgr.getStats(route).getAvailable());
+
+        mgr.closeExpiredConnections();
+
+        // Time has not expired yet.
+        Assert.assertEquals(1, mgr.getTotalStats().getAvailable());
+        Assert.assertEquals(1, mgr.getStats(route).getAvailable());
+
+        Thread.sleep(150);
+
+        mgr.closeExpiredConnections();
+
+        // Time expired now, connections are destroyed.
+        Assert.assertEquals(0, mgr.getTotalStats().getAvailable());
+        Assert.assertEquals(0, mgr.getStats(route).getAvailable());
+
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testCloseExpiredTTLConnections() throws Exception {
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(
+                SchemeRegistryFactory.createDefault(), 100, TimeUnit.MILLISECONDS);
+        mgr.setMaxTotal(1);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+
+        ManagedClientConnection conn = getConnection(mgr, route);
+        conn.open(route, httpContext, defaultParams);
+
+        Assert.assertEquals(1, mgr.getTotalStats().getLeased());
+        Assert.assertEquals(1, mgr.getStats(route).getLeased());
+        // Release, let remain idle for forever
+        conn.markReusable();
+        mgr.releaseConnection(conn, -1, TimeUnit.MILLISECONDS);
+
+        // Released, still active.
+        Assert.assertEquals(1, mgr.getTotalStats().getAvailable());
+        Assert.assertEquals(1, mgr.getStats(route).getAvailable());
+
+        mgr.closeExpiredConnections();
+
+        // Time has not expired yet.
+        Assert.assertEquals(1, mgr.getTotalStats().getAvailable());
+        Assert.assertEquals(1, mgr.getStats(route).getAvailable());
+
+        Thread.sleep(150);
+
+        mgr.closeExpiredConnections();
+
+        // TTL expired now, connections are destroyed.
+        Assert.assertEquals(0, mgr.getTotalStats().getAvailable());
+        Assert.assertEquals(0, mgr.getStats(route).getAvailable());
+
+        mgr.shutdown();
+    }
+
+    /**
+     * Tests releasing connection from #abort method called from the
+     * main execution thread while there is no blocking I/O operation.
+     */
+    @Test
+    public void testReleaseConnectionOnAbort() throws Exception {
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(1);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+        final int      rsplen = 8;
+        final String      uri = "/random/" + rsplen;
+
+        HttpRequest request =
+            new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
+
+        ManagedClientConnection conn = getConnection(mgr, route);
+        conn.open(route, httpContext, defaultParams);
+
+        // a new context is created for each testcase, no need to reset
+        HttpResponse response = Helper.execute(
+                request, conn, target,
+                httpExecutor, httpProcessor, defaultParams, httpContext);
+
+        Assert.assertEquals("wrong status in first response",
+                     HttpStatus.SC_OK,
+                     response.getStatusLine().getStatusCode());
+
+        // check that there are no connections available
+        try {
+            // this should fail quickly, connection has not been released
+            getConnection(mgr, route, 100L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        // abort the connection
+        Assert.assertTrue(conn instanceof ManagedClientConnection);
+        ((ManagedClientConnection) conn).abortConnection();
+
+        // the connection is expected to be released back to the manager
+        conn = getConnection(mgr, route, 5L, TimeUnit.SECONDS);
+        Assert.assertFalse("connection should have been closed", conn.isOpen());
+
+        mgr.releaseConnection(conn, -1, null);
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testAbortDuringConnecting() throws Exception {
+        final CountDownLatch connectLatch = new CountDownLatch(1);
+        final StallingSocketFactory stallingSocketFactory = new StallingSocketFactory(
+                connectLatch, WaitPolicy.BEFORE_CONNECT, PlainSocketFactory.getSocketFactory());
+        Scheme scheme = new Scheme("http", 80, stallingSocketFactory);
+        SchemeRegistry registry = new SchemeRegistry();
+        registry.register(scheme);
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(registry);
+        mgr.setMaxTotal(1);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+
+        final ManagedClientConnection conn = getConnection(mgr, route);
+
+        final AtomicReference<Throwable> throwRef = new AtomicReference<Throwable>();
+        Thread abortingThread = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    stallingSocketFactory.waitForState();
+                    conn.abortConnection();
+                    connectLatch.countDown();
+                } catch (Throwable e) {
+                    throwRef.set(e);
+                }
+            }
+        });
+        abortingThread.start();
+
+        try {
+            conn.open(route, httpContext, defaultParams);
+            Assert.fail("expected SocketException");
+        } catch(SocketException expected) {}
+
+        abortingThread.join(5000);
+        if(throwRef.get() != null)
+            throw new RuntimeException(throwRef.get());
+
+        Assert.assertFalse(conn.isOpen());
+        Assert.assertEquals(0, localServer.getAcceptedConnectionCount());
+
+        // the connection is expected to be released back to the manager
+        ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS);
+        Assert.assertFalse("connection should have been closed", conn2.isOpen());
+
+        mgr.releaseConnection(conn2, -1, null);
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testAbortBeforeSocketCreate() throws Exception {
+        final CountDownLatch connectLatch = new CountDownLatch(1);
+        final StallingSocketFactory stallingSocketFactory = new StallingSocketFactory(
+                connectLatch, WaitPolicy.BEFORE_CREATE, PlainSocketFactory.getSocketFactory());
+        Scheme scheme = new Scheme("http", 80, stallingSocketFactory);
+        SchemeRegistry registry = new SchemeRegistry();
+        registry.register(scheme);
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(registry);
+        mgr.setMaxTotal(1);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+
+        final ManagedClientConnection conn = getConnection(mgr, route);
+
+        final AtomicReference<Throwable> throwRef = new AtomicReference<Throwable>();
+        Thread abortingThread = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    stallingSocketFactory.waitForState();
+                    conn.abortConnection();
+                    connectLatch.countDown();
+                } catch (Throwable e) {
+                    throwRef.set(e);
+                }
+            }
+        });
+        abortingThread.start();
+
+        try {
+            conn.open(route, httpContext, defaultParams);
+            Assert.fail("expected exception");
+        } catch(IOException expected) {
+            Assert.assertEquals("Connection already shutdown", expected.getMessage());
+        }
+
+        abortingThread.join(5000);
+        if(throwRef.get() != null)
+            throw new RuntimeException(throwRef.get());
+
+        Assert.assertFalse(conn.isOpen());
+        Assert.assertEquals(0, localServer.getAcceptedConnectionCount());
+
+        // the connection is expected to be released back to the manager
+        ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS);
+        Assert.assertFalse("connection should have been closed", conn2.isOpen());
+
+        mgr.releaseConnection(conn2, -1, null);
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testAbortAfterSocketConnect() throws Exception {
+        final CountDownLatch connectLatch = new CountDownLatch(1);
+        final StallingSocketFactory stallingSocketFactory = new StallingSocketFactory(
+                connectLatch, WaitPolicy.AFTER_CONNECT, PlainSocketFactory.getSocketFactory());
+        Scheme scheme = new Scheme("http", 80, stallingSocketFactory);
+        SchemeRegistry registry = new SchemeRegistry();
+        registry.register(scheme);
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager(registry);
+        mgr.setMaxTotal(1);
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+
+        final ManagedClientConnection conn = getConnection(mgr, route);
+
+        final AtomicReference<Throwable> throwRef = new AtomicReference<Throwable>();
+        Thread abortingThread = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    stallingSocketFactory.waitForState();
+                    conn.abortConnection();
+                    connectLatch.countDown();
+                } catch (Throwable e) {
+                    throwRef.set(e);
+                }
+            }
+        });
+        abortingThread.start();
+
+        try {
+            conn.open(route, httpContext, defaultParams);
+            Assert.fail("expected SocketException");
+        } catch(SocketException expected) {}
+
+        abortingThread.join(5000);
+        if(throwRef.get() != null)
+            throw new RuntimeException(throwRef.get());
+
+        Assert.assertFalse(conn.isOpen());
+        // Give the server a bit of time to accept the connection, but
+        // ensure that it can accept it.
+        for(int i = 0; i < 10; i++) {
+            if(localServer.getAcceptedConnectionCount() == 1)
+                break;
+            Thread.sleep(100);
+        }
+        Assert.assertEquals(1, localServer.getAcceptedConnectionCount());
+
+        // the connection is expected to be released back to the manager
+        ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS);
+        Assert.assertFalse("connection should have been closed", conn2.isOpen());
+
+        mgr.releaseConnection(conn2, -1, null);
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testAbortAfterOperatorOpen() throws Exception {
+        final CountDownLatch connectLatch = new CountDownLatch(1);
+        final AtomicReference<StallingOperator> operatorRef = new AtomicReference<StallingOperator>();
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager() {
+            @Override
+            protected ClientConnectionOperator createConnectionOperator(
+                    SchemeRegistry schreg) {
+                operatorRef.set(new StallingOperator(connectLatch, WaitPolicy.AFTER_OPEN, super.createConnectionOperator(schreg)));
+                return operatorRef.get();
+            }
+        };
+        mgr.setMaxTotal(1);
+        Assert.assertNotNull(operatorRef.get());
+
+        final HttpHost target = getServerHttp();
+        final HttpRoute route = new HttpRoute(target, null, false);
+
+        final ManagedClientConnection conn = getConnection(mgr, route);
+
+        final AtomicReference<Throwable> throwRef = new AtomicReference<Throwable>();
+        Thread abortingThread = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    operatorRef.get().waitForState();
+                    conn.abortConnection();
+                    connectLatch.countDown();
+                } catch (Throwable e) {
+                    throwRef.set(e);
+                }
+            }
+        });
+        abortingThread.start();
+
+        try {
+            conn.open(route, httpContext, defaultParams);
+            Assert.fail("expected exception");
+        } catch(IOException iox) {
+        }
+
+        abortingThread.join(5000);
+        if(throwRef.get() != null)
+            throw new RuntimeException(throwRef.get());
+
+        Assert.assertFalse(conn.isOpen());
+        // Give the server a bit of time to accept the connection, but
+        // ensure that it can accept it.
+        for(int i = 0; i < 10; i++) {
+            if(localServer.getAcceptedConnectionCount() == 1)
+                break;
+            Thread.sleep(100);
+        }
+        Assert.assertEquals(1, localServer.getAcceptedConnectionCount());
+
+        // the connection is expected to be released back to the manager
+        ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS);
+        Assert.assertFalse("connection should have been closed", conn2.isOpen());
+
+        mgr.releaseConnection(conn2, -1, null);
+        mgr.shutdown();
+    }
+
+    private static class LatchSupport {
+        private final CountDownLatch continueLatch;
+        private final CountDownLatch waitLatch = new CountDownLatch(1);
+        protected final WaitPolicy waitPolicy;
+
+        LatchSupport(CountDownLatch continueLatch, WaitPolicy waitPolicy) {
+            this.continueLatch = continueLatch;
+            this.waitPolicy = waitPolicy;
+        }
+
+        void waitForState() throws InterruptedException {
+            if(!waitLatch.await(1, TimeUnit.SECONDS))
+                throw new RuntimeException("waited too long");
+        }
+
+        void latch() {
+            waitLatch.countDown();
+            try {
+                if (!continueLatch.await(60, TimeUnit.SECONDS))
+                    throw new RuntimeException("waited too long!");
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private static class StallingOperator extends LatchSupport implements ClientConnectionOperator {
+        private final ClientConnectionOperator delegate;
+
+        public StallingOperator(CountDownLatch continueLatch,
+                WaitPolicy waitPolicy, ClientConnectionOperator delegate) {
+            super(continueLatch, waitPolicy);
+            this.delegate = delegate;
+        }
+
+        public OperatedClientConnection createConnection() {
+            return delegate.createConnection();
+        }
+
+        public void openConnection(OperatedClientConnection conn,
+                HttpHost target, InetAddress local, HttpContext context,
+                HttpParams params) throws IOException {
+            delegate.openConnection(conn, target, local, context, params);
+            if(waitPolicy == WaitPolicy.AFTER_OPEN)
+                latch();
+        }
+
+        public void updateSecureConnection(OperatedClientConnection conn,
+                HttpHost target, HttpContext context, HttpParams params)
+                throws IOException {
+            delegate.updateSecureConnection(conn, target, context, params);
+        }
+    }
+
+    private static class StallingSocketFactory extends LatchSupport implements SchemeSocketFactory {
+
+        private final SchemeSocketFactory delegate;
+
+        public StallingSocketFactory(
+                final CountDownLatch continueLatch,
+                final WaitPolicy waitPolicy,
+                final SchemeSocketFactory delegate) {
+            super(continueLatch, waitPolicy);
+            this.delegate = delegate;
+        }
+
+        public Socket connectSocket(
+                final Socket sock,
+                final InetSocketAddress remoteAddress,
+                final InetSocketAddress localAddress,
+                final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
+            if(waitPolicy == WaitPolicy.BEFORE_CONNECT)
+                latch();
+
+            Socket socket = delegate.connectSocket(sock, remoteAddress, localAddress, params);
+
+            if(waitPolicy == WaitPolicy.AFTER_CONNECT)
+                latch();
+
+            return socket;
+        }
+
+        public Socket createSocket(final HttpParams params) throws IOException {
+            if(waitPolicy == WaitPolicy.BEFORE_CREATE)
+                latch();
+
+            return delegate.createSocket(params);
+        }
+
+        public boolean isSecure(Socket sock) throws IllegalArgumentException {
+            return delegate.isSecure(sock);
+        }
+    }
+
+    private enum WaitPolicy { BEFORE_CREATE, BEFORE_CONNECT, AFTER_CONNECT, AFTER_OPEN }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManagerNoServer.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManagerNoServer.java
new file mode 100644
index 0000000..a51f6e7
--- /dev/null
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/TestPoolingConnManagerNoServer.java
@@ -0,0 +1,533 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.http.impl.conn;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpVersion;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.ClientConnectionRequest;
+import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.ManagedClientConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * Tests for <code>PoolingClientConnectionManager</code> that do not require a server to
+ * communicate with.
+ */
+public class TestPoolingConnManagerNoServer {
+
+    private static ManagedClientConnection getConnection(
+            final ClientConnectionManager mgr,
+            final HttpRoute route,
+            long timeout,
+            TimeUnit unit) throws ConnectionPoolTimeoutException, InterruptedException {
+        ClientConnectionRequest connRequest = mgr.requestConnection(route, null);
+        return connRequest.getConnection(timeout, unit);
+    }
+
+    private static ManagedClientConnection getConnection(
+            final ClientConnectionManager mgr,
+            final HttpRoute route) throws ConnectionPoolTimeoutException, InterruptedException {
+        ClientConnectionRequest connRequest = mgr.requestConnection(route, null);
+        return connRequest.getConnection(0, null);
+    }
+
+    /**
+     * Instantiates default parameters.
+     *
+     * @return  the default parameters
+     */
+    public HttpParams createDefaultParams() {
+
+        HttpParams params = new BasicHttpParams();
+        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
+        HttpProtocolParams.setUseExpectContinue(params, false);
+
+        return params;
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testIllegalConstructor() {
+        new PoolingClientConnectionManager(null);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testGetConnection()
+            throws InterruptedException, ConnectionPoolTimeoutException {
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+
+        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
+        HttpRoute route = new HttpRoute(target, null, false);
+
+        ManagedClientConnection conn = getConnection(mgr, route);
+        Assert.assertNotNull(conn);
+        Assert.assertNull(conn.getRoute());
+        Assert.assertFalse(conn.isOpen());
+
+        mgr.releaseConnection(conn, -1, null);
+
+        try {
+            getConnection(mgr, null);
+        } finally {
+            mgr.shutdown();
+        }
+    }
+
+    // testTimeout in 3.x TestHttpConnectionManager is redundant
+    // several other tests here rely on timeout behavior
+    @Test
+    public void testMaxConnTotal()
+            throws InterruptedException, ConnectionPoolTimeoutException {
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(2);
+        mgr.setDefaultMaxPerRoute(1);
+
+        HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http");
+        HttpRoute route1 = new HttpRoute(target1, null, false);
+        HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http");
+        HttpRoute route2 = new HttpRoute(target2, null, false);
+
+        ManagedClientConnection conn1 = getConnection(mgr, route1);
+        Assert.assertNotNull(conn1);
+        ManagedClientConnection conn2 = getConnection(mgr, route2);
+        Assert.assertNotNull(conn2);
+
+        try {
+            // this should fail quickly, connection has not been released
+            getConnection(mgr, route2, 100L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        // release one of the connections
+        mgr.releaseConnection(conn2, -1, null);
+        conn2 = null;
+
+        // there should be a connection available now
+        try {
+            getConnection(mgr, route2, 100L, TimeUnit.MILLISECONDS);
+        } catch (ConnectionPoolTimeoutException cptx) {
+            Assert.fail("connection should have been available: " + cptx);
+        }
+
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testMaxConnPerHost() throws Exception {
+
+        HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http");
+        HttpRoute route1 = new HttpRoute(target1, null, false);
+        HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http");
+        HttpRoute route2 = new HttpRoute(target2, null, false);
+        HttpHost target3 = new HttpHost("www.test3.invalid", 80, "http");
+        HttpRoute route3 = new HttpRoute(target3, null, false);
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(100);
+        mgr.setDefaultMaxPerRoute(1);
+        mgr.setMaxPerRoute(route2, 2);
+        mgr.setMaxPerRoute(route3, 3);
+
+        // route 3, limit 3
+        ManagedClientConnection conn1 =
+            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
+        Assert.assertNotNull(conn1);
+        ManagedClientConnection conn2 =
+            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
+        Assert.assertNotNull(conn2);
+        ManagedClientConnection conn3 =
+            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
+        Assert.assertNotNull(conn3);
+        try {
+            // should fail quickly, connection has not been released
+            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        // route 2, limit 2
+        conn1 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
+        conn2 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
+        try {
+            // should fail quickly, connection has not been released
+            getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        // route 1, should use default limit of 1
+        conn1 = getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS);
+        try {
+            // should fail quickly, connection has not been released
+            getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+
+        // check releaseConnection with invalid arguments
+        try {
+            mgr.releaseConnection(null, -1, null);
+            Assert.fail("null connection adapter not detected");
+        } catch (IllegalArgumentException iax) {
+            // expected
+        }
+        try {
+            ManagedClientConnection conn = Mockito.mock(ManagedClientConnection.class);
+            mgr.releaseConnection(conn, -1, null);
+            Assert.fail("foreign connection adapter not detected");
+        } catch (IllegalArgumentException iax) {
+            // expected
+        }
+
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testReleaseConnection() throws Exception {
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(3);
+        mgr.setDefaultMaxPerRoute(1);
+
+        HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http");
+        HttpRoute route1 = new HttpRoute(target1, null, false);
+        HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http");
+        HttpRoute route2 = new HttpRoute(target2, null, false);
+        HttpHost target3 = new HttpHost("www.test3.invalid", 80, "http");
+        HttpRoute route3 = new HttpRoute(target3, null, false);
+
+        // the first three allocations should pass
+        ManagedClientConnection conn1 =
+            getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS);
+        ManagedClientConnection conn2 =
+            getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
+        ManagedClientConnection conn3 =
+            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
+        Assert.assertNotNull(conn1);
+        Assert.assertNotNull(conn2);
+        Assert.assertNotNull(conn3);
+
+        // obtaining another connection for either of the three should fail
+        // this is somehow redundant with testMaxConnPerHost
+        try {
+            getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+        try {
+            getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+        try {
+            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        // now release one and check that exactly that one can be obtained then
+        mgr.releaseConnection(conn2, -1, null);
+        conn2 = null;
+        try {
+            getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+        // this one succeeds
+        conn2 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
+        Assert.assertNotNull(conn2);
+        try {
+            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testDeleteClosedConnections()
+            throws InterruptedException, ConnectionPoolTimeoutException {
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+
+        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
+        HttpRoute route = new HttpRoute(target, null, false);
+
+        ManagedClientConnection conn = getConnection(mgr, route);
+
+        Assert.assertEquals(1, mgr.getTotalStats().getLeased());
+        Assert.assertEquals(1, mgr.getStats(route).getLeased());
+        conn.markReusable();
+        mgr.releaseConnection(conn, -1, null);
+
+        Assert.assertEquals(1, mgr.getTotalStats().getAvailable());
+        Assert.assertEquals(1, mgr.getStats(route).getAvailable());
+
+        // this implicitly deletes them
+        mgr.closeIdleConnections(0L, TimeUnit.MILLISECONDS);
+
+        Assert.assertEquals(0, mgr.getTotalStats().getAvailable());
+        Assert.assertEquals(0, mgr.getStats(route).getAvailable());
+
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testShutdown() throws Exception {
+        // 3.x: TestHttpConnectionManager.testShutdown
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(1);
+        mgr.setDefaultMaxPerRoute(1);
+
+        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
+        HttpRoute route = new HttpRoute(target, null, false);
+
+        // get the only connection, then start an extra thread
+        // on shutdown, the extra thread should get an exception
+
+        ManagedClientConnection conn =
+            getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS);
+        GetConnThread gct = new GetConnThread(mgr, route, 0L); // no timeout
+        gct.start();
+        Thread.sleep(100); // give extra thread time to block
+
+
+        mgr.shutdown();
+
+        // First release the connection. If the manager keeps working
+        // despite the shutdown, this will deblock the extra thread.
+        // The release itself should turn into a no-op, without exception.
+        mgr.releaseConnection(conn, -1, null);
+
+
+        gct.join(10000);
+        Assert.assertNull("thread should not have obtained connection",
+                   gct.getConnection());
+        Assert.assertNotNull("thread should have gotten an exception",
+                      gct.getException());
+        Assert.assertSame("thread got wrong exception",
+                InterruptedException.class, gct.getException().getClass());
+
+        // the manager is down, we should not be able to get a connection
+        try {
+            getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS);
+            Assert.fail("shut-down manager does not raise exception");
+        } catch (IllegalStateException isx) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testInterruptThread() throws Exception {
+        // 3.x: TestHttpConnectionManager.testWaitingThreadInterrupted
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(1);
+
+        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
+        HttpRoute route = new HttpRoute(target, null, false);
+
+        // get the only connection, then start an extra thread
+        ManagedClientConnection conn =
+            getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS);
+        GetConnThread gct = new GetConnThread(mgr, route, 0L); // no timeout
+        gct.start();
+        Thread.sleep(100); // give extra thread time to block
+
+
+        // interrupt the thread, it should cancel waiting with an exception
+        gct.interrupt();
+
+
+        gct.join(10000);
+        Assert.assertNotNull("thread should have gotten an exception",
+                      gct.getException());
+        Assert.assertSame("thread got wrong exception",
+                   InterruptedException.class,
+                   gct.getException().getClass());
+
+        // make sure the manager is still working
+        try {
+            getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("should have gotten a timeout");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        mgr.releaseConnection(conn, -1, null);
+        // this time: no exception
+        conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
+        Assert.assertNotNull("should have gotten a connection", conn);
+
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testReusePreference() throws Exception {
+        // 3.x: TestHttpConnectionManager.testHostReusePreference
+
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(1);
+
+        HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http");
+        HttpRoute route1 = new HttpRoute(target1, null, false);
+        HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http");
+        HttpRoute route2 = new HttpRoute(target2, null, false);
+
+        // get the only connection, then start two extra threads
+        ManagedClientConnection conn =
+            getConnection(mgr, route1, 1L, TimeUnit.MILLISECONDS);
+        GetConnThread gct1 = new GetConnThread(mgr, route1, 1000L);
+        GetConnThread gct2 = new GetConnThread(mgr, route2, 1000L);
+
+        // the second thread is started first, to distinguish the
+        // route-based reuse preference from first-come, first-served
+        gct2.start();
+        Thread.sleep(100); // give the thread time to block
+        gct1.start();
+        Thread.sleep(100); // give the thread time to block
+
+
+        // releasing the connection for route1 should deblock thread1
+        // the other thread gets a timeout
+        mgr.releaseConnection(conn, -1, null);
+
+        gct1.join(10000);
+        gct2.join(10000);
+
+        Assert.assertNotNull("thread 1 should have gotten a connection",
+                      gct1.getConnection());
+        Assert.assertNull   ("thread 2 should NOT have gotten a connection",
+                      gct2.getConnection());
+
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testAbortAfterRequestStarts() throws Exception {
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(1);
+
+        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
+        HttpRoute route = new HttpRoute(target, null, false);
+
+        // get the only connection, then start an extra thread
+        ManagedClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS);
+        ClientConnectionRequest request = mgr.requestConnection(route, null);
+        GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout
+        gct.start();
+        Thread.sleep(100); // give extra thread time to block
+
+        request.abortRequest();
+
+        gct.join(10000);
+        Assert.assertNotNull("thread should have gotten an exception",
+                      gct.getException());
+        Assert.assertSame("thread got wrong exception",
+                   InterruptedException.class,
+                   gct.getException().getClass());
+
+        // make sure the manager is still working
+        try {
+            getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("should have gotten a timeout");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        mgr.releaseConnection(conn, -1, null);
+        // this time: no exception
+        conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
+        Assert.assertNotNull("should have gotten a connection", conn);
+
+        mgr.shutdown();
+    }
+
+    @Test
+    public void testAbortBeforeRequestStarts() throws Exception {
+        PoolingClientConnectionManager mgr = new PoolingClientConnectionManager();
+        mgr.setMaxTotal(1);
+
+        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
+        HttpRoute route = new HttpRoute(target, null, false);
+
+        // get the only connection, then start an extra thread
+        ManagedClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS);
+        ClientConnectionRequest request = mgr.requestConnection(route, null);
+        request.abortRequest();
+
+        GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout
+        gct.start();
+        Thread.sleep(100); // give extra thread time to block
+
+        gct.join(10000);
+        Assert.assertNotNull("thread should have gotten an exception",
+                      gct.getException());
+        Assert.assertSame("thread got wrong exception",
+                   InterruptedException.class,
+                   gct.getException().getClass());
+
+        // make sure the manager is still working
+        try {
+            getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
+            Assert.fail("should have gotten a timeout");
+        } catch (ConnectionPoolTimeoutException e) {
+            // expected
+        }
+
+        mgr.releaseConnection(conn, -1, null);
+        // this time: no exception
+        conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
+        Assert.assertNotNull("should have gotten a connection", conn);
+
+        mgr.shutdown();
+    }
+
+}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestSCMWithServer.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestSCMWithServer.java
deleted file mode 100644
index 572802d..0000000
--- a/httpclient/src/test/java/org/apache/http/impl/conn/TestSCMWithServer.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.impl.conn;
-
-import java.util.concurrent.TimeUnit;
-
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.HttpVersion;
-import org.apache.http.conn.ManagedClientConnection;
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.localserver.ServerTestBase;
-import org.apache.http.message.BasicHttpRequest;
-import org.apache.http.protocol.ExecutionContext;
-import org.apache.http.util.EntityUtils;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestSCMWithServer extends ServerTestBase {
-
-    /**
-     * Helper to instantiate a <code>SingleClientConnManager</code>.
-     *
-     * @param schreg    the scheme registry, or
-     *                  <code>null</code> to use defaults
-     *
-     * @return  a connection manager to test
-     */
-    public SingleClientConnManager createSCCM(SchemeRegistry schreg) {
-        if (schreg == null)
-            schreg = supportedSchemes;
-        return new SingleClientConnManager(schreg);
-    }
-
-    /**
-     * Tests that SCM can still connect to the same host after
-     * a connection was aborted.
-     */
-    @Test
-    public void testOpenAfterAbort() throws Exception {
-        SingleClientConnManager mgr = createSCCM(null);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-
-        ManagedClientConnection conn = mgr.getConnection(route, null);
-        Assert.assertTrue(conn instanceof AbstractClientConnAdapter);
-        ((AbstractClientConnAdapter) conn).abortConnection();
-
-        conn = mgr.getConnection(route, null);
-        Assert.assertFalse("connection should have been closed", conn.isOpen());
-        conn.open(route, httpContext, defaultParams);
-
-        mgr.releaseConnection(conn, -1, null);
-        mgr.shutdown();
-    }
-
-    /**
-     * Tests releasing with time limits.
-     */
-    @Test
-    public void testReleaseConnectionWithTimeLimits() throws Exception {
-
-        SingleClientConnManager mgr = createSCCM(null);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-        final int      rsplen = 8;
-        final String      uri = "/random/" + rsplen;
-
-        HttpRequest request =
-            new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
-
-        ManagedClientConnection conn = mgr.getConnection(route, null);
-        conn.open(route, httpContext, defaultParams);
-
-        // a new context is created for each testcase, no need to reset
-        HttpResponse response = Helper.execute(
-                request, conn, target,
-                httpExecutor, httpProcessor, defaultParams, httpContext);
-
-        Assert.assertEquals("wrong status in first response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        byte[] data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of first response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        // release connection without marking for re-use
-        // expect the next connection obtained to be closed
-        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
-        conn = mgr.getConnection(route, null);
-        Assert.assertFalse("connection should have been closed", conn.isOpen());
-
-        // repeat the communication, no need to prepare the request again
-        conn.open(route, httpContext, defaultParams);
-        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
-        response = httpExecutor.execute(request, conn, httpContext);
-        httpExecutor.postProcess(response, httpProcessor, httpContext);
-
-        Assert.assertEquals("wrong status in second response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of second response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        // release connection after marking it for re-use
-        // expect the next connection obtained to be open
-        conn.markReusable();
-        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
-        conn =  mgr.getConnection(route, null);
-        Assert.assertTrue("connection should have been open", conn.isOpen());
-
-        // repeat the communication, no need to prepare the request again
-        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
-        response = httpExecutor.execute(request, conn, httpContext);
-        httpExecutor.postProcess(response, httpProcessor, httpContext);
-
-        Assert.assertEquals("wrong status in third response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of third response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        conn.markReusable();
-        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
-        Thread.sleep(150);
-        conn =  mgr.getConnection(route, null);
-        Assert.assertTrue("connection should have been closed", !conn.isOpen());
-
-        // repeat the communication, no need to prepare the request again
-        conn.open(route, httpContext, defaultParams);
-        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
-        response = httpExecutor.execute(request, conn, httpContext);
-        httpExecutor.postProcess(response, httpProcessor, httpContext);
-
-        Assert.assertEquals("wrong status in third response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of fourth response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testCloseExpiredConnections() throws Exception {
-
-        SingleClientConnManager mgr = createSCCM(null);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-
-        ManagedClientConnection conn =  mgr.getConnection(route, null);
-        conn.open(route, httpContext, defaultParams);
-        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
-
-        mgr.closeExpiredConnections();
-
-        conn = mgr.getConnection(route, null);
-        Assert.assertTrue(conn.isOpen());
-        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
-
-        Thread.sleep(150);
-        mgr.closeExpiredConnections();
-        conn = mgr.getConnection(route, null);
-        Assert.assertFalse(conn.isOpen());
-
-        mgr.shutdown();
-    }
-
-    @Test(expected=IllegalStateException.class)
-    public void testAlreadyLeased() throws Exception {
-
-        SingleClientConnManager mgr = createSCCM(null);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-
-        ManagedClientConnection conn =  mgr.getConnection(route, null);
-        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
-
-        mgr.getConnection(route, null);
-        mgr.getConnection(route, null);
-    }
-
-}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestTSCCMNoServer.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestTSCCMNoServer.java
deleted file mode 100644
index 79a1563..0000000
--- a/httpclient/src/test/java/org/apache/http/impl/conn/TestTSCCMNoServer.java
+++ /dev/null
@@ -1,577 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.impl.conn;
-
-import java.util.concurrent.TimeUnit;
-
-import org.apache.http.HttpHost;
-import org.apache.http.HttpVersion;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.ClientConnectionRequest;
-import org.apache.http.conn.ConnectionPoolTimeoutException;
-import org.apache.http.conn.ManagedClientConnection;
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpParams;
-import org.apache.http.params.HttpProtocolParams;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Tests for <code>ThreadSafeClientConnManager</code> that do not require
- * a server to communicate with.
- */
-public class TestTSCCMNoServer {
-
-    private static ManagedClientConnection getConnection(
-            final ClientConnectionManager mgr,
-            final HttpRoute route,
-            long timeout,
-            TimeUnit unit) throws ConnectionPoolTimeoutException, InterruptedException {
-        ClientConnectionRequest connRequest = mgr.requestConnection(route, null);
-        return connRequest.getConnection(timeout, unit);
-    }
-
-    private static ManagedClientConnection getConnection(
-            final ClientConnectionManager mgr,
-            final HttpRoute route) throws ConnectionPoolTimeoutException, InterruptedException {
-        ClientConnectionRequest connRequest = mgr.requestConnection(route, null);
-        return connRequest.getConnection(0, null);
-    }
-
-    /**
-     * Helper to instantiate a <code>ThreadSafeClientConnManager</code>.
-     *
-     * @param schreg    the scheme registry, or
-     *                  <code>null</code> to use defaults
-     *
-     * @return  a connection manager to test
-     */
-    public ThreadSafeClientConnManager createTSCCM(SchemeRegistry schreg) {
-        if (schreg == null)
-            schreg = createSchemeRegistry();
-        return new ThreadSafeClientConnManager(schreg);
-    }
-
-
-    /**
-     * Instantiates default parameters.
-     *
-     * @return  the default parameters
-     */
-    public HttpParams createDefaultParams() {
-
-        HttpParams params = new BasicHttpParams();
-        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
-        HttpProtocolParams.setUseExpectContinue(params, false);
-
-        return params;
-    }
-
-    /**
-     * Instantiates a default scheme registry.
-     *
-     * @return the default scheme registry
-     */
-    public SchemeRegistry createSchemeRegistry() {
-
-        SchemeRegistry schreg = new SchemeRegistry();
-        schreg.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
-
-        return schreg;
-    }
-
-    @Test
-    public void testConstructor() {
-        SchemeRegistry schreg = createSchemeRegistry();
-
-        ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(schreg);
-        Assert.assertNotNull(mgr);
-        mgr.shutdown();
-    }
-
-    @Test(expected=IllegalArgumentException.class)
-    public void testIllegalConstructor() {
-        new ThreadSafeClientConnManager(null);
-    }
-
-    @Test(expected=IllegalArgumentException.class)
-    public void testGetConnection()
-            throws InterruptedException, ConnectionPoolTimeoutException {
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-
-        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
-        HttpRoute route = new HttpRoute(target, null, false);
-
-        ManagedClientConnection conn = getConnection(mgr, route);
-        Assert.assertNotNull(conn);
-        Assert.assertNull(conn.getRoute());
-        Assert.assertFalse(conn.isOpen());
-
-        mgr.releaseConnection(conn, -1, null);
-
-        try {
-            getConnection(mgr, null);
-        } finally {
-            mgr.shutdown();
-        }
-    }
-
-    // testTimeout in 3.x TestHttpConnectionManager is redundant
-    // several other tests here rely on timeout behavior
-    @Test
-    public void testMaxConnTotal()
-            throws InterruptedException, ConnectionPoolTimeoutException {
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(2);
-        mgr.setDefaultMaxPerRoute(1);
-
-        HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http");
-        HttpRoute route1 = new HttpRoute(target1, null, false);
-        HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http");
-        HttpRoute route2 = new HttpRoute(target2, null, false);
-
-        ManagedClientConnection conn1 = getConnection(mgr, route1);
-        Assert.assertNotNull(conn1);
-        ManagedClientConnection conn2 = getConnection(mgr, route2);
-        Assert.assertNotNull(conn2);
-
-        try {
-            // this should fail quickly, connection has not been released
-            getConnection(mgr, route2, 100L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        // release one of the connections
-        mgr.releaseConnection(conn2, -1, null);
-        conn2 = null;
-
-        // there should be a connection available now
-        try {
-            getConnection(mgr, route2, 100L, TimeUnit.MILLISECONDS);
-        } catch (ConnectionPoolTimeoutException cptx) {
-            Assert.fail("connection should have been available: " + cptx);
-        }
-
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testMaxConnPerHost() throws Exception {
-
-        HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http");
-        HttpRoute route1 = new HttpRoute(target1, null, false);
-        HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http");
-        HttpRoute route2 = new HttpRoute(target2, null, false);
-        HttpHost target3 = new HttpHost("www.test3.invalid", 80, "http");
-        HttpRoute route3 = new HttpRoute(target3, null, false);
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(100);
-        mgr.setDefaultMaxPerRoute(1);
-        mgr.setMaxForRoute(route2, 2);
-        mgr.setMaxForRoute(route3, 3);
-
-        // route 3, limit 3
-        ManagedClientConnection conn1 =
-            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
-        Assert.assertNotNull(conn1);
-        ManagedClientConnection conn2 =
-            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
-        Assert.assertNotNull(conn2);
-        ManagedClientConnection conn3 =
-            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
-        Assert.assertNotNull(conn3);
-        try {
-            // should fail quickly, connection has not been released
-            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        // route 2, limit 2
-        conn1 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
-        conn2 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
-        try {
-            // should fail quickly, connection has not been released
-            getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        // route 1, should use default limit of 1
-        conn1 = getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS);
-        try {
-            // should fail quickly, connection has not been released
-            getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-
-        // check releaseConnection with invalid arguments
-        try {
-            mgr.releaseConnection(null, -1, null);
-            Assert.fail("null connection adapter not detected");
-        } catch (IllegalArgumentException iax) {
-            // expected
-        }
-        try {
-            mgr.releaseConnection(new ClientConnAdapterMockup(null), -1, null);
-            Assert.fail("foreign connection adapter not detected");
-        } catch (IllegalArgumentException iax) {
-            // expected
-        }
-
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testReleaseConnection() throws Exception {
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(3);
-        mgr.setDefaultMaxPerRoute(1);
-
-        HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http");
-        HttpRoute route1 = new HttpRoute(target1, null, false);
-        HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http");
-        HttpRoute route2 = new HttpRoute(target2, null, false);
-        HttpHost target3 = new HttpHost("www.test3.invalid", 80, "http");
-        HttpRoute route3 = new HttpRoute(target3, null, false);
-
-        // the first three allocations should pass
-        ManagedClientConnection conn1 =
-            getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS);
-        ManagedClientConnection conn2 =
-            getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
-        ManagedClientConnection conn3 =
-            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
-        Assert.assertNotNull(conn1);
-        Assert.assertNotNull(conn2);
-        Assert.assertNotNull(conn3);
-
-        // obtaining another connection for either of the three should fail
-        // this is somehow redundant with testMaxConnPerHost
-        try {
-            getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-        try {
-            getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-        try {
-            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        // now release one and check that exactly that one can be obtained then
-        mgr.releaseConnection(conn2, -1, null);
-        conn2 = null;
-        try {
-            getConnection(mgr, route1, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-        // this one succeeds
-        conn2 = getConnection(mgr, route2, 10L, TimeUnit.MILLISECONDS);
-        Assert.assertNotNull(conn2);
-        try {
-            getConnection(mgr, route3, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testDeleteClosedConnections()
-            throws InterruptedException, ConnectionPoolTimeoutException {
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-
-        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
-        HttpRoute route = new HttpRoute(target, null, false);
-
-        ManagedClientConnection conn = getConnection(mgr, route);
-
-        Assert.assertEquals("connectionsInPool",
-                     mgr.getConnectionsInPool(), 1);
-        Assert.assertEquals("connectionsInPool(host)",
-                     mgr.getConnectionsInPool(route), 1);
-        mgr.releaseConnection(conn, -1, null);
-
-        Assert.assertEquals("connectionsInPool",
-                     mgr.getConnectionsInPool(), 1);
-        Assert.assertEquals("connectionsInPool(host)",
-                     mgr.getConnectionsInPool(route), 1);
-
-        // this implicitly deletes them
-        mgr.closeIdleConnections(0L, TimeUnit.MILLISECONDS);
-
-        Assert.assertEquals("connectionsInPool",
-                     mgr.getConnectionsInPool(), 0);
-        Assert.assertEquals("connectionsInPool(host)",
-                     mgr.getConnectionsInPool(route), 0);
-
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testShutdown() throws Exception {
-        // 3.x: TestHttpConnectionManager.testShutdown
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(1);
-        mgr.setDefaultMaxPerRoute(1);
-
-        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
-        HttpRoute route = new HttpRoute(target, null, false);
-
-        // get the only connection, then start an extra thread
-        // on shutdown, the extra thread should get an exception
-
-        ManagedClientConnection conn =
-            getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS);
-        GetConnThread gct = new GetConnThread(mgr, route, 0L); // no timeout
-        gct.start();
-        Thread.sleep(100); // give extra thread time to block
-
-
-        mgr.shutdown();
-
-        // First release the connection. If the manager keeps working
-        // despite the shutdown, this will deblock the extra thread.
-        // The release itself should turn into a no-op, without exception.
-        mgr.releaseConnection(conn, -1, null);
-
-
-        gct.join(10000);
-        Assert.assertNull("thread should not have obtained connection",
-                   gct.getConnection());
-        Assert.assertNotNull("thread should have gotten an exception",
-                      gct.getException());
-        Assert.assertSame("thread got wrong exception",
-                   IllegalStateException.class, gct.getException().getClass());
-
-        // the manager is down, we should not be able to get a connection
-        try {
-            getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS);
-            Assert.fail("shut-down manager does not raise exception");
-        } catch (IllegalStateException isx) {
-            // expected
-        }
-    }
-
-    @Test
-    public void testInterruptThread() throws Exception {
-        // 3.x: TestHttpConnectionManager.testWaitingThreadInterrupted
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(1);
-
-        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
-        HttpRoute route = new HttpRoute(target, null, false);
-
-        // get the only connection, then start an extra thread
-        ManagedClientConnection conn =
-            getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS);
-        GetConnThread gct = new GetConnThread(mgr, route, 0L); // no timeout
-        gct.start();
-        Thread.sleep(100); // give extra thread time to block
-
-
-        // interrupt the thread, it should cancel waiting with an exception
-        gct.interrupt();
-
-
-        gct.join(10000);
-        Assert.assertNotNull("thread should have gotten an exception",
-                      gct.getException());
-        Assert.assertSame("thread got wrong exception",
-                   InterruptedException.class,
-                   gct.getException().getClass());
-
-        // make sure the manager is still working
-        try {
-            getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("should have gotten a timeout");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        mgr.releaseConnection(conn, -1, null);
-        // this time: no exception
-        conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
-        Assert.assertNotNull("should have gotten a connection", conn);
-
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testReusePreference() throws Exception {
-        // 3.x: TestHttpConnectionManager.testHostReusePreference
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(1);
-
-        HttpHost target1 = new HttpHost("www.test1.invalid", 80, "http");
-        HttpRoute route1 = new HttpRoute(target1, null, false);
-        HttpHost target2 = new HttpHost("www.test2.invalid", 80, "http");
-        HttpRoute route2 = new HttpRoute(target2, null, false);
-
-        // get the only connection, then start two extra threads
-        ManagedClientConnection conn =
-            getConnection(mgr, route1, 1L, TimeUnit.MILLISECONDS);
-        GetConnThread gct1 = new GetConnThread(mgr, route1, 1000L);
-        GetConnThread gct2 = new GetConnThread(mgr, route2, 1000L);
-
-        // the second thread is started first, to distinguish the
-        // route-based reuse preference from first-come, first-served
-        gct2.start();
-        Thread.sleep(100); // give the thread time to block
-        gct1.start();
-        Thread.sleep(100); // give the thread time to block
-
-
-        // releasing the connection for route1 should deblock thread1
-        // the other thread gets a timeout
-        mgr.releaseConnection(conn, -1, null);
-
-        gct1.join(10000);
-        gct2.join(10000);
-
-        Assert.assertNotNull("thread 1 should have gotten a connection",
-                      gct1.getConnection());
-        Assert.assertNull   ("thread 2 should NOT have gotten a connection",
-                      gct2.getConnection());
-
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testAbortAfterRequestStarts() throws Exception {
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(1);
-
-        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
-        HttpRoute route = new HttpRoute(target, null, false);
-
-        // get the only connection, then start an extra thread
-        ManagedClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS);
-        ClientConnectionRequest request = mgr.requestConnection(route, null);
-        GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout
-        gct.start();
-        Thread.sleep(100); // give extra thread time to block
-
-        request.abortRequest();
-
-        gct.join(10000);
-        Assert.assertNotNull("thread should have gotten an exception",
-                      gct.getException());
-        Assert.assertSame("thread got wrong exception",
-                   InterruptedException.class,
-                   gct.getException().getClass());
-
-        // make sure the manager is still working
-        try {
-            getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("should have gotten a timeout");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        mgr.releaseConnection(conn, -1, null);
-        // this time: no exception
-        conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
-        Assert.assertNotNull("should have gotten a connection", conn);
-
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testAbortBeforeRequestStarts() throws Exception {
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(1);
-
-        HttpHost target = new HttpHost("www.test.invalid", 80, "http");
-        HttpRoute route = new HttpRoute(target, null, false);
-
-        // get the only connection, then start an extra thread
-        ManagedClientConnection conn = getConnection(mgr, route, 1L, TimeUnit.MILLISECONDS);
-        ClientConnectionRequest request = mgr.requestConnection(route, null);
-        request.abortRequest();
-
-        GetConnThread gct = new GetConnThread(request, route, 0L); // no timeout
-        gct.start();
-        Thread.sleep(100); // give extra thread time to block
-
-        gct.join(10000);
-        Assert.assertNotNull("thread should have gotten an exception",
-                      gct.getException());
-        Assert.assertSame("thread got wrong exception",
-                   InterruptedException.class,
-                   gct.getException().getClass());
-
-        // make sure the manager is still working
-        try {
-            getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("should have gotten a timeout");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        mgr.releaseConnection(conn, -1, null);
-        // this time: no exception
-        conn = getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
-        Assert.assertNotNull("should have gotten a connection", conn);
-
-        mgr.shutdown();
-    }
-
-}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/TestTSCCMWithServer.java b/httpclient/src/test/java/org/apache/http/impl/conn/TestTSCCMWithServer.java
deleted file mode 100644
index c3dc6ef..0000000
--- a/httpclient/src/test/java/org/apache/http/impl/conn/TestTSCCMWithServer.java
+++ /dev/null
@@ -1,863 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.http.impl.conn;
-
-import java.io.IOException;
-import java.lang.ref.WeakReference;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketException;
-import java.net.UnknownHostException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.HttpVersion;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.ClientConnectionOperator;
-import org.apache.http.conn.ClientConnectionRequest;
-import org.apache.http.conn.ConnectTimeoutException;
-import org.apache.http.conn.ConnectionPoolTimeoutException;
-import org.apache.http.conn.ManagedClientConnection;
-import org.apache.http.conn.OperatedClientConnection;
-import org.apache.http.conn.routing.HttpRoute;
-import org.apache.http.conn.scheme.PlainSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.conn.scheme.SchemeSocketFactory;
-import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
-import org.apache.http.localserver.ServerTestBase;
-import org.apache.http.message.BasicHttpRequest;
-import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.BasicHttpContext;
-import org.apache.http.protocol.ExecutionContext;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.util.EntityUtils;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Tests for <code>ThreadSafeClientConnManager</code> that do require
- * a server to communicate with.
- */
-public class TestTSCCMWithServer extends ServerTestBase {
-
-    /**
-     * Helper to instantiate a <code>ThreadSafeClientConnManager</code>.
-     *
-     * @param schreg    the scheme registry, or
-     *                  <code>null</code> to use defaults
-     *
-     * @return  a connection manager to test
-     */
-    public ThreadSafeClientConnManager createTSCCM(SchemeRegistry schreg) {
-        return createTSCCM(schreg, -1, TimeUnit.MILLISECONDS);
-    }
-
-    public ThreadSafeClientConnManager createTSCCM(SchemeRegistry schreg,
-            long connTTL, TimeUnit connTTLTimeUnit) {
-        if (schreg == null)
-            schreg = supportedSchemes;
-        return new ThreadSafeClientConnManager(schreg, connTTL, connTTLTimeUnit);
-    }
-
-    /**
-     * Tests executing several requests in parallel.
-     */
-    @Test
-    public void testParallelRequests() throws Exception {
-        // 3.x: TestHttpConnectionManager.testGetFromMultipleThreads
-
-        final int COUNT = 8; // adjust to execute more requests
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(COUNT/2);
-        mgr.setDefaultMaxPerRoute(COUNT/2);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-        final int      rsplen = 8;
-        final String      uri = "/random/" + rsplen;
-
-        ExecReqThread[] threads = new ExecReqThread [COUNT];
-        for (int i=0; i<COUNT; i++) {
-
-            HttpRequest request = new BasicHttpRequest
-                ("GET", uri, HttpVersion.HTTP_1_1);
-
-            ExecReqThread.RequestSpec ertrs = new ExecReqThread.RequestSpec();
-            ertrs.executor = httpExecutor;
-            ertrs.processor = httpProcessor;
-            ertrs.context = new BasicHttpContext(null);
-            ertrs.params = defaultParams;
-
-            ertrs.context.setAttribute
-                (ExecutionContext.HTTP_TARGET_HOST, target);
-            ertrs.context.setAttribute
-                (ExecutionContext.HTTP_REQUEST, request);
-
-            threads[i] = new ExecReqThread(mgr, route, 5000L, ertrs);
-        }
-
-        for (int i=0; i<threads.length; i++) {
-            threads[i].start();
-        }
-
-        for (int i=0; i<threads.length; i++) {
-            threads[i].join(10000);
-            Assert.assertNull("exception in thread " + i,
-                       threads[i].getException());
-            Assert.assertNotNull("no response in thread " + i,
-                          threads[i].getResponse());
-            Assert.assertEquals("wrong status code in thread " + i, 200,
-                         threads[i].getResponse()
-                         .getStatusLine().getStatusCode());
-            Assert.assertNotNull("no response data in thread " + i,
-                          threads[i].getResponseData());
-            Assert.assertEquals("wrong length of data in thread" + i, rsplen,
-                         threads[i].getResponseData().length);
-        }
-
-        mgr.shutdown();
-    }
-
-    private static ManagedClientConnection getConnection(
-            final ClientConnectionManager mgr,
-            final HttpRoute route,
-            long timeout,
-            TimeUnit unit) throws ConnectionPoolTimeoutException, InterruptedException {
-        ClientConnectionRequest connRequest = mgr.requestConnection(route, null);
-        return connRequest.getConnection(timeout, unit);
-    }
-
-    private static ManagedClientConnection getConnection(
-            final ClientConnectionManager mgr,
-            final HttpRoute route) throws ConnectionPoolTimeoutException, InterruptedException {
-        ClientConnectionRequest connRequest = mgr.requestConnection(route, null);
-        return connRequest.getConnection(0, null);
-    }
-
-    /**
-     * Tests releasing and re-using a connection after a response is read.
-     */
-    @Test
-    public void testReleaseConnection() throws Exception {
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(1);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-        final int      rsplen = 8;
-        final String      uri = "/random/" + rsplen;
-
-        HttpRequest request =
-            new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
-
-        ManagedClientConnection conn = getConnection(mgr, route);
-        conn.open(route, httpContext, defaultParams);
-
-        // a new context is created for each testcase, no need to reset
-        HttpResponse response = Helper.execute(
-                request, conn, target,
-                httpExecutor, httpProcessor, defaultParams, httpContext);
-
-        Assert.assertEquals("wrong status in first response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        byte[] data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of first response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        // check that there is no auto-release by default
-        try {
-            // this should fail quickly, connection has not been released
-            getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        // release connection without marking for re-use
-        // expect the next connection obtained to be closed
-        mgr.releaseConnection(conn, -1, null);
-        conn = getConnection(mgr, route);
-        Assert.assertFalse("connection should have been closed", conn.isOpen());
-
-        // repeat the communication, no need to prepare the request again
-        conn.open(route, httpContext, defaultParams);
-        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
-        response = httpExecutor.execute(request, conn, httpContext);
-        httpExecutor.postProcess(response, httpProcessor, httpContext);
-
-        Assert.assertEquals("wrong status in second response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of second response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        // release connection after marking it for re-use
-        // expect the next connection obtained to be open
-        conn.markReusable();
-        mgr.releaseConnection(conn, -1, null);
-        conn = getConnection(mgr, route);
-        Assert.assertTrue("connection should have been open", conn.isOpen());
-
-        // repeat the communication, no need to prepare the request again
-        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
-        response = httpExecutor.execute(request, conn, httpContext);
-        httpExecutor.postProcess(response, httpProcessor, httpContext);
-
-        Assert.assertEquals("wrong status in third response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of third response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        mgr.releaseConnection(conn, -1, null);
-        mgr.shutdown();
-    }
-
-    /**
-     * Tests releasing with time limits.
-     */
-    @Test
-    public void testReleaseConnectionWithTimeLimits() throws Exception {
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(1);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-        final int      rsplen = 8;
-        final String      uri = "/random/" + rsplen;
-
-        HttpRequest request =
-            new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
-
-        ManagedClientConnection conn = getConnection(mgr, route);
-        conn.open(route, httpContext, defaultParams);
-
-        // a new context is created for each testcase, no need to reset
-        HttpResponse response = Helper.execute(
-                request, conn, target,
-                httpExecutor, httpProcessor, defaultParams, httpContext);
-
-        Assert.assertEquals("wrong status in first response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        byte[] data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of first response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        // check that there is no auto-release by default
-        try {
-            // this should fail quickly, connection has not been released
-            getConnection(mgr, route, 10L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        // release connection without marking for re-use
-        // expect the next connection obtained to be closed
-        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
-        conn = getConnection(mgr, route);
-        Assert.assertFalse("connection should have been closed", conn.isOpen());
-
-        // repeat the communication, no need to prepare the request again
-        conn.open(route, httpContext, defaultParams);
-        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
-        response = httpExecutor.execute(request, conn, httpContext);
-        httpExecutor.postProcess(response, httpProcessor, httpContext);
-
-        Assert.assertEquals("wrong status in second response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of second response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        // release connection after marking it for re-use
-        // expect the next connection obtained to be open
-        conn.markReusable();
-        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
-        conn = getConnection(mgr, route);
-        Assert.assertTrue("connection should have been open", conn.isOpen());
-
-        // repeat the communication, no need to prepare the request again
-        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
-        response = httpExecutor.execute(request, conn, httpContext);
-        httpExecutor.postProcess(response, httpProcessor, httpContext);
-
-        Assert.assertEquals("wrong status in third response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of third response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        conn.markReusable();
-        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
-        Thread.sleep(150);
-        conn = getConnection(mgr, route);
-        Assert.assertTrue("connection should have been closed", !conn.isOpen());
-
-        // repeat the communication, no need to prepare the request again
-        conn.open(route, httpContext, defaultParams);
-        httpContext.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
-        response = httpExecutor.execute(request, conn, httpContext);
-        httpExecutor.postProcess(response, httpProcessor, httpContext);
-
-        Assert.assertEquals("wrong status in third response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-        data = EntityUtils.toByteArray(response.getEntity());
-        Assert.assertEquals("wrong length of fourth response entity",
-                     rsplen, data.length);
-        // ignore data, but it must be read
-
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testCloseExpiredIdleConnections() throws Exception {
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(1);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-
-        ManagedClientConnection conn = getConnection(mgr, route);
-        conn.open(route, httpContext, defaultParams);
-
-        Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
-        Assert.assertEquals("connectionsInPool(host)", 1, mgr.getConnectionsInPool(route));
-        mgr.releaseConnection(conn, 100, TimeUnit.MILLISECONDS);
-
-        // Released, still active.
-        Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
-        Assert.assertEquals("connectionsInPool(host)", 1,  mgr.getConnectionsInPool(route));
-
-        mgr.closeExpiredConnections();
-
-        // Time has not expired yet.
-        Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
-        Assert.assertEquals("connectionsInPool(host)", 1,  mgr.getConnectionsInPool(route));
-
-        Thread.sleep(150);
-
-        mgr.closeExpiredConnections();
-
-        // Time expired now, connections are destroyed.
-        Assert.assertEquals("connectionsInPool", 0, mgr.getConnectionsInPool());
-        Assert.assertEquals("connectionsInPool(host)", 0, mgr.getConnectionsInPool(route));
-
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testCloseExpiredTTLConnections() throws Exception {
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null, 100, TimeUnit.MILLISECONDS);
-        mgr.setMaxTotal(1);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-
-        ManagedClientConnection conn = getConnection(mgr, route);
-        conn.open(route, httpContext, defaultParams);
-
-        Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
-        Assert.assertEquals("connectionsInPool(host)", 1, mgr.getConnectionsInPool(route));
-        // Release, let remain idle for forever
-        mgr.releaseConnection(conn, -1, TimeUnit.MILLISECONDS);
-
-        // Released, still active.
-        Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
-        Assert.assertEquals("connectionsInPool(host)", 1,  mgr.getConnectionsInPool(route));
-
-        mgr.closeExpiredConnections();
-
-        // Time has not expired yet.
-        Assert.assertEquals("connectionsInPool", 1, mgr.getConnectionsInPool());
-        Assert.assertEquals("connectionsInPool(host)", 1,  mgr.getConnectionsInPool(route));
-
-        Thread.sleep(150);
-
-        mgr.closeExpiredConnections();
-
-        // TTL expired now, connections are destroyed.
-        Assert.assertEquals("connectionsInPool", 0, mgr.getConnectionsInPool());
-        Assert.assertEquals("connectionsInPool(host)", 0, mgr.getConnectionsInPool(route));
-
-        mgr.shutdown();
-    }
-
-    /**
-     * Tests releasing connection from #abort method called from the
-     * main execution thread while there is no blocking I/O operation.
-     */
-    @Test
-    public void testReleaseConnectionOnAbort() throws Exception {
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-        mgr.setMaxTotal(1);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-        final int      rsplen = 8;
-        final String      uri = "/random/" + rsplen;
-
-        HttpRequest request =
-            new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
-
-        ManagedClientConnection conn = getConnection(mgr, route);
-        conn.open(route, httpContext, defaultParams);
-
-        // a new context is created for each testcase, no need to reset
-        HttpResponse response = Helper.execute(
-                request, conn, target,
-                httpExecutor, httpProcessor, defaultParams, httpContext);
-
-        Assert.assertEquals("wrong status in first response",
-                     HttpStatus.SC_OK,
-                     response.getStatusLine().getStatusCode());
-
-        // check that there are no connections available
-        try {
-            // this should fail quickly, connection has not been released
-            getConnection(mgr, route, 100L, TimeUnit.MILLISECONDS);
-            Assert.fail("ConnectionPoolTimeoutException should have been thrown");
-        } catch (ConnectionPoolTimeoutException e) {
-            // expected
-        }
-
-        // abort the connection
-        Assert.assertTrue(conn instanceof AbstractClientConnAdapter);
-        ((AbstractClientConnAdapter) conn).abortConnection();
-
-        // the connection is expected to be released back to the manager
-        conn = getConnection(mgr, route, 5L, TimeUnit.SECONDS);
-        Assert.assertFalse("connection should have been closed", conn.isOpen());
-
-        mgr.releaseConnection(conn, -1, null);
-        mgr.shutdown();
-    }
-
-    /**
-     * Tests GC of an unreferenced connection manager.
-     */
-    @Test
-    public void testConnectionManagerGC() throws Exception {
-        // 3.x: TestHttpConnectionManager.testDroppedThread
-
-        ThreadSafeClientConnManager mgr = createTSCCM(null);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-        final int      rsplen = 8;
-        final String      uri = "/random/" + rsplen;
-
-        HttpRequest request =
-            new BasicHttpRequest("GET", uri, HttpVersion.HTTP_1_1);
-
-        ManagedClientConnection conn = getConnection(mgr, route);
-        conn.open(route, httpContext, defaultParams);
-
-        // a new context is created for each testcase, no need to reset
-        HttpResponse response = Helper.execute(request, conn, target,
-                httpExecutor, httpProcessor, defaultParams, httpContext);
-        EntityUtils.toByteArray(response.getEntity());
-
-        // release connection after marking it for re-use
-        conn.markReusable();
-        mgr.releaseConnection(conn, -1, null);
-
-        // We now have a manager with an open connection in its pool.
-        // We drop all potential hard reference to the manager and check
-        // whether it is GCed. Internal references might prevent that
-        // if set up incorrectly.
-        // Note that we still keep references to the connection wrapper
-        // we got from the manager, directly as well as in the request
-        // and in the context. The manager will be GCed only if the
-        // connection wrapper is truly detached.
-        WeakReference<ThreadSafeClientConnManager> wref =
-            new WeakReference<ThreadSafeClientConnManager>(mgr);
-        mgr = null;
-
-        // Java does not guarantee that this will trigger the GC, but
-        // it does in the test environment. GC is asynchronous, so we
-        // need to give the garbage collector some time afterwards.
-        System.gc();
-        Thread.sleep(1000);
-
-        Assert.assertNull("TSCCM not garbage collected", wref.get());
-    }
-
-    @Test
-    public void testAbortDuringConnecting() throws Exception {
-        final CountDownLatch connectLatch = new CountDownLatch(1);
-        final StallingSocketFactory stallingSocketFactory = new StallingSocketFactory(
-                connectLatch, WaitPolicy.BEFORE_CONNECT, PlainSocketFactory.getSocketFactory());
-        Scheme scheme = new Scheme("http", 80, stallingSocketFactory);
-        SchemeRegistry registry = new SchemeRegistry();
-        registry.register(scheme);
-
-        ThreadSafeClientConnManager mgr = createTSCCM(registry);
-        mgr.setMaxTotal(1);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-
-        final ManagedClientConnection conn = getConnection(mgr, route);
-        Assert.assertTrue(conn instanceof AbstractClientConnAdapter);
-
-        final AtomicReference<Throwable> throwRef = new AtomicReference<Throwable>();
-        Thread abortingThread = new Thread(new Runnable() {
-            public void run() {
-                try {
-                    stallingSocketFactory.waitForState();
-                    conn.abortConnection();
-                    connectLatch.countDown();
-                } catch (Throwable e) {
-                    throwRef.set(e);
-                }
-            }
-        });
-        abortingThread.start();
-
-        try {
-            conn.open(route, httpContext, defaultParams);
-            Assert.fail("expected SocketException");
-        } catch(SocketException expected) {}
-
-        abortingThread.join(5000);
-        if(throwRef.get() != null)
-            throw new RuntimeException(throwRef.get());
-
-        Assert.assertFalse(conn.isOpen());
-        Assert.assertEquals(0, localServer.getAcceptedConnectionCount());
-
-        // the connection is expected to be released back to the manager
-        ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS);
-        Assert.assertFalse("connection should have been closed", conn2.isOpen());
-
-        mgr.releaseConnection(conn2, -1, null);
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testAbortBeforeSocketCreate() throws Exception {
-        final CountDownLatch connectLatch = new CountDownLatch(1);
-        final StallingSocketFactory stallingSocketFactory = new StallingSocketFactory(
-                connectLatch, WaitPolicy.BEFORE_CREATE, PlainSocketFactory.getSocketFactory());
-        Scheme scheme = new Scheme("http", 80, stallingSocketFactory);
-        SchemeRegistry registry = new SchemeRegistry();
-        registry.register(scheme);
-
-        ThreadSafeClientConnManager mgr = createTSCCM(registry);
-        mgr.setMaxTotal(1);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-
-        final ManagedClientConnection conn = getConnection(mgr, route);
-        Assert.assertTrue(conn instanceof AbstractClientConnAdapter);
-
-        final AtomicReference<Throwable> throwRef = new AtomicReference<Throwable>();
-        Thread abortingThread = new Thread(new Runnable() {
-            public void run() {
-                try {
-                    stallingSocketFactory.waitForState();
-                    conn.abortConnection();
-                    connectLatch.countDown();
-                } catch (Throwable e) {
-                    throwRef.set(e);
-                }
-            }
-        });
-        abortingThread.start();
-
-        try {
-            conn.open(route, httpContext, defaultParams);
-            Assert.fail("expected exception");
-        } catch(IOException expected) {
-            Assert.assertEquals("Connection already shutdown", expected.getMessage());
-        }
-
-        abortingThread.join(5000);
-        if(throwRef.get() != null)
-            throw new RuntimeException(throwRef.get());
-
-        Assert.assertFalse(conn.isOpen());
-        Assert.assertEquals(0, localServer.getAcceptedConnectionCount());
-
-        // the connection is expected to be released back to the manager
-        ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS);
-        Assert.assertFalse("connection should have been closed", conn2.isOpen());
-
-        mgr.releaseConnection(conn2, -1, null);
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testAbortAfterSocketConnect() throws Exception {
-        final CountDownLatch connectLatch = new CountDownLatch(1);
-        final StallingSocketFactory stallingSocketFactory = new StallingSocketFactory(
-                connectLatch, WaitPolicy.AFTER_CONNECT, PlainSocketFactory.getSocketFactory());
-        Scheme scheme = new Scheme("http", 80, stallingSocketFactory);
-        SchemeRegistry registry = new SchemeRegistry();
-        registry.register(scheme);
-
-        ThreadSafeClientConnManager mgr = createTSCCM(registry);
-        mgr.setMaxTotal(1);
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-
-        final ManagedClientConnection conn = getConnection(mgr, route);
-        Assert.assertTrue(conn instanceof AbstractClientConnAdapter);
-
-        final AtomicReference<Throwable> throwRef = new AtomicReference<Throwable>();
-        Thread abortingThread = new Thread(new Runnable() {
-            public void run() {
-                try {
-                    stallingSocketFactory.waitForState();
-                    conn.abortConnection();
-                    connectLatch.countDown();
-                } catch (Throwable e) {
-                    throwRef.set(e);
-                }
-            }
-        });
-        abortingThread.start();
-
-        try {
-            conn.open(route, httpContext, defaultParams);
-            Assert.fail("expected SocketException");
-        } catch(SocketException expected) {}
-
-        abortingThread.join(5000);
-        if(throwRef.get() != null)
-            throw new RuntimeException(throwRef.get());
-
-        Assert.assertFalse(conn.isOpen());
-        // Give the server a bit of time to accept the connection, but
-        // ensure that it can accept it.
-        for(int i = 0; i < 10; i++) {
-            if(localServer.getAcceptedConnectionCount() == 1)
-                break;
-            Thread.sleep(100);
-        }
-        Assert.assertEquals(1, localServer.getAcceptedConnectionCount());
-
-        // the connection is expected to be released back to the manager
-        ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS);
-        Assert.assertFalse("connection should have been closed", conn2.isOpen());
-
-        mgr.releaseConnection(conn2, -1, null);
-        mgr.shutdown();
-    }
-
-    @Test
-    public void testAbortAfterOperatorOpen() throws Exception {
-        final CountDownLatch connectLatch = new CountDownLatch(1);
-        final AtomicReference<StallingOperator> operatorRef = new AtomicReference<StallingOperator>();
-
-        ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(supportedSchemes) {
-            @Override
-            protected ClientConnectionOperator createConnectionOperator(
-                    SchemeRegistry schreg) {
-                operatorRef.set(new StallingOperator(connectLatch, WaitPolicy.AFTER_OPEN, super.createConnectionOperator(schreg)));
-                return operatorRef.get();
-            }
-        };
-        mgr.setMaxTotal(1);
-        Assert.assertNotNull(operatorRef.get());
-
-        final HttpHost target = getServerHttp();
-        final HttpRoute route = new HttpRoute(target, null, false);
-
-        final ManagedClientConnection conn = getConnection(mgr, route);
-        Assert.assertTrue(conn instanceof AbstractClientConnAdapter);
-
-        final AtomicReference<Throwable> throwRef = new AtomicReference<Throwable>();
-        Thread abortingThread = new Thread(new Runnable() {
-            public void run() {
-                try {
-                    operatorRef.get().waitForState();
-                    conn.abortConnection();
-                    connectLatch.countDown();
-                } catch (Throwable e) {
-                    throwRef.set(e);
-                }
-            }
-        });
-        abortingThread.start();
-
-        try {
-            conn.open(route, httpContext, defaultParams);
-            Assert.fail("expected exception");
-        } catch(IOException iox) {
-            Assert.assertEquals("Request aborted", iox.getMessage());
-        }
-
-        abortingThread.join(5000);
-        if(throwRef.get() != null)
-            throw new RuntimeException(throwRef.get());
-
-        Assert.assertFalse(conn.isOpen());
-        // Give the server a bit of time to accept the connection, but
-        // ensure that it can accept it.
-        for(int i = 0; i < 10; i++) {
-            if(localServer.getAcceptedConnectionCount() == 1)
-                break;
-            Thread.sleep(100);
-        }
-        Assert.assertEquals(1, localServer.getAcceptedConnectionCount());
-
-        // the connection is expected to be released back to the manager
-        ManagedClientConnection conn2 = getConnection(mgr, route, 5L, TimeUnit.SECONDS);
-        Assert.assertFalse("connection should have been closed", conn2.isOpen());
-
-        mgr.releaseConnection(conn2, -1, null);
-        mgr.shutdown();
-    }
-
-    private static class LatchSupport {
-        private final CountDownLatch continueLatch;
-        private final CountDownLatch waitLatch = new CountDownLatch(1);
-        protected final WaitPolicy waitPolicy;
-
-        LatchSupport(CountDownLatch continueLatch, WaitPolicy waitPolicy) {
-            this.continueLatch = continueLatch;
-            this.waitPolicy = waitPolicy;
-        }
-
-        void waitForState() throws InterruptedException {
-            if(!waitLatch.await(1, TimeUnit.SECONDS))
-                throw new RuntimeException("waited too long");
-        }
-
-        void latch() {
-            waitLatch.countDown();
-            try {
-                if (!continueLatch.await(1, TimeUnit.SECONDS))
-                    throw new RuntimeException("waited too long!");
-            } catch (InterruptedException e) {
-                throw new RuntimeException(e);
-            }
-        }
-    }
-
-    private static class StallingOperator extends LatchSupport implements ClientConnectionOperator {
-        private final ClientConnectionOperator delegate;
-
-        public StallingOperator(CountDownLatch continueLatch,
-                WaitPolicy waitPolicy, ClientConnectionOperator delegate) {
-            super(continueLatch, waitPolicy);
-            this.delegate = delegate;
-        }
-
-        public OperatedClientConnection createConnection() {
-            return delegate.createConnection();
-        }
-
-        public void openConnection(OperatedClientConnection conn,
-                HttpHost target, InetAddress local, HttpContext context,
-                HttpParams params) throws IOException {
-            delegate.openConnection(conn, target, local, context, params);
-            if(waitPolicy == WaitPolicy.AFTER_OPEN)
-                latch();
-        }
-
-        public void updateSecureConnection(OperatedClientConnection conn,
-                HttpHost target, HttpContext context, HttpParams params)
-                throws IOException {
-            delegate.updateSecureConnection(conn, target, context, params);
-        }
-    }
-
-    private static class StallingSocketFactory extends LatchSupport implements SchemeSocketFactory {
-
-        private final SchemeSocketFactory delegate;
-
-        public StallingSocketFactory(
-                final CountDownLatch continueLatch,
-                final WaitPolicy waitPolicy,
-                final SchemeSocketFactory delegate) {
-            super(continueLatch, waitPolicy);
-            this.delegate = delegate;
-        }
-
-        public Socket connectSocket(
-                final Socket sock,
-                final InetSocketAddress remoteAddress,
-                final InetSocketAddress localAddress,
-                final HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {
-            if(waitPolicy == WaitPolicy.BEFORE_CONNECT)
-                latch();
-
-            Socket socket = delegate.connectSocket(sock, remoteAddress, localAddress, params);
-
-            if(waitPolicy == WaitPolicy.AFTER_CONNECT)
-                latch();
-
-            return socket;
-        }
-
-        public Socket createSocket(final HttpParams params) throws IOException {
-            if(waitPolicy == WaitPolicy.BEFORE_CREATE)
-                latch();
-
-            return delegate.createSocket(params);
-        }
-
-        public boolean isSecure(Socket sock) throws IllegalArgumentException {
-            return delegate.isSecure(sock);
-        }
-    }
-
-    private enum WaitPolicy { BEFORE_CREATE, BEFORE_CONNECT, AFTER_CONNECT, AFTER_OPEN }
-
-}
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/AwaitThread.java b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/AwaitThread.java
index 02ca6a7..a6dd17d 100644
--- a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/AwaitThread.java
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/AwaitThread.java
@@ -35,6 +35,7 @@ import java.util.concurrent.locks.Lock;
 /**
  * Thread to await something.
  */
+ at Deprecated 
 public class AwaitThread extends Thread {
 
     protected final WaitingThread wait_object;
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestConnPoolByRoute.java b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestConnPoolByRoute.java
index 1dee2ab..133038c 100644
--- a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestConnPoolByRoute.java
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestConnPoolByRoute.java
@@ -27,20 +27,57 @@
 
 package org.apache.http.impl.conn.tsccm;
 
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.util.Random;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.http.HttpHost;
 import org.apache.http.conn.ClientConnectionOperator;
 import org.apache.http.conn.ConnectionPoolTimeoutException;
+import org.apache.http.conn.OperatedClientConnection;
 import org.apache.http.conn.params.ConnPerRouteBean;
 import org.apache.http.conn.routing.HttpRoute;
 import org.apache.http.impl.conn.DefaultClientConnectionOperator;
 import org.apache.http.localserver.ServerTestBase;
+import org.apache.http.params.BasicHttpParams;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
 
+ at SuppressWarnings("deprecation")
+ at RunWith(MockitoJUnitRunner.class)
+ at Deprecated
 public class TestConnPoolByRoute extends ServerTestBase {
 
+    private ConnPoolByRoute impl;
+    private HttpRoute route = new HttpRoute(new HttpHost("localhost"));
+    private HttpRoute route2 = new HttpRoute(new HttpHost("localhost:8080"));
+
+    @Mock private OperatedClientConnection mockConnection;
+    @Mock private OperatedClientConnection mockConnection2;
+    @Mock private ClientConnectionOperator mockOperator;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        impl = new ConnPoolByRoute(
+                new DefaultClientConnectionOperator(supportedSchemes),
+                new ConnPerRouteBean(), 1, -1, TimeUnit.MILLISECONDS);
+    }
+
+    private void useMockOperator() {
+        reset(mockOperator);
+        impl = new ConnPoolByRoute(
+                mockOperator, new ConnPerRouteBean(), 1, -1, TimeUnit.MILLISECONDS);
+        when(mockOperator.createConnection()).thenReturn(mockConnection);
+    }
+
     @Test
     public void testStatelessConnections() throws Exception {
         final HttpHost target = getServerHttp();
@@ -154,4 +191,265 @@ public class TestConnPoolByRoute extends ServerTestBase {
         }
     }
 
+    @Test(expected=IllegalArgumentException.class)
+    public void nullOperatorIsNotAllowed() {
+        new ConnPoolByRoute(null, new ConnPerRouteBean(), 1, -1, TimeUnit.MILLISECONDS);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void nullConnPerRouteIsNotAllowed() {
+        new ConnPoolByRoute(new DefaultClientConnectionOperator(supportedSchemes),
+                null, 1, -1, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void deprecatedConstructorIsStillSupported() {
+        new ConnPoolByRoute(new DefaultClientConnectionOperator(supportedSchemes),
+                new BasicHttpParams());
+    }
+
+    @Test
+    public void emptyPoolHasNoConnections() {
+        assertEquals(0, impl.getConnectionsInPool());
+    }
+
+    @Test
+    public void poolHasOneConnectionAfterRequestingOne() throws Exception {
+        useMockOperator();
+        impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        assertEquals(1, impl.getConnectionsInPool());
+    }
+
+    @Test
+    public void emptyPoolHasNoRouteSpecificConnections() {
+        assertEquals(0, impl.getConnectionsInPool(route));
+    }
+
+    @Test
+    public void routeSpecificPoolHasOneConnectionAfterRequestingOne() throws Exception {
+        useMockOperator();
+        impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        assertEquals(1, impl.getConnectionsInPool(route));
+    }
+
+    @Test
+    public void abortingPoolEntryRequestEarlyDoesNotCreateConnection() {
+        PoolEntryRequest req = impl.requestPoolEntry(route, new Object());
+        req.abortRequest();
+        assertEquals(0, impl.getConnectionsInPool(route));
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void cannotAcquireConnectionIfPoolShutdown() throws Exception {
+        impl.shutdown();
+        impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void multipleShutdownsAreOk() {
+        impl.shutdown();
+        impl.shutdown();
+    }
+
+    @Test
+    public void canAcquirePoolEntry() throws Exception {
+        impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void canRetrieveMaxTotalConnections() {
+        int max = (new Random()).nextInt(10) + 2;
+        impl.setMaxTotalConnections(max);
+        assertEquals(max, impl.getMaxTotalConnections());
+    }
+
+    @Test
+    public void closesFreedConnectionsWhenShutdown() throws Exception {
+        useMockOperator();
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        impl.shutdown();
+        impl.freeEntry(entry, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
+        verify(mockConnection, atLeastOnce()).close();
+    }
+
+    @Test
+    public void deleteClosedConnectionsReclaimsPoolSpace() throws Exception {
+        useMockOperator();
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        impl.freeEntry(entry, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
+        assertFalse(impl.freeConnections.isEmpty());
+        when(mockConnection.isOpen()).thenReturn(false);
+        impl.deleteClosedConnections();
+        assertTrue(impl.freeConnections.isEmpty());
+        assertEquals(0, impl.numConnections);
+    }
+
+    @Test
+    public void deleteClosedConnectionsDoesNotReclaimOpenConnections() throws Exception {
+        useMockOperator();
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        impl.freeEntry(entry, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
+        assertFalse(impl.freeConnections.isEmpty());
+        when(mockConnection.isOpen()).thenReturn(true);
+        impl.deleteClosedConnections();
+        assertFalse(impl.freeConnections.isEmpty());
+        assertEquals(1, impl.numConnections);
+    }
+
+    @Test
+    public void closeIdleConnectionsClosesThoseThatHaveTimedOut() throws Exception {
+        useMockOperator();
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        impl.freeEntry(entry, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
+        Thread.sleep(200L);
+        impl.closeIdleConnections(1, TimeUnit.MILLISECONDS);
+        verify(mockConnection, atLeastOnce()).close();
+    }
+
+    @Test
+    public void closeIdleConnectionsDoesNotCloseThoseThatHaveNotTimedOut() throws Exception {
+        useMockOperator();
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        impl.freeEntry(entry, true, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
+        impl.closeIdleConnections(3, TimeUnit.SECONDS);
+        verify(mockConnection, never()).close();
+    }
+
+    @Test
+    public void closeExpiredConnectionsClosesExpiredOnes() throws Exception {
+        useMockOperator();
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        impl.freeEntry(entry, true, 1, TimeUnit.MILLISECONDS);
+        Thread.sleep(200L);
+        impl.closeExpiredConnections();
+        verify(mockConnection, atLeastOnce()).close();
+    }
+
+    @Test
+    public void closeExpiredConnectionsDoesNotCloseUnexpiredOnes() throws Exception {
+        useMockOperator();
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        impl.freeEntry(entry, true, 10, TimeUnit.SECONDS);
+        Thread.sleep(200L);
+        impl.closeExpiredConnections();
+        verify(mockConnection, never()).close();
+    }
+
+    @Test
+    public void closesNonReusableConnections() throws Exception {
+        useMockOperator();
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        impl.freeEntry(entry, false, 0, TimeUnit.MILLISECONDS);
+        verify(mockConnection, atLeastOnce()).close();
+    }
+
+    @Test
+    public void handlesExceptionsWhenClosingConnections() throws Exception {
+        useMockOperator();
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        doThrow(new IOException()).when(mockConnection).close();
+        impl.freeEntry(entry, false, 0, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void wakesUpWaitingThreadsWhenEntryAvailable() throws Exception {
+        useMockOperator();
+        when(mockOperator.createConnection()).thenReturn(mockConnection);
+        impl.setMaxTotalConnections(1);
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        final Flag f = new Flag(false);
+        Thread t = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+                    f.flag = true;
+                } catch (ConnectionPoolTimeoutException e) {
+                } catch (InterruptedException e) {
+                }
+            }
+        });
+        t.start();
+        Thread.sleep(100);
+        impl.freeEntry(entry, true, 1000, TimeUnit.MILLISECONDS);
+        Thread.sleep(100);
+        assertTrue(f.flag);
+    }
+
+    @Test
+    public void wakesUpWaitingThreadsOnOtherRoutesWhenEntryAvailable() throws Exception {
+        useMockOperator();
+        when(mockOperator.createConnection()).thenReturn(mockConnection);
+        impl.setMaxTotalConnections(1);
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        final Flag f = new Flag(false);
+        Thread t = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    impl.requestPoolEntry(route2, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+                    f.flag = true;
+                } catch (ConnectionPoolTimeoutException e) {
+                } catch (InterruptedException e) {
+                }
+            }
+        });
+        t.start();
+        Thread.sleep(100);
+        impl.freeEntry(entry, true, 1000, TimeUnit.MILLISECONDS);
+        Thread.sleep(100);
+        assertTrue(f.flag);
+    }
+
+    @Test
+    public void doesNotRecycleExpiredConnections() throws Exception {
+        useMockOperator();
+        when(mockOperator.createConnection()).thenReturn(mockConnection, mockConnection2);
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        impl.freeEntry(entry, true, 1, TimeUnit.MILLISECONDS);
+        Thread.sleep(200L);
+        BasicPoolEntry entry2 = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        assertNotSame(mockConnection, entry2.getConnection());
+    }
+
+    @Test
+    public void closesExpiredConnectionsWhenNotReusingThem() throws Exception {
+        useMockOperator();
+        when(mockOperator.createConnection()).thenReturn(mockConnection, mockConnection2);
+        BasicPoolEntry entry = impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        impl.freeEntry(entry, true, 1, TimeUnit.MILLISECONDS);
+        Thread.sleep(200L);
+        impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        verify(mockConnection, atLeastOnce()).close();
+    }
+
+
+    @Test
+    public void wakesUpWaitingThreadsOnShutdown() throws Exception {
+        useMockOperator();
+        when(mockOperator.createConnection()).thenReturn(mockConnection);
+        when(mockOperator.createConnection()).thenReturn(mockConnection);
+        impl.setMaxTotalConnections(1);
+        impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+        final Flag f = new Flag(false);
+        Thread t = new Thread(new Runnable() {
+            public void run() {
+                try {
+                    impl.requestPoolEntry(route, new Object()).getPoolEntry(-1, TimeUnit.MILLISECONDS);
+                } catch (IllegalStateException expected) {
+                    f.flag = true;
+                } catch (ConnectionPoolTimeoutException e) {
+                } catch (InterruptedException e) {
+                }
+            }
+        });
+        t.start();
+        Thread.sleep(1);
+        impl.shutdown();
+        Thread.sleep(1);
+        assertTrue(f.flag);
+    }
+
+    private static class Flag {
+        public boolean flag;
+        public Flag(boolean init) { flag = init; }
+    }
 }
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java
index 5b4644a..c744fc6 100644
--- a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestSpuriousWakeup.java
@@ -52,6 +52,7 @@ import org.junit.Test;
  * satisfying the condition.
  *
  */
+ at Deprecated 
 public class TestSpuriousWakeup {
 
     public final static
diff --git a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestWaitingThread.java b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestWaitingThread.java
index 79fefe1..8d03172 100644
--- a/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestWaitingThread.java
+++ b/httpclient/src/test/java/org/apache/http/impl/conn/tsccm/TestWaitingThread.java
@@ -41,6 +41,7 @@ import org.junit.Test;
 /**
  * Tests for <code>WaitingThread</code>.
  */
+ at Deprecated
 public class TestWaitingThread {
 
     public final static
diff --git a/httpclient/src/test/java/org/apache/http/localserver/BasicServerTestBase.java b/httpclient/src/test/java/org/apache/http/localserver/BasicServerTestBase.java
index d4b60df..333af92 100644
--- a/httpclient/src/test/java/org/apache/http/localserver/BasicServerTestBase.java
+++ b/httpclient/src/test/java/org/apache/http/localserver/BasicServerTestBase.java
@@ -30,19 +30,29 @@ package org.apache.http.localserver;
 import java.net.InetSocketAddress;
 
 import org.apache.http.HttpHost;
+import org.apache.http.impl.client.DefaultHttpClient;
 import org.junit.After;
+import org.mockito.Mockito;
 
 /**
  * Base class for tests using {@link LocalTestServer}. The server will not be started
  * per default.
  */
-public abstract class BasicServerTestBase {
+public abstract class BasicServerTestBase extends Mockito {
 
     /** The local server for testing. */
     protected LocalTestServer localServer;
+    protected DefaultHttpClient httpclient;
 
     @After
-    public void tearDown() throws Exception {
+    public void shutDownClient() throws Exception {
+        if (httpclient != null) {
+            httpclient.getConnectionManager().shutdown();
+        }
+    }
+
+    @After
+    public void shutDownServer() throws Exception {
         if (localServer != null) {
             localServer.stop();
         }
diff --git a/httpclient/src/test/java/org/apache/http/mockup/SecureSocketFactoryMockup.java b/httpclient/src/test/java/org/apache/http/mockup/SecureSocketFactoryMockup.java
index 1756865..85d01e4 100644
--- a/httpclient/src/test/java/org/apache/http/mockup/SecureSocketFactoryMockup.java
+++ b/httpclient/src/test/java/org/apache/http/mockup/SecureSocketFactoryMockup.java
@@ -29,16 +29,17 @@ package org.apache.http.mockup;
 
 import java.net.Socket;
 
-import org.apache.http.conn.scheme.LayeredSchemeSocketFactory;
+import org.apache.http.conn.scheme.SchemeLayeredSocketFactory;
+import org.apache.http.params.HttpParams;
 
 /**
  * {@link LayeredSchemeSocketFactory} mockup implementation.
  */
 public class SecureSocketFactoryMockup extends SocketFactoryMockup
-    implements LayeredSchemeSocketFactory {
+    implements SchemeLayeredSocketFactory {
 
     /* A default instance of this mockup. */
-    public final static LayeredSchemeSocketFactory INSTANCE = new SecureSocketFactoryMockup("INSTANCE");
+    public final static SchemeLayeredSocketFactory INSTANCE = new SecureSocketFactoryMockup("INSTANCE");
 
     public SecureSocketFactoryMockup(String name) {
         super(name);
@@ -53,7 +54,7 @@ public class SecureSocketFactoryMockup extends SocketFactoryMockup
 
 
     public Socket createLayeredSocket(Socket socket, String host, int port,
-                                      boolean autoClose) {
+                                      HttpParams params) {
         throw new UnsupportedOperationException("I'm a mockup!");
     }
 
diff --git a/httpclient-cache/src/test/resources/commons-logging.properties b/httpclient/src/test/resources/commons-logging.properties
similarity index 100%
copy from httpclient-cache/src/test/resources/commons-logging.properties
copy to httpclient/src/test/resources/commons-logging.properties
diff --git a/httpclient/src/test/resources/test.keystore b/httpclient/src/test/resources/test.keystore
index 97a862b..917f83e 100644
Binary files a/httpclient/src/test/resources/test.keystore and b/httpclient/src/test/resources/test.keystore differ
diff --git a/httpmime/pom.xml b/httpmime/pom.xml
index 8853cc2..ab5998a 100644
--- a/httpmime/pom.xml
+++ b/httpmime/pom.xml
@@ -30,7 +30,7 @@
   <parent>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpcomponents-client</artifactId>
-    <version>4.1.1</version>
+    <version>4.2.1</version>
   </parent>
   <artifactId>httpmime</artifactId>
   <name>HttpMime</name>
@@ -44,19 +44,18 @@
     <dependency>
       <groupId>org.apache.httpcomponents</groupId>
       <artifactId>httpcore</artifactId>
-      <version>${httpcore.version}</version>
       <scope>compile</scope>
     </dependency>
+    <!-- Not currently used
     <dependency>
       <groupId>commons-logging</groupId>
       <artifactId>commons-logging</artifactId>
-      <version>${commons-logging.version}</version>
       <scope>compile</scope>
     </dependency>
+    -->
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
-      <version>${junit.version}</version>
       <scope>test</scope>
     </dependency>
   </dependencies>
@@ -120,6 +119,7 @@
 
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
+         <version>${hc.javadoc.version}</version>
         <configuration>
           <!-- reduce console output. Can override with -Dquiet=false -->
           <quiet>true</quiet>
@@ -149,10 +149,12 @@
 
       <plugin>
         <artifactId>maven-jxr-plugin</artifactId>
+         <version>${hc.jxr.version}</version>
       </plugin>
 
       <plugin>
         <artifactId>maven-surefire-report-plugin</artifactId>
+         <version>${hc.surefire-report.version}</version>
       </plugin>
 
     </plugins>
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipart.java b/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipart.java
index c42a261..5d9301b 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipart.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipart.java
@@ -103,7 +103,7 @@ public class HttpMultipart {
 
     /**
      * Creates an instance with the specified settings.
-     * 
+     *
      * @param subType mime subtype - must not be {@code null}
      * @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
      * @param boundary to use  - must not be {@code null}
@@ -128,7 +128,7 @@ public class HttpMultipart {
     /**
      * Creates an instance with the specified settings.
      * Mode is set to {@link HttpMultipartMode#STRICT}
-     * 
+     *
      * @param subType mime subtype - must not be {@code null}
      * @param charset the character set to use. May be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
      * @param boundary to use  - must not be {@code null}
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/content/FileBody.java b/httpmime/src/main/java/org/apache/http/entity/mime/content/FileBody.java
index 9623554..93ea6a2 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/content/FileBody.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/content/FileBody.java
@@ -85,14 +85,6 @@ public class FileBody extends AbstractContentBody {
         return new FileInputStream(this.file);
     }
 
-    /**
-     * @deprecated use {@link #writeTo(OutputStream)}
-     */
-    @Deprecated
-    public void writeTo(final OutputStream out, int mode) throws IOException {
-        writeTo(out);
-    }
-
     public void writeTo(final OutputStream out) throws IOException {
         if (out == null) {
             throw new IllegalArgumentException("Output stream may not be null");
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/content/InputStreamBody.java b/httpmime/src/main/java/org/apache/http/entity/mime/content/InputStreamBody.java
index 9766212..1e0f9ea 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/content/InputStreamBody.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/content/InputStreamBody.java
@@ -59,14 +59,6 @@ public class InputStreamBody extends AbstractContentBody {
         return this.in;
     }
 
-    /**
-     * @deprecated use {@link #writeTo(OutputStream)}
-     */
-    @Deprecated
-    public void writeTo(final OutputStream out, int mode) throws IOException {
-        writeTo(out);
-    }
-
     public void writeTo(final OutputStream out) throws IOException {
         if (out == null) {
             throw new IllegalArgumentException("Output stream may not be null");
diff --git a/httpmime/src/main/java/org/apache/http/entity/mime/content/StringBody.java b/httpmime/src/main/java/org/apache/http/entity/mime/content/StringBody.java
index f406c58..d3bdfb4 100644
--- a/httpmime/src/main/java/org/apache/http/entity/mime/content/StringBody.java
+++ b/httpmime/src/main/java/org/apache/http/entity/mime/content/StringBody.java
@@ -78,7 +78,7 @@ public class StringBody extends AbstractContentBody {
 
     /**
      * Create a StringBody from the specified text, mime type and character set.
-     * 
+     *
      * @param text to be used for the body, not {@code null}
      * @param mimeType the mime type, not {@code null}
      * @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used
@@ -103,7 +103,7 @@ public class StringBody extends AbstractContentBody {
     /**
      * Create a StringBody from the specified text and character set.
      * The mime type is set to "text/plain".
-     * 
+     *
      * @param text to be used for the body, not {@code null}
      * @param charset the character set, may be {@code null}, in which case the US-ASCII charset is used
      * @throws UnsupportedEncodingException
@@ -117,7 +117,7 @@ public class StringBody extends AbstractContentBody {
      * Create a StringBody from the specified text.
      * The mime type is set to "text/plain".
      * The hosts default charset is used.
-     * 
+     *
      * @param text to be used for the body, not {@code null}
      * @throws UnsupportedEncodingException
      * @throws IllegalArgumentException if the {@code text} parameter is null
@@ -132,14 +132,6 @@ public class StringBody extends AbstractContentBody {
                 this.charset);
     }
 
-    /**
-     * @deprecated use {@link #writeTo(OutputStream)}
-     */
-    @Deprecated
-    public void writeTo(final OutputStream out, int mode) throws IOException {
-        writeTo(out);
-    }
-
     public void writeTo(final OutputStream out) throws IOException {
         if (out == null) {
             throw new IllegalArgumentException("Output stream may not be null");
diff --git a/httpmime/src/site/site.xml b/httpmime/src/site/site.xml
index 463453f..02ea590 100644
--- a/httpmime/src/site/site.xml
+++ b/httpmime/src/site/site.xml
@@ -30,7 +30,7 @@
   <body>
     <menu name="HttpClient Overview">
       <item name="Description" href="../index.html"/>
-      <item name="Examples" href="../examples.html"/>
+      <item name="Quick Start" href="../quickstart.html"/>
     </menu>
     <menu ref="modules" />
     <menu ref="reports"/>      
diff --git a/pom.xml b/pom.xml
index e3ba289..c2f2497 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,15 +28,14 @@
   <parent>
     <artifactId>project</artifactId>
     <groupId>org.apache.httpcomponents</groupId>
-    <version>4.1.1</version>
+    <version>6</version>
     <relativePath>../project/pom.xml</relativePath>
   </parent>
   <modelVersion>4.0.0</modelVersion>
-  <groupId>org.apache.httpcomponents</groupId>
   <artifactId>httpcomponents-client</artifactId>
   <name>HttpComponents Client</name>
-  <version>4.1.1</version>
-  <description>Components to build client side HTTP services</description>
+  <version>4.2.1</version>
+  <description>Apache HttpComponents Client is a library of components for building client side HTTP services</description>
   <url>http://hc.apache.org/httpcomponents-client</url>
   <inceptionYear>1999</inceptionYear>
   <packaging>pom</packaging>
@@ -60,29 +59,102 @@
   </issueManagement>
 
   <scm>
-    <connection>scm:svn:https://svn.apache.org/repos/asf/httpcomponents/httpclient/tags/4.1.1</connection>
-    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/httpcomponents/httpclient/tags/4.1.1</developerConnection>
-    <url>https://svn.apache.org/repos/asf/httpcomponents/httpclient/tags/4.1.1</url>
+    <connection>scm:svn:https://svn.apache.org/repos/asf/httpcomponents/httpclient/tags/4.2.1</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/httpcomponents/httpclient/tags/4.2.1</developerConnection>
+    <url>https://svn.apache.org/repos/asf/httpcomponents/httpclient/tags/4.2.1</url>
   </scm>
 
   <properties>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-    <httpcore.version>4.1</httpcore.version>
+    <httpcore.version>4.2.1</httpcore.version>
     <commons-logging.version>1.1.1</commons-logging.version>
-    <commons-codec.version>1.4</commons-codec.version>
+    <commons-codec.version>1.6</commons-codec.version>
     <ehcache.version>2.2.0</ehcache.version>
+    <memcached.version>2.6</memcached.version>
     <slf4j.version>1.5.11</slf4j.version>
-    <junit.version>4.8.2</junit.version>
+    <junit.version>4.9</junit.version>
     <easymock.version>2.5.2</easymock.version>
     <mockito.version>1.8.5</mockito.version>
-    <comparisonVersion>4.1</comparisonVersion>
+    <api.comparison.version>4.1</api.comparison.version>
   </properties>
 
+  <repositories>
+    <repository>
+      <id>spy</id>
+      <name>Spy Repository</name>
+      <layout>default</layout>
+      <url>http://files.couchbase.com/maven2/</url>
+      <snapshots>
+        <enabled>false</enabled>
+      </snapshots>
+    </repository>
+  </repositories>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpcore</artifactId>
+        <version>${httpcore.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-logging</groupId>
+        <artifactId>commons-logging</artifactId>
+        <version>${commons-logging.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-codec</groupId>
+        <artifactId>commons-codec</artifactId>
+        <version>${commons-codec.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>net.sf.ehcache</groupId>
+        <artifactId>ehcache-core</artifactId>
+        <version>${ehcache.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.slf4j</groupId>
+        <artifactId>slf4j-jcl</artifactId>
+        <version>${slf4j.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>spy</groupId>
+        <artifactId>spymemcached</artifactId>
+        <version>${memcached.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>junit</groupId>
+        <artifactId>junit</artifactId>
+        <version>${junit.version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.mockito</groupId>
+        <artifactId>mockito-core</artifactId>
+        <version>${mockito.version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.easymock</groupId>
+        <artifactId>easymock</artifactId>
+        <version>${easymock.version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.easymock</groupId>
+        <artifactId>easymockclassextension</artifactId>
+        <version>${easymock.version}</version>
+        <scope>test</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
   <modules>
     <module>httpclient</module>
     <module>httpmime</module>
     <module>httpclient-cache</module>
+    <module>fluent-hc</module>
     <module>httpclient-osgi</module>
   </modules>
 
@@ -147,7 +219,18 @@
       </plugin>
       <plugin>
         <artifactId>maven-javadoc-plugin</artifactId>
+          <executions>
+            <execution>
+              <id>javadoc-aggregate</id>
+              <goals>
+                <goal>aggregate</goal>
+              </goals>
+              <phase>pre-site</phase>
+            </execution>
+          </executions>        
         <configuration>
+          <!-- reduce console output. Can override with -Dquiet=false -->
+          <quiet>true</quiet>
           <source>1.5</source>
           <links>
             <link>http://download.oracle.com/javase/1.5.0/docs/api/</link>
@@ -200,6 +283,16 @@
             <scope>runtime</scope>
           </dependency>
         </dependencies>
+        <executions>
+          <execution>
+            <id>tutorial-site</id>
+            <goals>
+              <goal>generate-html</goal>
+              <goal>generate-pdf</goal>
+            </goals>
+            <phase>pre-site</phase>
+          </execution>
+        </executions>        
         <configuration>
           <includes>index.xml</includes>
           <chunkedOutput>true</chunkedOutput>
@@ -214,13 +307,12 @@
             </entity>
           </entities>
           <postProcess>
-            <copy todir="target/site/tutorial">
-              <fileset dir="target/docbkx">
+            <copy todir="target/site/tutorial/html" failonerror="false">
+              <fileset dir="target/docbkx/html/index">
                 <include name="**/*.html" />
-                <include name="**/*.pdf" />
               </fileset>
             </copy>
-            <copy todir="target/site/tutorial/html">
+            <copy todir="target/site/tutorial/html" failonerror="false">
               <fileset dir="src/docbkx/resources">
                 <include name="**/*.css" />
                 <include name="**/*.png" />
@@ -228,7 +320,7 @@
                 <include name="**/*.jpg" />
               </fileset>
             </copy>
-            <move file="target/site/tutorial/pdf/index.pdf" tofile="target/site/tutorial/pdf/httpclient-tutorial.pdf" failonerror="false" />
+            <copy file="target/docbkx/pdf/index.pdf" tofile="target/site/tutorial/pdf/httpclient-tutorial.pdf" failonerror="false" />
           </postProcess>
         </configuration>
       </plugin>
@@ -257,7 +349,14 @@
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>clirr-maven-plugin</artifactId>
         <configuration>
-          <comparisonVersion>${comparisonVersion}</comparisonVersion>
+          <comparisonVersion>${api.comparison.version}</comparisonVersion>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-release-plugin</artifactId>
+        <configuration>
+          <autoVersionSubmodules>true</autoVersionSubmodules>
         </configuration>
       </plugin>
     </plugins>
@@ -268,12 +367,12 @@
 
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
+         <version>${hc.project-info.version}</version>
          <reportSets>
            <reportSet>
              <reports>
                <report>dependencies</report>
                <report>project-team</report>
-               <report>mailing-list</report>
                <report>issue-tracking</report>
                <report>scm</report>
              </reports>
@@ -284,8 +383,9 @@
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>clirr-maven-plugin</artifactId>
+         <version>${hc.clirr.version}</version>
         <configuration>
-          <comparisonVersion>${comparisonVersion}</comparisonVersion>
+          <comparisonVersion>${api.comparison.version}</comparisonVersion>
         </configuration>
       </plugin>
 
diff --git a/src/docbkx/authentication.xml b/src/docbkx/authentication.xml
index 7a59f1a..29e8f23 100644
--- a/src/docbkx/authentication.xml
+++ b/src/docbkx/authentication.xml
@@ -113,7 +113,7 @@ pwd
             </listitem>
             <listitem>
                 <formalpara>
-                    <title>SPNEGO/Kerberos:</title>
+                    <title>SPNEGO:</title>
                     <para><literal>SPNEGO</literal> (<emphasis>S</emphasis>imple and
                             <emphasis>P</emphasis>rotected <literal>GSSAPI</literal>
                         <emphasis>Nego</emphasis>tiation Mechanism) is a <literal>GSSAPI</literal>
@@ -124,6 +124,12 @@ pwd
                         At present HttpClient only supports the Kerberos sub-mechanism. </para>
                 </formalpara>
             </listitem>
+            <listitem>
+                <formalpara>
+                    <title>Kerberos:</title>
+                    <para>Kerberos authentication implementation. </para>
+                </formalpara>
+            </listitem>
         </itemizedlist>
     </section>
     <section>
@@ -209,7 +215,13 @@ httpclient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, authpref);
             <listitem>
                 <formalpara>
                     <title>AuthPolicy.SPNEGO:</title>
-                    <para>SPNEGO/Kerberos authentication</para>
+                    <para>SPNEGO authentication</para>
+                </formalpara>
+            </listitem>
+            <listitem>
+                <formalpara>
+                    <title>AuthPolicy.KERBEROS:</title>
+                    <para>Kerberos authentication</para>
                 </formalpara>
             </listitem>
         </itemizedlist>
@@ -327,12 +339,12 @@ HttpResponse response = httpclient.execute(httpget, localContext);
 
 AuthState proxyAuthState = (AuthState) localContext.getAttribute(
     ClientContext.PROXY_AUTH_STATE);
-System.out.println("Proxy auth scope: " + proxyAuthState.getAuthScope());
+System.out.println("Proxy auth state: " + proxyAuthState.getState());
 System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
 System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
 AuthState targetAuthState = (AuthState) localContext.getAttribute(
     ClientContext.TARGET_AUTH_STATE);
-System.out.println("Target auth scope: " + targetAuthState.getAuthScope());
+System.out.println("Target auth state: " + targetAuthState.getState());
 System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
 System.out.println("Target auth credentials: " + targetAuthState.getCredentials());
 ]]></programlisting>
@@ -490,7 +502,7 @@ EntityUtils.consume(entity2);
                 supports <literal>SPNEGO</literal> authentication more completely.</para>
             <para>The Sun JRE provides the supporting classes to do nearly all the Kerberos and
                     <literal>SPNEGO</literal> token handling. This means that a lot of the setup is
-                for the GSS classes. The <classname>NegotiateScheme</classname> is a simple class to
+                for the GSS classes. The <classname>SPNegoScheme</classname> is a simple class to
                 handle marshalling the tokens and reading and writing the correct headers.</para>
             <para>The best way to start is to grab the <literal>KerberosHttpClient.java</literal>
                 file in examples and try and get it to work. There are a lot of issues that can
@@ -583,40 +595,6 @@ Value: 0x01
 ]]>
             </programlisting>
         </section>
-        <section>
-            <title>Customizing <literal>SPNEGO</literal> authentication scheme</title>
-            <para>In order to customize <literal>SPNEGO</literal> support a new instance of
-                the <classname>NegotiateSchemeFactory</classname> class must be created and
-                registered with the authentication scheme registry of HttpClient. </para>
-            <programlisting><![CDATA[
-DefaultHttpClient httpclient = new DefaultHttpClient();
-NegotiateSchemeFactory nsf = new NegotiateSchemeFactory();
-httpclient.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);
-]]>
-            </programlisting>
-            <para>There are several options that can be used to customize the behaviour of
-                    <classname>NegotiateSchemeFactory</classname>. </para>
-            <section>
-                <title>Strip port</title>
-                <para>Strips the port off service names e.g.
-                        <literal>HTTP/webserver.ad.example.net:8080</literal> ->
-                        <literal>HTTP/webserver.ad.example.net</literal></para>
-                <para>Found it useful when authenticating against JBoss Negotiation.</para>
-            </section>
-            <section>
-                <title>Custom <literal>SPNEGO</literal> token generator</title>
-                <para>Use this method to inject a custom
-                        <interfacename>SpnegoTokenGenerator</interfacename> class to do the Kerberos
-                    to <literal>SPNEGO</literal> token wrapping.
-                    The <classname>BouncySpnegoTokenGenerator</classname> implementation is provided
-                    as an unsupported contribution from the contrib package. This requires the
-                    BouncyCastle libraries <ulink url="http://www.bouncycastle.org/java.html"
-                        >"http://www.bouncycastle.org/java.html"</ulink>. Found especially useful 
-                    when using Java 1.5, which is known to provide only a limited support for 
-                    <literal>SPNEGO</literal> authentication.    
-                </para>
-            </section>
-        </section>
     </section>
 
 </chapter>
diff --git a/src/docbkx/connmgmt.xml b/src/docbkx/connmgmt.xml
index c596307..8e8f70c 100644
--- a/src/docbkx/connmgmt.xml
+++ b/src/docbkx/connmgmt.xml
@@ -253,7 +253,7 @@ sf.connectSocket(socket, address, null, params);
 ]]></programlisting>
         <section>
             <title>Secure socket layering</title>
-            <para><interfacename>LayeredSchemeSocketFactory</interfacename> is an extension of
+            <para><interfacename>SchemeLayeredSocketFactory</interfacename> is an extension of
                 the <interfacename>SchemeSocketFactory</interfacename> interface. Layered socket 
                 factories are capable of creating sockets layered over an existing plain socket. 
                 Socket layering is used primarily for creating secure sockets through proxies.
@@ -270,37 +270,14 @@ sf.connectSocket(socket, address, null, params);
                     <interfacename>javax.net.ssl.SSLContext</interfacename> as a parameter and use
                 it to create custom configured SSL connections.</para>
             <programlisting><![CDATA[
-TrustManager easyTrustManager = new X509TrustManager() {
-
-    @Override
-    public void checkClientTrusted(
-            X509Certificate[] chain,
-            String authType) throws CertificateException {
-        // Oh, I am easy!
-    }
-
-    @Override
-    public void checkServerTrusted(
-            X509Certificate[] chain,
-            String authType) throws CertificateException {
-        // Oh, I am easy!
-    }
-
-    @Override
-    public X509Certificate[] getAcceptedIssuers() {
-        return null;
-    }
-    
-};
-
+HttpParams params = new BasicHttpParams();
 SSLContext sslcontext = SSLContext.getInstance("TLS");
-sslcontext.init(null, new TrustManager[] { easyTrustManager }, null);
+sslcontext.init(null, null, null);
 
 SSLSocketFactory sf = new SSLSocketFactory(sslcontext); 
-SSLSocket socket = (SSLSocket) sf.createSocket();
+SSLSocket socket = (SSLSocket) sf.createSocket(params);
 socket.setEnabledCipherSuites(new String[] { "SSL_RSA_WITH_RC4_128_MD5" });
 
-HttpParams params = new BasicHttpParams();
 params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 1000L);
 InetSocketAddress address = new InetSocketAddress("locahost", 443);
 sf.connectSocket(socket, address, null, params);
@@ -485,7 +462,7 @@ httpclient.setRoutePlanner(new HttpRoutePlanner() {
 Scheme http = new Scheme("http", 80, PlainSocketFactory.getSocketFactory());
 SchemeRegistry sr = new SchemeRegistry();
 sr.register(http);
-ClientConnectionManager connMrg = new SingleClientConnManager(sr);
+ClientConnectionManager connMrg = new BasicClientConnectionManager(sr);
 
 // Request new connection. This can be a long process
 ClientConnectionRequest connRequest = connMrg.requestConnection(
@@ -539,27 +516,27 @@ try {
         </section>
         <section>
             <title>Simple connection manager</title>
-            <para><classname>SingleClientConnManager</classname> is a simple connection manager that
-                maintains only one connection at a time. Even though this class is thread-safe it
-                ought to be used by one execution thread only.
-                    <classname>SingleClientConnManager</classname> will make an effort to reuse the
-                connection for subsequent requests with the same route. It will, however, close the
-                existing connection and re-open it for the given route, if the route of the persistent
-                connection does not match that of the connection request. If the connection has been
-                already been allocated, then
-                    <exceptionname>java.lang.IllegalStateException</exceptionname> is thrown.</para>
-            <para><classname>SingleClientConnManager</classname> is used by HttpClient per
+            <para><classname>BasicClientConnectionManager</classname> is a simple connection manager
+                that maintains only one connection at a time. Even though this class is thread-safe 
+                it ought to be used by one execution thread only.
+                <classname>BasicClientConnectionManager</classname> will make an effort to reuse 
+                the connection for subsequent requests with the same route. It will, however, close 
+                the existing connection and re-open it for the given route, if the route of the 
+                persistent connection does not match that of the connection request. 
+                If the connection has been already been allocated, then <exceptionname>
+                java.lang.IllegalStateException</exceptionname> is thrown.</para>
+            <para><classname>BasicClientConnectionManager</classname> is used by HttpClient per
                 default.</para>
         </section>
         <section>
             <title>Pooling connection manager</title>
-            <para><classname>ThreadSafeClientConnManager</classname> is a more complex
+            <para><classname>PoolingClientConnectionManager</classname> is a more complex
                 implementation that manages a pool of client connections and is able to service
                 connection requests from multiple execution threads. Connections are pooled on a per
                 route basis. A request for a route for which the manager already has a persistent
                 connection available in the pool will be serviced by leasing a connection from
                 the pool rather than creating a brand new connection.</para>
-            <para><classname>ThreadSafeClientConnManager</classname> maintains a maximum limit of
+            <para><classname>PoolingClientConnectionManager</classname> maintains a maximum limit of
                 connections on a per route basis and in total. Per default this implementation will
                 create no more than 2 concurrent connections per given route and no more 20
                 connections in total. For many real-world applications these limits may prove too
@@ -573,14 +550,14 @@ schemeRegistry.register(
 schemeRegistry.register(
          new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
 
-ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(schemeRegistry);
+PoolingClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry);
 // Increase max total connection to 200
-cm.setMaxTotalConnections(200);
+cm.setMaxTotal(200);
 // Increase default max connection per route to 20
 cm.setDefaultMaxPerRoute(20);
 // Increase max connections for localhost:80 to 50
 HttpHost localhost = new HttpHost("locahost", 80);
-cm.setMaxForRoute(new HttpRoute(localhost), 50);
+cm.setMaxPerRoute(new HttpRoute(localhost), 50);
  
 HttpClient httpClient = new DefaultHttpClient(cm);
 ]]></programlisting>
@@ -604,22 +581,23 @@ httpclient.getConnectionManager().shutdown();
     </section>
     <section>
         <title>Multithreaded request execution</title>
-        <para>When equipped with a pooling connection manager such as ThreadSafeClientConnManager,
-            HttpClient can be used to execute multiple requests simultaneously using multiple
-            threads of execution.</para>
-        <para>The <classname>ThreadSafeClientConnManager</classname> will allocate connections based on
-            its configuration. If all connections for a given route have already been leased, a
-            request for a connection will block until a connection is released back to the pool. One
-            can ensure the connection manager does not block indefinitely in the connection request
-            operation by setting <literal>'http.conn-manager.timeout'</literal> to a positive value.
-            If the connection request cannot be serviced within the given time period
-                <exceptionname>ConnectionPoolTimeoutException</exceptionname> will be thrown.</para>
+        <para>When equipped with a pooling connection manager such as <classname>
+            PoolingClientConnectionManager</classname>, HttpClient can be used to execute multiple 
+            requests simultaneously using multiple threads of execution.</para>
+        <para>The <classname>PoolingClientConnectionManager</classname> will allocate connections 
+            based on its configuration. If all connections for a given route have already been 
+            leased, a request for a connection will block until a connection is released back to 
+            the pool. One can ensure the connection manager does not block indefinitely in the 
+            connection request operation by setting <literal>'http.conn-manager.timeout'</literal> 
+            to a positive value. If the connection request cannot be serviced within the given time 
+            period <exceptionname>ConnectionPoolTimeoutException</exceptionname> will be thrown.
+            </para>
         <programlisting><![CDATA[
 SchemeRegistry schemeRegistry = new SchemeRegistry();
 schemeRegistry.register(
         new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
 
-ClientConnectionManager cm = new ThreadSafeClientConnManager(schemeRegistry);
+ClientConnectionManager cm = new PoolingClientConnectionManager(schemeRegistry);
 HttpClient httpClient = new DefaultHttpClient(cm);
 
 // URIs to perform GETs on
@@ -648,6 +626,10 @@ for (int j = 0; j < threads.length; j++) {
 }
 
 ]]></programlisting>
+        <para>While <interfacename>HttpClient</interfacename> instances are thread safe and can be 
+              shared between multiple threads of execution, it is highly recommended that each 
+              thread maintains its own dedicated instance of <interfacename>HttpContext
+              </interfacename>.</para>
         <programlisting><![CDATA[
 static class GetThread extends Thread {
     
diff --git a/src/docbkx/fluent.xml b/src/docbkx/fluent.xml
new file mode 100644
index 0000000..1fb38ce
--- /dev/null
+++ b/src/docbkx/fluent.xml
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+                 "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<!-- 
+   ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+   distributed with this work for additional information
+   regarding copyright ownership.  The ASF licenses this file
+   to you 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.
+   ====================================================================
+
+-->
+<chapter id="fluent">
+  <title>Fluent API</title>
+  <section>
+    <title>Easy to use facade API</title>
+    <para>
+    As of version of 4.2 HttpClient comes with an easy to use facade API based on the concept 
+    of a fluent interface. Fluent facade API exposes only the most fundamental functions of 
+    HttpClient and is intended for simple use cases that do not require the full flexibility of 
+    HttpClient. For instance, fluent facade API relieves the users from having to deal with 
+    connection management and resource deallocation.  
+    </para>
+    <para>Here are several examples of HTTP requests executed through the HC fluent API</para>
+    <programlisting><![CDATA[
+// Execute a GET with timeout settings and return response content as String.
+Request.Get("http://somehost/")
+        .connectTimeout(1000)
+        .socketTimeout(1000)
+        .execute().returnContent().asString();
+]]>
+    </programlisting>
+    <programlisting><![CDATA[
+// Execute a POST with the 'expect-continue' handshake, using HTTP/1.1,
+// containing a request body as String and return response content as byte array.
+Request.Post("http://somehost/do-stuff")
+        .useExpectContinue()
+        .version(HttpVersion.HTTP_1_1)
+        .bodyString("Important stuff", ContentType.DEFAULT_TEXT)
+        .execute().returnContent().asBytes();
+]]>
+    </programlisting>
+    <programlisting><![CDATA[
+// Execute a POST with a custom header through the proxy containing a request body
+// as an HTML form and save the result to the file
+Request.Post("http://somehost/some-form")
+        .addHeader("X-Custom-header", "stuff")
+        .viaProxy(new HttpHost("myproxy", 8080))
+        .bodyForm(Form.form().add("username", "vip").add("password", "secret").build())
+        .execute().saveContent(new File("result.dump"));
+]]>
+    </programlisting>
+    <para>One can also use <classname>Executor</classname> directly in order to execute requests in 
+    a specific security context whereby authentication details are cached and re-used for 
+    subsequent requests.
+    </para>
+    <programlisting><![CDATA[
+Executor executor = Executor.newInstance()
+        .auth(new HttpHost("somehost"), "username", "password")
+        .auth(new HttpHost("myproxy", 8080), "username", "password")
+        .authPreemptive(new HttpHost("myproxy", 8080));
+
+executor.execute(Request.Get("http://somehost/"))
+        .returnContent().asString();
+
+executor.execute(Request.Post("http://somehost/do-stuff")
+        .useExpectContinue()
+        .bodyString("Important stuff", ContentType.DEFAULT_TEXT))
+        .returnContent().asString();
+]]>
+    </programlisting>
+    <section>
+      <title>Response handling</title>
+      <para>The fluent facade API generally relieves the users from having to deal with 
+      connection management and resource deallocation. In most cases, though, this comes at 
+      a price of having to buffer content of response messages in memory. It is highly 
+      recommended to use <interfacename>ResponseHandler</interfacename> for HTTP response 
+      processing in order to avoid having to buffer content in memory.</para>
+      <programlisting><![CDATA[
+Document result = Request.Get("http://somehost/content")
+        .execute().handleResponse(new ResponseHandler<Document>() {
+
+    public Document handleResponse(final HttpResponse response) throws IOException {
+        StatusLine statusLine = response.getStatusLine();
+        HttpEntity entity = response.getEntity();
+        if (statusLine.getStatusCode() >= 300) {
+            throw new HttpResponseException(
+                    statusLine.getStatusCode(),
+                    statusLine.getReasonPhrase());
+        }
+        if (entity == null) {
+            throw new ClientProtocolException("Response contains no content");
+        }
+        DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
+        try {
+            DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
+            ContentType contentType = ContentType.getOrDefault(entity);
+            if (!contentType.equals(ContentType.APPLICATION_XML)) {
+                throw new ClientProtocolException("Unexpected content type:" + contentType);
+            }
+            String charset = contentType.getCharset();
+            if (charset == null) {
+                charset = HTTP.DEFAULT_CONTENT_CHARSET;
+            }
+            return docBuilder.parse(entity.getContent(), charset);
+        } catch (ParserConfigurationException ex) {
+            throw new IllegalStateException(ex);
+        } catch (SAXException ex) {
+            throw new ClientProtocolException("Malformed XML document", ex);
+        }
+    }
+
+    });
+]]>
+      </programlisting>
+    </section>
+    <section>
+      <title>Asynchronous execution</title>
+      <para>The fluent facade API can be used to execute multiple requests asynchronously using 
+      background threads.
+      </para> 
+      <programlisting><![CDATA[
+ExecutorService threadpool = Executors.newFixedThreadPool(2);
+Async async = Async.newInstance().use(threadpool);
+
+Request[] requests = new Request[] {
+        Request.Get("http://www.google.com/"),
+        Request.Get("http://www.yahoo.com/"),
+        Request.Get("http://www.apache.com/"),
+        Request.Get("http://www.apple.com/")
+};
+
+Queue<Future<Content>> queue = new LinkedList<Future<Content>>();
+for (final Request request: requests) {
+    Future<Content> future = async.execute(request, new FutureCallback<Content>() {
+        
+        public void failed(final Exception ex) {
+            System.out.println(ex.getMessage() + ": " + request);
+        }
+        
+        public void completed(final Content content) {
+            System.out.println("Request completed: " + request);
+        }
+        
+        public void cancelled() {
+        }
+        
+    });
+    queue.add(future);
+}
+
+// Process the queue
+]]>
+      </programlisting>
+    </section>
+  </section>
+</chapter>
diff --git a/src/docbkx/fundamentals.xml b/src/docbkx/fundamentals.xml
index 15df4c4..9c550dc 100644
--- a/src/docbkx/fundamentals.xml
+++ b/src/docbkx/fundamentals.xml
@@ -40,9 +40,10 @@ HttpResponse response = httpclient.execute(httpget);
 HttpEntity entity = response.getEntity();
 if (entity != null) {
     InputStream instream = entity.getContent();
-    int l;
-    byte[] tmp = new byte[2048];
-    while ((l = instream.read(tmp)) != -1) {
+    try {
+        // do something useful
+    } finally {
+        instream.close();
     }
 }
 ]]></programlisting>
@@ -65,28 +66,16 @@ if (entity != null) {
 HttpGet httpget = new HttpGet(
      "http://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq=");
 ]]></programlisting>
-            <para>HttpClient provides a number of utility methods to simplify creation and
-                modification of request URIs.</para>
-            <para>URI can be assembled programmatically:</para>
-            <programlisting><![CDATA[
-URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search", 
-    "q=httpclient&btnG=Google+Search&aq=f&oq=", null);
-HttpGet httpget = new HttpGet(uri);
-System.out.println(httpget.getURI());
-]]></programlisting>
-            <para>stdout ></para>
-            <programlisting><![CDATA[
-http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=
-]]></programlisting>
-            <para>Query string can also be generated from individual parameters:</para>
+            <para>HttpClient provides <classname>URIBuilder</classname> utility class to simplify 
+                creation and modification of request URIs.</para>
             <programlisting><![CDATA[
-List<NameValuePair> qparams = new ArrayList<NameValuePair>();
-qparams.add(new BasicNameValuePair("q", "httpclient"));
-qparams.add(new BasicNameValuePair("btnG", "Google Search"));
-qparams.add(new BasicNameValuePair("aq", "f"));
-qparams.add(new BasicNameValuePair("oq", null));
-URI uri = URIUtils.createURI("http", "www.google.com", -1, "/search", 
-    URLEncodedUtils.format(qparams, "UTF-8"), null);
+URIBuilder builder = new URIBuilder();
+builder.setScheme("http").setHost("www.google.com").setPath("/search")
+    .setParameter("q", "httpclient")
+    .setParameter("btnG", "Google Search")
+    .setParameter("aq", "f")
+    .setParameter("oq", "");
+URI uri = builder.build();
 HttpGet httpget = new HttpGet(uri);
 System.out.println(httpget.getURI());
 ]]></programlisting>
@@ -276,19 +265,16 @@ domain=localhost
                     supplied by the creator of the entity.</para>
                 <programlisting><![CDATA[
 StringEntity myEntity = new StringEntity("important message", 
-    "UTF-8");
+   ContentType.create("text/plain", "UTF-8"));
 
 System.out.println(myEntity.getContentType());
 System.out.println(myEntity.getContentLength());
-System.out.println(EntityUtils.getContentCharSet(myEntity));
 System.out.println(EntityUtils.toString(myEntity));
-System.out.println(EntityUtils.toByteArray(myEntity).length);
-]]></programlisting>
+System.out.println(EntityUtils.toByteArray(myEntity).length);]]></programlisting>
                 <para>stdout ></para>
                 <programlisting><![CDATA[
-Content-Type: text/plain; charset=UTF-8
+Content-Type: text/plain; charset=utf-8
 17
-UTF-8
 important message
 17
 ]]></programlisting>
@@ -394,7 +380,7 @@ if (entity != null) {
                 <classname>FileEntity</classname>.</para>
             <programlisting><![CDATA[
 File file = new File("somefile.txt");
-FileEntity entity = new FileEntity(file, "text/plain; charset=\"UTF-8\"");
+FileEntity entity = new FileEntity(file, ContentType.create("text/plain", "UTF-8"));        
 
 HttpPost httppost = new HttpPost("http://localhost/action.do");
 httppost.setEntity(entity);
@@ -405,34 +391,6 @@ httppost.setEntity(entity);
                 self-contained instead of using the generic <classname>InputStreamEntity</classname>.
                     <classname>FileEntity</classname> can be a good starting point.</para>
             <section>
-                <title>Dynamic content entities</title>
-                <para>Often HTTP entities need to be generated dynamically based a particular
-                    execution context. HttpClient provides support for dynamic entities by using
-                    the <classname>EntityTemplate</classname> entity class and
-                        <interfacename>ContentProducer</interfacename> interface. Content producers
-                    are objects which produce their content on demand, by writing it out to an
-                    output stream. They are expected to be able produce their content every time
-                    they are requested to do so. So entities created with
-                        <classname>EntityTemplate</classname> are generally self-contained and
-                    repeatable.</para>
-                <programlisting><![CDATA[
-ContentProducer cp = new ContentProducer() {
-    public void writeTo(OutputStream outstream) throws IOException {
-        Writer writer = new OutputStreamWriter(outstream, "UTF-8");
-        writer.write("<response>");
-        writer.write("  <content>");
-        writer.write("    important stuff");
-        writer.write("  </content>");
-        writer.write("</response>");
-        writer.flush();
-    }
-};
-HttpEntity entity = new EntityTemplate(cp);
-HttpPost httppost = new HttpPost("http://localhost/handler.do");
-httppost.setEntity(entity);
-]]></programlisting>
-            </section>
-            <section>
                 <title>HTML forms</title>
                 <para>Many applications need to simulate the process of submitting an
                     HTML form, for instance, in order to log in to a web application or submit input
@@ -515,6 +473,9 @@ byte[] response = httpclient.execute(httpget, handler);
             simply a collection of arbitrary named values. An application can populate context
             attributes prior to request execution or examine the context after the execution has
             been completed.</para>
+        <para><interfacename>HttpContext</interfacename> can contain arbitrary objects and 
+            therefore may be unsafe to share between multiple threads. It is recommended that
+            each thread of execution maintains its own context.</para>
         <para>In the course of HTTP request execution HttpClient adds the following attributes to
             the execution context:</para>
         <itemizedlist>
@@ -659,15 +620,6 @@ Final target: http://www.google.ch
                         target server (i.e. the request has not been fully transmitted to the
                         server).</para>
                 </listitem>
-                <listitem>
-                    <para>HttpClient will automatically retry those methods that have been fully
-                        transmitted to the server, but the server failed to respond with an HTTP
-                        status code (the server simply drops the connection without sending anything
-                        back). In this case it is assumed that the request has not been processed by
-                        the server and the application state has not changed. If this assumption may
-                        not hold true for the web server your application is targeting it is highly
-                        recommended to provide a custom exception handler.</para>
-                </listitem>
             </itemizedlist>
         </section>
         <section>
@@ -688,12 +640,20 @@ HttpRequestRetryHandler myRetryHandler = new HttpRequestRetryHandler() {
             // Do not retry if over max retry count
             return false;
         }
-        if (exception instanceof NoHttpResponseException) {
-            // Retry if the server dropped connection on us
-            return true;
+        if (exception instanceof InterruptedIOException) {
+            // Timeout
+            return false;
+        }
+        if (exception instanceof UnknownHostException) {
+            // Unknown host
+            return false;
+        }
+        if (exception instanceof ConnectException) {
+            // Connection refused
+            return false;
         }
-        if (exception instanceof SSLHandshakeException) {
-            // Do not retry on SSL handshake exception
+        if (exception instanceof SSLException) {
+            // SSL handshake exception
             return false;
         }
         HttpRequest request = (HttpRequest) context.getAttribute(
diff --git a/src/docbkx/httpagent.xml b/src/docbkx/httpagent.xml
index e66be82..94ab8c5 100644
--- a/src/docbkx/httpagent.xml
+++ b/src/docbkx/httpagent.xml
@@ -144,10 +144,12 @@ httpclient.getConnectionManager().shutdown();
             <listitem>
                 <formalpara>
                     <title><constant>ClientPNames.VIRTUAL_HOST</constant>='http.virtual-host':</title>
-                    <para>defines the virtual host name to be used in the <literal>Host</literal>
-                        header instead of the physical host name. This parameter expects a value of
-                        type <classname>HttpHost</classname>. If this parameter is not set name or
-                        IP address of the target host will be used.</para>
+                    <para>defines the virtual host settings to be used in the <literal>Host</literal>
+                        header instead of the physical host. This parameter expects a value of
+                        type <classname>HttpHost</classname>. The HttpHost port does not have to
+                        be specified as it will be derived from the target.
+                        If this parameter is not set, the name or
+                        IP address (and port if required) of the target host will be used.</para>
                 </formalpara>
             </listitem>
             <listitem>
diff --git a/src/docbkx/index.xml b/src/docbkx/index.xml
index 7759da1..677f7bc 100644
--- a/src/docbkx/index.xml
+++ b/src/docbkx/index.xml
@@ -66,6 +66,7 @@
     <xi:include href="statemgmt.xml"/>
     <xi:include href="authentication.xml"/>
     <xi:include href="httpagent.xml"/>
+    <xi:include href="fluent.xml"/>
     <xi:include href="caching.xml"/>
     <xi:include href="advanced.xml"/>
     
diff --git a/src/docbkx/statemgmt.xml b/src/docbkx/statemgmt.xml
index c8c8651..73fabf9 100644
--- a/src/docbkx/statemgmt.xml
+++ b/src/docbkx/statemgmt.xml
@@ -160,6 +160,12 @@ stdCookie.setAttribute(ClientCookie.PORT_ATTR, "80,8080");
                         above implementations into one class.</para>
                 </formalpara>
             </listitem>
+            <listitem>
+                <formalpara>
+                    <title>Ignore cookies:</title>
+                    <para>All cookies are ignored.</para>
+                </formalpara>
+            </listitem>
         </itemizedlist>
         <para>It is strongly recommended to use the <literal>Best Match</literal> policy and let
             HttpClient pick up an appropriate compliance level at runtime based on the execution
@@ -247,6 +253,12 @@ stdCookie.setAttribute(ClientCookie.PORT_ATTR, "80,8080");
                     <para>Best match meta-policy.</para>
                 </formalpara>
             </listitem>
+            <listitem>
+                <formalpara>
+                    <title>ignoreCookies:</title>
+                    <para>All cookies are ignored.</para>
+                </formalpara>
+            </listitem>
         </itemizedlist>
     </section>
     <section>
diff --git a/src/main/assembly/bin.xml b/src/main/assembly/bin.xml
index e500a5f..e97771c 100644
--- a/src/main/assembly/bin.xml
+++ b/src/main/assembly/bin.xml
@@ -69,18 +69,10 @@
           <directory>target/site/apidocs</directory>
           <outputDirectory>javadoc</outputDirectory>
         </fileSet>
-        <!-- Tutorial (HTML) -->
+        <!-- Tutorial -->
         <fileSet>
-          <directory>target/docbkx/html</directory>
-          <outputDirectory>tutorial/html</outputDirectory>
-        </fileSet>
-        <fileSet>
-          <directory>src/docbkx/resources/css</directory>
-          <outputDirectory>tutorial/html/css</outputDirectory>
-        </fileSet>
-        <fileSet>
-          <directory>src/docbkx/resources/images</directory>
-          <outputDirectory>tutorial/html/images</outputDirectory>
+          <directory>target/site/tutorial</directory>
+          <outputDirectory>tutorial</outputDirectory>
         </fileSet>
         <!-- Base module -->
         <fileSet>
@@ -99,12 +91,4 @@
           </includes>
         </fileSet>
     </fileSets>
-    <files>
-        <!-- Tutorial (PDF) -->
-        <file>
-          <source>target/docbkx/pdf/index.pdf</source>
-          <outputDirectory>tutorial/pdf</outputDirectory>
-          <destName>httpclient-tutorial.pdf</destName>
-        </file>
-    </files>
 </assembly>
diff --git a/src/main/assembly/osgi-bin.xml b/src/main/assembly/osgi-bin.xml
index 9ff0f8c..36c0cea 100644
--- a/src/main/assembly/osgi-bin.xml
+++ b/src/main/assembly/osgi-bin.xml
@@ -56,18 +56,10 @@
           <directory>target/site/apidocs</directory>
           <outputDirectory>javadoc</outputDirectory>
         </fileSet>
-        <!-- Tutorial (HTML) -->
+        <!-- Tutorial -->
         <fileSet>
-          <directory>target/docbkx/html</directory>
-          <outputDirectory>tutorial/html</outputDirectory>
-        </fileSet>
-        <fileSet>
-          <directory>src/docbkx/resources/css</directory>
-          <outputDirectory>tutorial/html/css</outputDirectory>
-        </fileSet>
-        <fileSet>
-          <directory>src/docbkx/resources/images</directory>
-          <outputDirectory>tutorial/html/images</outputDirectory>
+          <directory>target/site/tutorial</directory>
+          <outputDirectory>tutorial</outputDirectory>
         </fileSet>
         <!-- Base module -->
         <fileSet>
@@ -86,12 +78,4 @@
           </includes>
         </fileSet>
     </fileSets>
-    <files>
-        <!-- Tutorial (PDF) -->
-        <file>
-          <source>target/docbkx/pdf/index.pdf</source>
-          <outputDirectory>tutorial/pdf</outputDirectory>
-          <destName>httpclient-tutorial.pdf</destName>
-        </file>
-    </files>
 </assembly>
diff --git a/src/site/apt/download.apt b/src/site/apt/download.apt
index e7334a2..a063244 100644
--- a/src/site/apt/download.apt
+++ b/src/site/apt/download.apt
@@ -42,35 +42,47 @@ HttpClient Downloads
     in your {{{http://maven.apache.org/guides/introduction/introduction-to-the-pom.html}pom.xml}} 
     by adding the following block to the dependency descriptor:
 
-* {HttpComponents Client 4.1.1}
+* {HttpComponents Client 4.2.1}
 
 -------------------------
   <dependency>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpclient</artifactId>
-    <version>4.1.1</version>
+    <version>4.2.1</version>
     <scope>compile</scope>
   </dependency>
 -------------------------
 
-* {HttpComponents HttpMime 4.1.1}
+* {HttpComponents HttpMime 4.2.1}
 
 -------------------------
   <dependency>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpmime</artifactId>
-    <version>4.1.1</version>
+    <version>4.2.1</version>
     <scope>compile</scope>
   </dependency>
 -------------------------
 
-* {HttpComponents HttpClient Cache 4.1.1}
+* {HttpComponents HttpClient Cache 4.2.1}
 
 -------------------------
   <dependency>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpclient-cache</artifactId>
-    <version>4.1.1</version>
+    <version>4.2.1</version>
     <scope>compile</scope>
   </dependency>
 -------------------------
+
+* {HttpComponents HttpClient Fluent 4.2.1}
+
+-------------------------
+  <dependency>
+    <groupId>org.apache.httpcomponents</groupId>
+    <artifactId>hc-fluent</artifactId>
+    <version>4.2.1</version>
+    <scope>compile</scope>
+  </dependency>
+-------------------------
+
diff --git a/src/site/apt/index.apt b/src/site/apt/index.apt
index 92ba78d..b0dde66 100644
--- a/src/site/apt/index.apt
+++ b/src/site/apt/index.apt
@@ -47,10 +47,21 @@ HttpClient Overview
 
 {Documentation}
 
-    * HttpClient Tutorial ( {{{./tutorial/html/index.html}HTML}} / {{{./tutorial/pdf/httpclient-tutorial.pdf}PDF}} )
-    
-    * Some examples of HttpClient in action can be found {{{./examples.html}here}}
+    [[1]] {{{./quickstart.html}Quick Start}} - contains a simple, complete example of an HTTP GET 
+    and POST with parameters.
+
+    [[1]] {{{./tutorial/html/index.html}HttpClient Tutorial}} - gives a detailed examination of the 
+    HttpClient API, which was written in close accordance with the (sometimes not very intuitive) 
+    HTTP specification/standard. A copy is also shipped with the release.  
+    {{{./tutorial/pdf/httpclient-tutorial.pdf}A PDF version}} is also available
+        
+    [[1]] {{{./examples.html}HttpClient Examples}} - a set of examples demonstrating some of 
+    the more complex behavior. 
     
+    [[1]] {{{./primer.html}HttpClient Primer}} - explains the scope of HttpClient.
+    Note that HttpClient is not a browser.  It lacks the UI, HTML renderer and a JavaScript engine
+    that a browser will possess. 
+   
 {Features}
 
     * Standards based, pure Java, implementation of HTTP versions 1.0 and 1.1
@@ -64,7 +75,7 @@ HttpClient Overview
 
     * Tunneled HTTPS connections through HTTP proxies, via the CONNECT method.
 
-    * Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos authentication schemes.
+    * Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO, Kerberos authentication schemes.
 
     * Plug-in mechanism for custom authentication schemes.
 
@@ -91,7 +102,7 @@ HttpClient Overview
 
     * The ability to set connection timeouts.
 
-    * Experimental support for HTTP/1.1 response caching.
+    * Support for HTTP/1.1 response caching.
 
     * Source code is freely available under the Apache License.
 
diff --git a/src/site/apt/ntlm.apt b/src/site/apt/ntlm.apt
new file mode 100644
index 0000000..3fd54aa
--- /dev/null
+++ b/src/site/apt/ntlm.apt
@@ -0,0 +1,155 @@
+~~ ====================================================================
+~~ Licensed to the Apache Software Foundation (ASF) under one
+~~ or more contributor license agreements.  See the NOTICE file
+~~ distributed with this work for additional information
+~~ regarding copyright ownership.  The ASF licenses this file
+~~ to you 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.
+~~ ====================================================================
+~~
+~~ This software consists of voluntary contributions made by many
+~~ individuals on behalf of the Apache Software Foundation.  For more
+~~ information on the Apache Software Foundation, please see
+~~ <http://www.apache.org/>.
+
+    ----------
+    NTLM support in HttpClient
+    ----------
+    ----------
+    ----------
+
+NTLM support in HttpClient
+
+* {Background}
+
+    NTLM is a proprietary authentication scheme developed by Microsoft and optimized for
+    Windows operating system.
+
+    Until year 2008 there was no official, publicly available, complete documentation of
+    the protocol. {{{http://davenport.sourceforge.net/ntlm.html}Unofficial}} 3rd party
+    protocol descriptions existed as a result of reverse-engineering efforts. It was not
+    really known whether the protocol based on the reverse-engineering were complete or
+    even correct.
+
+    Microsoft published {{{http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NLMP%5D.pdf}MS-NLMP}}
+    and {{{http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NTHT%5D.pdf}MS-NTHT}}
+    specifications in February 2008 as a part of its
+    {{{http://www.microsoft.com/interop/principles/default.mspx}Interoperability
+    Principles initiative}}. 
+
+    HttpClient as of version 4.1 supports NTLMv1 and NTLMv2 authentication protocols out 
+    of the box using a custom authentication engine. However, there are still known compatibility
+    issues with newer Microsoft products as the default NTLM engine implementation is still 
+    relatively new. One can also use {{{http://jcifs.samba.org/}JCIFS}} as an alternative, more 
+    established and mature NTLM engine developed by Samba project. 
+
+* {Using Samba JCIFS as an alternative NTLM engine}
+
+    Follow these instructions to build an NTLMEngine implementation using JCIFS library
+
+    <<!!!!DISCLAIMER !!!! HttpComponents project DOES _NOT_ SUPPORT the code provided below. 
+    Use it as is at your own discretion>>.
+
+    * Download version 1.3.14 or newer of the JCIFS library from the 
+    {{{http://jcifs.samba.org/}Samba}} web site
+
+    * Implement NTLMEngine interface
+
+----------------------------------------
+import java.io.IOException;
+
+import jcifs.ntlmssp.NtlmFlags;
+import jcifs.ntlmssp.Type1Message;
+import jcifs.ntlmssp.Type2Message;
+import jcifs.ntlmssp.Type3Message;
+import jcifs.util.Base64;
+
+import org.apache.http.impl.auth.NTLMEngine;
+import org.apache.http.impl.auth.NTLMEngineException;
+
+public final class JCIFSEngine implements NTLMEngine {
+
+    private static final int TYPE_1_FLAGS = 
+            NtlmFlags.NTLMSSP_NEGOTIATE_56 | 
+            NtlmFlags.NTLMSSP_NEGOTIATE_128 | 
+            NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 | 
+            NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN | 
+            NtlmFlags.NTLMSSP_REQUEST_TARGET;
+
+    public String generateType1Msg(final String domain, final String workstation)
+            throws NTLMEngineException {
+        final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
+        return Base64.encode(type1Message.toByteArray());
+    }
+
+    public String generateType3Msg(final String username, final String password,
+            final String domain, final String workstation, final String challenge)
+            throws NTLMEngineException {
+        Type2Message type2Message;
+        try {
+            type2Message = new Type2Message(Base64.decode(challenge));
+        } catch (final IOException exception) {
+            throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
+        }
+        final int type2Flags = type2Message.getFlags();
+        final int type3Flags = type2Flags
+                & (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
+        final Type3Message type3Message = new Type3Message(type2Message, password, domain,
+                username, workstation, type3Flags);
+        return Base64.encode(type3Message.toByteArray());
+    }
+
+}
+----------------------------------------
+
+    * Implement AuthSchemeFactory interface
+
+----------------------------------------
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeFactory;
+import org.apache.http.impl.auth.NTLMScheme;
+import org.apache.http.params.HttpParams;
+
+public class NTLMSchemeFactory implements AuthSchemeFactory {
+
+    public AuthScheme newInstance(final HttpParams params) {
+        return new NTLMScheme(new JCIFSEngine());
+    }
+
+}
+----------------------------------------
+
+    * Register NTLMSchemeFactory with the HttpClient instance you want to NTLM 
+    enable.
+
+----------------------------------------
+httpclient.getAuthSchemes().register("ntlm", new NTLMSchemeFactory());
+----------------------------------------
+
+    * Set NTCredentials for the web server you are going to access.
+
+----------------------------------------
+httpclient.getCredentialsProvider().setCredentials(
+    new AuthScope("myserver", -1), 
+    new NTCredentials("username", "password", "MYSERVER", "MYDOMAIN"));
+-----------------------------------------------------------
+
+    * You are done.
+
+
+* {Why this code is not distributed with HttpClient} 
+
+    JCIFS is licensed under the Lesser General Public License (LGPL). This license 
+    is not compatible with the Apache Licenses under which all Apache Software is 
+    released. Lawyers of the Apache Software Foundation are currently investigating 
+    under which conditions Apache software is allowed to make use of LGPL software.
diff --git a/src/site/apt/quickstart.apt b/src/site/apt/quickstart.apt
index 688a8e3..c003f50 100644
--- a/src/site/apt/quickstart.apt
+++ b/src/site/apt/quickstart.apt
@@ -33,8 +33,8 @@ HttpClient Quick Start
     [[1]] Download 'Binary' package of the latest official release from 
     the {{{http://hc.apache.org/downloads.cgi} project download page}}. 
   
-    There should be 5 jars in total (components marked with (*) are optional if MIME multipart 
-    support is not required)
+    There should be 7 jars in total (components marked with (*) include additional features and 
+    are optional) on the classpath.
   
     * commons-logging-<x.x.x>.jar
   
@@ -46,14 +46,85 @@ HttpClient Quick Start
   
     * httpmime-<x.x.x>.jar (*)
     
+    * httpclient-cache-<x.x.x>.jar (*)
+
+    * fluent-hc-<x.x.x>.jar (*)
+
     []
+
+    [[2]] The below code fragment illustrates the execution of HTTP GET and POST requests using the HttpClient native API.
+
+-------------    
+
+DefaultHttpClient httpclient = new DefaultHttpClient();
+HttpGet httpGet = new HttpGet("http://targethost/homepage");
+
+    HttpResponse response1 = httpclient.execute(httpGet);
+
+// The underlying HTTP connection is still held by the response object 
+// to allow the response content to be streamed directly from the network socket. 
+// In order to ensure correct deallocation of system resources 
+// the user MUST either fully consume the response content  or abort request 
+// execution by calling HttpGet#releaseConnection().
+
+try {
+    System.out.println(response1.getStatusLine());
+    HttpEntity entity1 = response1.getEntity();
+    // do something useful with the response body
+    // and ensure it is fully consumed
+    EntityUtils.consume(entity1);
+} finally {
+    httpGet.releaseConnection();
+}
+
+HttpPost httpPost = new HttpPost("http://targethost/login");
+List <NameValuePair> nvps = new ArrayList <NameValuePair>();
+nvps.add(new BasicNameValuePair("username", "vip"));
+nvps.add(new BasicNameValuePair("password", "secret"));
+httpPost.setEntity(new UrlEncodedFormEntity(nvps));
+HttpResponse response2 = httpclient.execute(httpPost);
+
+try {
+    System.out.println(response2.getStatusLine());
+    HttpEntity entity2 = response2.getEntity();
+    // do something useful with the response body
+    // and ensure it is fully consumed
+    EntityUtils.consume(entity2);
+} finally {
+    httpPost.releaseConnection();
+}
+
+-------------    
+
+    Source can be downloaded 
+    {{{./httpclient/examples/org/apache/http/examples/client/QuickStart.java}here}}
+
+    [[3]] The same requests can be executed using a simpler, albeit less flexible, fluent API.
+
+-------------    
+
+// The fluent API relieves the user from having to deal with manual deallocation of system
+// resources at the cost of having to buffer response content in memory in some cases.
+
+Request.Get("http://targethost/homepage")
+    .execute().returnContent();
+Request.Post("http://targethost/login")
+    .bodyForm(Form.form().add("username",  "vip").add("password",  "secret").build())
+    .execute().returnContent();
+
+-------------    
     
-    [[2]] Take a look at the HttpClient tutorial shipped with the release package or available
-    {{{./tutorial/html/index.html}online}} to learn the HttpClient API.
-    
-    [[3]] Another good way of getting started with HttpClient is by seeing it in action. Take 
-    a look at the samples shipped with the release package or available {{{./examples.html}online}}.
+    Source can be downloaded 
+    {{{./httpclient/examples/org/apache/http/examples/client/fuent/FluentQuickStart.java}here}}
     
-    [[4]] Please note that HttpClient is not a browser. Importantly it lacks UI, cache, HTML 
-    renderer and a JavaScript engine. To learn more about the scope of HttpClient please refer to 
-    {{{./primer.html}HttpClient Primer}}
\ No newline at end of file
+    [[4]] {{{./examples.html}HttpClient Examples}} - a set of examples demonstrating some of 
+    the more complex behavior. 
+
+    [[5]] {{{./tutorial/html/index.html}HttpClient Tutorial}} - gives a detailed examination of the 
+    HttpClient API, which was written in close accordance with the (sometimes not very intuitive) 
+    HTTP specification/standard. A copy is also shipped with the release.  
+    {{{./tutorial/pdf/httpclient-tutorial.pdf}A PDF version}} is also available
+
+    [[6]] {{{./primer.html}HttpClient Primer}} - explains the scope of HttpClient.
+    Note that HttpClient is not a browser.  It lacks the UI, HTML renderer and a JavaScript engine
+    that a browser will possess.   
diff --git a/src/site/site.xml b/src/site/site.xml
index 902cd66..153f70a 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -36,12 +36,14 @@
       <item name="Tutorial" href="tutorial/html/index.html"/>
       <item name="Examples" href="examples.html"/>
       <item name="Client HTTP Programming Primer" href="primer.html"/>
+      <item name="NTLM Guide" href="ntlm.html"/>
       <item name="Logging" href="logging.html"/>
     </menu>
     <menu name="Modules">
       <item name="HttpClient" href="httpclient/index.html"/>
       <item name="HttpMime" href="httpmime/index.html"/>
       <item name="HttpClient Cache" href="httpclient-cache/index.html"/>
+      <item name="Fluent HC" href="fluent-hc/index.html"/>
     </menu>
     <!-- Reports don't really apply at this level; in particular the dependecy report is misleading -->
     <!-- menu ref="reports"/-->

-- 
httpcomponents-client: HTTP/1.1 compliant HTTP agent Java implementation



More information about the pkg-java-commits mailing list