HttpAsyncClient 4.1.3 RC1 tag

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpasyncclient/tags/4.1.3-RC1@1836329 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/4.1.x/.gitignore b/4.1.x/.gitignore
new file mode 100644
index 0000000..0121106
--- /dev/null
+++ b/4.1.x/.gitignore
@@ -0,0 +1,9 @@
+.classpath
+.project
+.settings
+.clover
+.externalToolBuilders
+target
+maven-eclipse.xml
+.idea
+*.iml
diff --git a/4.1.x/.travis.yml b/4.1.x/.travis.yml
new file mode 100644
index 0000000..c1335bd
--- /dev/null
+++ b/4.1.x/.travis.yml
@@ -0,0 +1,24 @@
+# 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.
+
+language: java
+sudo: false
+
+jdk:
+  - openjdk7
+  - oraclejdk8
+
+after_success:
+  - mvn clean cobertura:cobertura coveralls:report
diff --git a/4.1.x/BUILDING.txt b/4.1.x/BUILDING.txt
new file mode 100644
index 0000000..cc22570
--- /dev/null
+++ b/4.1.x/BUILDING.txt
@@ -0,0 +1,50 @@
+Building HttpComponents AsyncClient 
+============================
+
+(1) Requisites
+--------------
+JDK 1.6+ is required in order to compile and run HttpAsyncClient.
+
+HttpAsyncClient utilizes Maven as a distribution management and packaging tool.
+Version 3.0.3 or later is required. 
+
+Maven installation and configuration instructions can be found here:
+
+http://maven.apache.org/run-maven/index.html
+
+(2) Executing test cases
+
+Execute the following command in order to compile and test the components
+
+mvn test
+
+(3) Building packages 
+
+Execute the following command in order to build the JAR packages
+
+mvn package
+
+The resultant packages can be found in the target folders of their respective modules
+
+httpasyncclient/target/httpasyncclient-<VERSION>.jar
+httpasyncclient-cache/target/httpasyncclient-cache-<VERSION>.jar
+httpasyncclient-osgi/target/org.apache.httpcomponents.httpasyncclient_<VERSION>.jar
+
+where <VERSION> is the release version
+
+(4) Validating packages
+
+Check for binary compatibility with the previous version with:
+
+mvn clirr:check
+
+Check for proper license headers with:
+
+mvn apache-rat:check
+
+(5) Building documentation 
+
+Execute the following commands in order to generate Javadoc:
+
+mvn compile javadoc:aggregate
+
diff --git a/4.1.x/LICENSE.txt b/4.1.x/LICENSE.txt
new file mode 100644
index 0000000..2c41ec8
--- /dev/null
+++ b/4.1.x/LICENSE.txt
@@ -0,0 +1,182 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+   
+This project contains annotations derived from JCIP-ANNOTATIONS
+Copyright (c) 2005 Brian Goetz and Tim Peierls.
+See http://www.jcip.net and the Creative Commons Attribution License 
+(http://creativecommons.org/licenses/by/2.5)
+
diff --git a/4.1.x/NOTICE.txt b/4.1.x/NOTICE.txt
new file mode 100644
index 0000000..b98ba5e
--- /dev/null
+++ b/4.1.x/NOTICE.txt
@@ -0,0 +1,5 @@
+Apache HttpComponents AsyncClient
+Copyright 2010-2017 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/4.1.x/README.txt b/4.1.x/README.txt
new file mode 100644
index 0000000..6c0e096
--- /dev/null
+++ b/4.1.x/README.txt
@@ -0,0 +1,71 @@
+Apache HttpComponents AsyncClient
+============================
+
+Welcome to the HttpAsyncClient component of the Apache HttpComponents project.
+
+Building Instructions
+---------------------
+
+For building from source instructions please refer to BUILDING.txt.
+
+Dependencies
+------------
+
+HttpAsyncClient main module requires Java 5.0 compatible runtime and
+depends on the following external libraries:
+
+* Apache HttpComponents HttpCore
+* Apache HttpComponents HttpCore NIO
+* Apache HttpComponents HttpClient
+* Apache Commons Logging
+* Apache Commons Codec
+
+(for detailed information on external dependencies please see pom.xml)
+
+Licensing
+---------
+
+Apache HttpComponents AsyncClient is licensed under the Apache License 2.0.
+See the files called LICENSE.txt and NOTICE.txt for more information.
+
+Cryptographic Software Notice
+-----------------------------
+
+This distribution may include software that has been designed for use
+with cryptographic software. The country in which you currently reside
+may have restrictions on the import, possession, use, and/or re-export
+to another country, of encryption software. BEFORE using any encryption
+software, please check your country's laws, regulations and policies
+concerning the import, possession, or use, and re-export of encryption
+software, to see if this is permitted. See <http://www.wassenaar.org/>
+for more information.
+
+The U.S. Government Department of Commerce, Bureau of Industry and
+Security (BIS), has classified this software as Export Commodity
+Control Number (ECCN) 5D002.C.1, which includes information security
+software using or performing cryptographic functions with asymmetric
+algorithms. The form and manner of this Apache Software Foundation
+distribution makes it eligible for export under the License Exception
+ENC Technology Software Unrestricted (TSU) exception (see the BIS
+Export Administration Regulations, Section 740.13) for both object
+code and source code.
+
+The following provides more details on the included software that
+may be subject to export controls on cryptographic software:
+
+  Apache HttpComponents AsyncClient interfaces with the
+  Java Secure Socket Extension (JSSE) API to provide
+
+    - HTTPS support
+
+  Apache HttpComponents AsyncClient does not include any
+  implementation of JSSE.
+
+Contact
+-------
+
+  o For general information visit the main project site at
+    http://hc.apache.org/
+
+  o For current status information visit the status page at
+    http://hc.apache.org/status.html
diff --git a/4.1.x/RELEASE_NOTES.txt b/4.1.x/RELEASE_NOTES.txt
new file mode 100644
index 0000000..71b9980
--- /dev/null
+++ b/4.1.x/RELEASE_NOTES.txt
@@ -0,0 +1,434 @@
+Release 4.1.4
+-------------------
+
+This is a maintenance release that adds Automatic-Module-Name to the manifest for compatibility
+with Java 9 Platform Module System and fixes a number of issues discovered since 4.1.3
+
+
+Changelog
+-------------------
+
+* Add Automatic-Module-Name in manifest so Java9 modular applications can depend on this library.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Upgraded HttpCore dependency to version 4.4.10; upgraded HttpClient dependency to version 4.5.6.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Pass the original request as a parameter to the redirect handler.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-135] HttpAsyncMethods.createHead() methods creates HttpGet objects.
+  Contributed by Mateusz Matela <mateusz dot matela at gmail dot>
+
+* [HTTPASYNC-136]: Failing tests on Fedora 28 due to weak encryption algorithms in test keystore.
+  Contributed by Gary Gregory <ggregory at apache.org> and Michael Simacek <msimacek at redhat dot com>
+
+* [HTTPASYNC-126] Increase range of commons-logging in OSGI manifest
+
+* [HTTPASYNC-118] HttpAsyncClientBuilder to use SystemDefaultCredentialsProvider by default when configured
+  to use system properties.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+Release 4.1.3
+-------------------
+
+Changelog
+-------------------
+
+* HttpCore upgraded to version 4.4.6
+
+* HttpClient upgraded to version 4.5.3
+
+* [HTTPASYNC-110] Default exchange handler fails to correctly re-use fully established tunnelled connection
+  leased from the pool when CONNECT request results in 407 over connection that cannot be kept alive.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-116] Correct cancellation of client message exchange.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Fix HttpAsyncMethods#createPost/createPut(URI,byte[],ContentType): set entity to request
+  Contributed by Dmitry Potapov <dpotapov at yandex-team.ru>
+
+
+
+Release 4.1.2
+-------------------
+
+This is a maintenance release that fixes a number of issues discovered since 4.1.1 and upgrades
+HttpCore and HttpClient dependencies.
+
+Changelog
+-------------------
+
+* HttpCore upgraded to version 4.4.5
+
+* HttpClient upgraded to version 4.5.2
+
+* [HTTPASYNC-105] socketTimeout is not reset back to default after a request that has specific 
+  timeout
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-101 CachingHttpAsyncClient to create default HttpContext if none specified
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-97] Upgraded HttpClient OSGi dependency to 4.5
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+
+Release 4.1.1
+-------------------
+
+This is a maintenance release that fixes a number of issues discovered since 4.1 and upgrades
+HttpCore and HttpClient dependencies.
+
+Changelog
+-------------------
+
+* HttpCore upgraded to version 4.4.4
+
+* HttpClient upgraded to version 4.5.1
+
+* [HTTPASYNC-92] Fixed osgi bundle imports (added missing 'org.apache.http.ssl')
+  Contributed by Daniel Kulp <dkulp at apache.org>
+
+* HttpAsyncClientBuilder ignores UseSystemProperties setting when initializing SSL context
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+
+
+Release 4.1
+-------------------
+
+This is the first stable (GA) release of HttpAsyncClient 4.1. Notable features and enhancements 
+included in 4.1 series are:
+
+* Support for pipelined request execution 
+
+* Support for the latest HTTP state management specification (RFC 6265). Please note that the old 
+cookie policy is still used by default for compatibility reasons. RFC 6265 compliant cookie 
+policies need to be explicitly configured by the user. Please also note that as of next feature 
+release support for Netscape draft, RFC 2109 and RFC 2965 cookie policies will be deprecated 
+and disabled by default. It is recommended to use RFC 6265 compliant policies for new applications 
+unless compatibility with RFC 2109 and RFC 2965 is required and to migrate existing applications 
+to the default cookie policy.
+
+* Enhanced, redesigned and rewritten default SSL hostname verifier with improved RFC 2818
+compliance
+
+* Default SSL hostname verifier and default cookie policy now validate certificate identity 
+and cookie domain of origin against the public suffix list maintained by Mozilla.org
+<https://publicsuffix.org/list>
+
+* Authentication cache thread-safety: authentication cache used by HttpClient is now thread-safe
+and can be shared by multiple threads in order to re-use authentication state for subsequent
+requests
+
+
+Changelog:
+-------------------
+
+* [HTTPASYNC-90] Fixed SNI support.
+  Contributed by David <dblack at atlassian.com>
+
+* [HTTPASYNC-88] persistent connections can time out while kept alive in the pool
+  causing unexpected SocketTimeoutException
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-86]: fixed a race condition upon connection lease from the connection pool:
+  in very rare  circumstances the main execution thread can get jammed for so long
+  that the I/O dispatch thread succeeds in completing the request and releasing the connection
+  while the main still is performing connection validation.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Update Apache Commons Logging version from 1.1.3 to 1.2. 
+  Contributed by Gary Gregory <ggregory at apache.org>
+
+
+Release 4.1 BETA1
+-------------------
+
+This is the first BETA release of HttpAsyncClient 4.1. Notable features and enhancements included
+in 4.1 series are:
+
+* Support for pipelined request execution 
+
+* Enhanced redesigned and rewritten default SSL hostname verifier with improved RFC 2818
+compliance
+
+* Default SSL hostname verifier and default cookie policy now validate certificate identity 
+and cookie domain of origin against the public suffix list maintained by Mozilla.org
+<https://publicsuffix.org/list>
+
+* Authentication cache thread-safety: authentication caches used by HttpAsyncClient is now 
+thread-safe and can be shared by multiple contexts in order to re-use authentication state for 
+subsequent requests
+
+
+Release 4.0.2
+-------------------
+
+HttpAsyncClient 4.0.2 (GA) is a bug fix release that addresses several issues reported since 
+release 4.0.1. This release also upgrades HttpClient dependency to the latest stable version. 
+
+All users of HttpAsyncClient 4.0 are strongly advised to upgrade.
+
+Changelog
+-------------------
+
+* [HTTPASYNC-79] Rearranged sequence of operations upon connection lease to eliminate possibility
+  of a connection leak due to race condition on keep-alive boundary
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Replaced dynamic proxies with custom proxy classes to reduce thread contention.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-73] Original request headers are not copied upon redirect
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+
+Release 4.0.1
+-------------------
+
+This maintenance release fixes a number of bugs including incorrect OSGi bundle metadata
+found since release 4.0. This release also upgrades HttpCore and HttpClient dependencies to 
+the latest stable versions. 
+
+All users of HttpAsyncClient 4.0 are advised to upgrade.
+
+Changelog
+-------------------
+
+* [HTTPASYNC-69] Premature cancellation of the response consumer to immediately shut down
+  and release the underlying connection.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-68] Connection closed by the opposite endpoint immediately upon its lease from
+  the pool is never released back and reclaimed by the pool (collection leak).
+  Contributed by Dmitry Potapov <potapov.d at gmail.com>
+
+* Persistent connections always get discarded in case of a response future cancellation
+  even if they can be kept alive.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-64] Race conditions in async caching module.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-65] Relaxed OSGi dependency import version ranges
+  Contributed by Christian Schneider <chris at die-schneider.net> 
+
+* [HTTPCLIENT-1446] NTLM proxy + BASIC target auth fails with 'Unexpected state:
+  MSG_TYPE3_GENERATED'.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-63] ConnectionShutdownException thrown in case of out-of-sequence response.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPCLIENT-1442] Authentication header set by the user gets removed in case
+  of proxy authentication.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-60] Fixed incorrect OSGi Symbolic-BundleName.
+  Contributed by M. van Cuijk <mark at phedny.net>
+
+
+
+Release 4.0
+-------------------
+
+This is the first stable (GA) release of Apache HttpAsyncClient 4.0. HttpAsyncClient is a library 
+for asynchronous client-side HTTP communication built on top of HttpCore NIO transport. 
+It is a complementary library to Apache HttpClient intended and optimized for special cases 
+whereby ability to scale to many thousands of concurrent connections is more important than 
+performance in terms of raw data throughput. 
+
+HttpAsyncClient 4.0 is designed to have similar APIs as Apache HttpClient 4.3 and a comparable
+feature set. In addition HttpAsyncClient provides full support for zero-copy file upload and 
+download operations. It presently does not support transparent content decompression and automatic 
+I/O error recovery. These features may be added in future releases.
+
+
+Changelog:
+-------------------
+
+* [HTTPASYNC-56] Fixed thread deadlock in DefaultClientExchangeHandlerImpl.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-57] HTTPS request execution via a HTTP proxy can cause an infinite loop.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-52] Proxy configuration set at the request level does not apply.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-51] Request producers do not get correctly reset if the server responds early 
+  (out of sequence), which can cause entity enclosing requests to be malformed in case of 
+  re-execution.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-45] CachingHttpAsyncClient to override Future returned by the backend.
+  Contributed by James Leigh <james at 3roundstones dot com>
+
+
+Release 4.0 Beta 4
+-------------------
+
+The 4.0 BETA4 release delivers significant performance improvements in request execution,
+especially for short HTTP messages, and also re-aligns programming interfaces used by the library 
+with HttpCore 4.3 and HttpClient 4.3 APIs. Configuration and preference APIs of HttpAsyncClient
+are now consistent with those used by HttpClient 4.3.
+
+Users of previous releases of HttpAsyncClient are advised to upgrade.
+
+Changelog:
+
+* [HTTPCLIENT-1353] 303 Redirects Should be Cacheable
+  Contributed by James Leigh <james at 3roundstones dot com>
+
+* Redesign of configuration and preference APIs. 
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* Redesign of connection management APIs.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-34] HttpAsyncClient fails to re-start request execution if the opposite end 
+  prematurely terminates persistent connection. 
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-28] PoolEntry's expiry information is never updated.
+  Contributed by Daniel Kulp <dkulp at apache.org>
+
+
+
+Release 4.0 Beta 3
+-------------------
+
+The 4.0 BETA3 is a maintenance release that picks up the latest bug fixes in the core components.
+
+Changelog:
+
+* Upgraded HttpCore to version 4.2.2
+
+* [HTTPASYNC-26] OSGi bundle import fix.
+  Contributed by Daniel Kulp <dkulp at apache.org>
+
+* [HTTPASYNC-25] AsyncSchemeRegistry instance set in the execution context takes precedence over
+  the default one.
+  Contributed by Daniel Kulp <dkulp at apache.org> 
+
+* Ported fix for HTTPCLIENT-1224
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+Release 4.0 Beta 2
+-------------------
+
+The 4.0 BETA2 release fixes a number of non-critical issues found since release 4.0-beta1 and 
+introduces basic support for HTTP/1.1 response caching. Please note that caching for streaming 
+HTTP exchanges is currently not supported.
+
+Users of previous releases of HttpAsyncClient are advised to upgrade.
+
+Changelog:
+
+* [HTTPASYNC-20] HTTP exchange can now be aborted with HttpUriRequest#abort().
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-17] Caching HttpAsyncClient facade.
+  Contributed by Clinton Nielsen <clinton.h.nielsen at gmail.com>
+
+* [HTTPASYNC-19] Fixed incorrect execution of message exchanges that span across multiple hosts 
+  (for instance, in case of a request redirect).  
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-18] Fixed thread deadlock that could occur in the DefaultAsyncRequestDirector 
+  in case of a runtime exception.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-15]: HttpAsyncClient hangs if connection leased from the pool gets immediately closed 
+  by the opposite endpoint.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+Release 4.0 Beta 1
+-------------------
+
+The 4.0 BETA1 release completes the application programming interface and the feature set 
+of HttpAsyncClient and upgrades to the latest versions of core and client components 
+(HttpCore 4.2-beta1 and HttpClient 4.2-beta1). As of this release HttpAsyncClient is expected
+to be API stable.
+
+Users of previous releases of HttpAsyncClient are advised to upgrade.
+
+Changelog:
+
+* [HTTPASYNC-11]: HttpAsyncClient fails to re-authenticate if connection is non-persistent.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+* [HTTPASYNC-5]: Allow customized LineParser for asynchronous connections.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+
+Release 4.0 Alpha 3
+-------------------
+
+The 4.0 ALPHA3 release largely completes the application programming interface and feature set 
+of HttpAsyncClient. While the API may still change in the course of the ALPHA development phase, 
+this is expected to be the last round of major API changes and the API is expected to be reasonably
+stable as of this release.
+
+We are kindly asking actual and prospective users of HttpAsyncClient to review its API, evaluate
+its functionality and give us feedback while the 4.0 API is still not final. If no major flaws 
+are discovered the 4.0 API is expected to be frozen with the next BETA release. 
+
+Changelog:
+
+* [HTTPASYNC-6]: Fixed NPE in the DefaultAsyncRequestDirector#responseCompleted method.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+Release 4.0 Alpha 2
+-------------------
+
+The second ALPHA release of HttpAsyncClient 4.0 comes with a number of important improvements and
+enhancements. As of this version HttpAsyncClient fully supports HTTP state management (cookies) 
+and HTTP authentication (basic, digest, NTLM, spnego/kerberos). Connection management classes have 
+been thoroughly reworked and improved. This version also improves support for zero copy file 
+upload / download operations.
+
+The HttpAsyncClient 4.0 API is still considered experimental and is expected to change 
+in the course of the ALPHA development phase.
+
+Please note that currently HttpAsyncClient DOES NOT support
+
+* Stateful HTTP connections
+
+
+Changelog:
+
+* [HTTPASYNC-3]: Fixed incorrect handling of expired I/O sessions by SessionPool.
+  Contributed by Oleg Kalnichevski <olegk at apache.org>
+
+
+Release 4.0 Alpha 1
+-------------------
+
+HttpAsyncClient is a library for asynchronous client-side HTTP communication built on HttpCore NIO
+and HttpClient components.
+
+HttpAsyncClient is NOT meant to replace or supersede Apache HttpClient. It is a complementary
+library to Apache HttpClient intended for special cases where ability to handle a great number of
+concurrent connections is more important than performance in terms of a raw data throughput or
+for those users who prefer event-driven APIs.
+
+The HttpAsyncClient 4.0 API is still very experimental and is expected to change in the course
+of the ALPHA development phase.
+
+Please note that currently HttpAsyncClient DOES NOT support
+
+* HTTP state management (cookies)
+* HTTP authentication
+* Stateful HTTP connections
diff --git a/4.1.x/doap_HttpComponents_AsyncClient.rdf b/4.1.x/doap_HttpComponents_AsyncClient.rdf
new file mode 100644
index 0000000..ea0c3b3
--- /dev/null
+++ b/4.1.x/doap_HttpComponents_AsyncClient.rdf
@@ -0,0 +1,128 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl"?>
+<rdf:RDF xml:lang="en"
+         xmlns="http://usefulinc.com/ns/doap#"
+         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+         xmlns:asfext="http://projects.apache.org/ns/asfext#"
+         xmlns:foaf="http://xmlns.com/foaf/0.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 rdf:about="http://hc.apache.org/httpcomponents-asyncclient/">
+    <created>2007-11-15</created>
+    <license rdf:resource="http://usefulinc.com/doap/licenses/asl20" />
+    <name>Apache HttpComponents AsyncClient</name>
+    <homepage rdf:resource="http://hc.apache.org/httpcomponents-asyncclient/" />
+    <asfext:pmc rdf:resource="http://httpcomponents.apache.org" />
+    <shortdesc>
+Java library implementing an asynchronous HTTP client based on HttpCore NIO and HttpClient
+components.
+    </shortdesc>
+    <description>
+HttpAsyncClient is a library for asynchronous client-side HTTP communication built on HttpCore NIO
+and HttpClient components. It is a complementary library to Apache HttpClient for special cases
+where ability to handle a great number of concurrent connections is more important than
+performance in terms of a raw data throughput.
+    </description>
+    <bug-database rdf:resource="http://issues.apache.org/jira/browse/HTTPASYNC" />
+    <mailing-list rdf:resource="http://httpcomponents.apache.org/mail-lists.html" />
+    <download-page rdf:resource="http://httpcomponents.apache.org/downloads.cgi" />
+    <programming-language>Java</programming-language>
+    <category rdf:resource="http://projects.apache.org/category/http" />
+    <category rdf:resource="http://projects.apache.org/category/library" />
+    <category rdf:resource="http://projects.apache.org/category/network-client" />
+
+    <!-- multiple releases can be listed, each in it's own section -->
+    <release>
+      <Version>
+        <name>httpcomponents-asyncclient-4.0-alpha1</name>
+        <created>2011-01-18</created>
+        <revision>4.0-alpha1</revision>
+      </Version>
+      <Version>
+        <name>httpcomponents-asyncclient-4.0-alpha2</name>
+        <created>2011-05-24</created>
+        <revision>4.0-alpha2</revision>
+      </Version>
+      <Version>
+        <name>httpcomponents-asyncclient-4.0-alpha3</name>
+        <created>2011-09-29</created>
+        <revision>4.0-alpha3</revision>
+      </Version>
+    </release>
+
+    <repository>
+      <SVNRepository>
+        <location rdf:resource="http://svn.apache.org/repos/asf/httpcomponents/httpasyncclient/trunk"/>
+        <browse rdf:resource="http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk"/>
+      </SVNRepository>
+    </repository>
+
+    <asfext:implements><asfext:Standard>
+      <asfext:title>Hypertext Transfer Protocol -- HTTP/1.1</asfext:title>
+      <asfext:body>IETF</asfext:body>
+      <asfext:id>RFC 2616</asfext:id>
+      <asfext:url rdf:resource="http://tools.ietf.org/html/rfc2616"/>
+    </asfext:Standard></asfext:implements>
+    <asfext:implements><asfext:Standard>
+      <asfext:title>Hypertext Transfer Protocol -- HTTP/1.0</asfext:title>
+      <asfext:body>IETF</asfext:body>
+      <asfext:id>RFC 1945</asfext:id>
+      <asfext:url rdf:resource="http://tools.ietf.org/html/rfc1945"/>
+    </asfext:Standard></asfext:implements>
+    <asfext:implements><asfext:Standard>
+      <asfext:title>Upgrading to TLS Within HTTP/1.1</asfext:title>
+      <asfext:body>IETF</asfext:body>
+      <asfext:id>RFC 2817</asfext:id>
+      <asfext:url rdf:resource="http://tools.ietf.org/html/rfc2817"/>
+    </asfext:Standard></asfext:implements>
+    <asfext:implements><asfext:Standard>
+      <asfext:title>HTTP Over TLS</asfext:title>
+      <asfext:body>IETF</asfext:body>
+      <asfext:id>RFC 2818</asfext:id>
+      <asfext:url rdf:resource="http://tools.ietf.org/html/rfc2818"/>
+    </asfext:Standard></asfext:implements>
+    <asfext:implements><asfext:Standard>
+      <asfext:title>HTTP Authentication: Basic and Digest Access Authentication</asfext:title>
+      <asfext:body>IETF</asfext:body>
+      <asfext:id>RFC 2617</asfext:id>
+      <asfext:url rdf:resource="http://tools.ietf.org/html/rfc2617"/>
+    </asfext:Standard></asfext:implements>
+    <asfext:implements><asfext:Standard>
+      <asfext:title>HTTP State Management Mechanism (Cookies)</asfext:title>
+      <asfext:body>IETF</asfext:body>
+      <asfext:id>RFC 2109</asfext:id>
+      <asfext:url rdf:resource="http://tools.ietf.org/html/rfc2109"/>
+    </asfext:Standard></asfext:implements>
+    <asfext:implements><asfext:Standard>
+      <asfext:title>HTTP State Management Mechanism (Cookie2)</asfext:title>
+      <asfext:body>IETF</asfext:body>
+      <asfext:id>RFC 2965</asfext:id>
+      <asfext:url rdf:resource="http://tools.ietf.org/html/rfc2965"/>
+    </asfext:Standard></asfext:implements>
+
+  </Project>
+</rdf:RDF>
diff --git a/4.1.x/httpasyncclient-cache/pom.xml b/4.1.x/httpasyncclient-cache/pom.xml
new file mode 100644
index 0000000..fca104b
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/pom.xml
@@ -0,0 +1,166 @@
+<?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-asyncclient</artifactId>
+    <version>4.1.4</version>
+  </parent>
+  <artifactId>httpasyncclient-cache</artifactId>
+  <name>Apache HttpAsyncClient Cache</name>
+  <description>
+   Apache HttpComponents AsyncClient Cache
+  </description>
+  <url>http://hc.apache.org/httpcomponents-asyncclient</url>
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpasyncclient</artifactId>
+      <version>${project.version}</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient-cache</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient-cache</artifactId>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</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>
+
+  <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-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>default-jar</id>
+            <phase>package</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive combine.children="append">
+                <manifestEntries>
+                  <Automatic-Module-Name>org.apache.httpcomponents.httpasyncclient.cache</Automatic-Module-Name>
+                </manifestEntries>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>${hc.javadoc.version}</version>
+        <configuration>
+          <source>${maven.compiler.source}</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-project-info-reports-plugin</artifactId>
+        <version>${hc.project-info.version}</version>
+        <inherited>false</inherited>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>dependencies</report>
+              <report>dependency-info</report>
+              <report>summary</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/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousAsyncValidationRequest.java b/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousAsyncValidationRequest.java
new file mode 100644
index 0000000..692440f
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousAsyncValidationRequest.java
@@ -0,0 +1,110 @@
+/*
+ * ====================================================================
+ * 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.util.concurrent.ExecutionException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolException;
+import org.apache.http.client.cache.HttpCacheContext;
+import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+
+/**
+ * Class used to represent an asynchronous revalidation event, such as with
+ * "stale-while-revalidate"
+ */
+class AsynchronousAsyncValidationRequest implements Runnable {
+    private final AsynchronousAsyncValidator parent;
+    private final CachingHttpAsyncClient cachingAsyncClient;
+    private final HttpHost target;
+    private final HttpRequestWrapper request;
+    private final HttpCacheContext clientContext;
+    private final HttpCacheEntry cacheEntry;
+    private final String identifier;
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    /**
+     * Used internally by {@link AsynchronousValidator} to schedule a
+     * revalidation.
+     */
+    AsynchronousAsyncValidationRequest(final AsynchronousAsyncValidator parent,
+            final CachingHttpAsyncClient cachingClient, final HttpHost target, final HttpRequestWrapper request,
+            final HttpCacheContext clientContext, final HttpCacheEntry cacheEntry, final String identifier) {
+        this.parent = parent;
+        this.cachingAsyncClient = cachingClient;
+        this.target = target;
+        this.request = request;
+        this.clientContext = clientContext;
+        this.cacheEntry = cacheEntry;
+        this.identifier = identifier;
+    }
+
+    @Override
+    public void run() {
+        try {
+            final FutureCallback<HttpResponse> callback = new FutureCallback<HttpResponse>() {
+
+                @Override
+                public void cancelled() {
+                }
+
+                @Override
+                public void completed(final HttpResponse httpResponse) {
+                }
+
+                @Override
+                public void failed(final Exception e) {
+                    log.debug("Asynchronous revalidation failed", e);
+                }
+            };
+            final BasicFuture<HttpResponse> future = new BasicFuture<HttpResponse>(callback);
+            this.cachingAsyncClient.revalidateCacheEntry(future, this.target, this.request,
+                    this.clientContext, this.cacheEntry);
+            future.get();
+        } catch (final ProtocolException pe) {
+            this.log.error("ProtocolException thrown during asynchronous revalidation", pe);
+        } catch (final ExecutionException e) {
+            this.log.error("Exception thrown during asynchronous revalidation", e.getCause());
+        } catch (final InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } finally {
+            this.parent.markComplete(this.identifier);
+        }
+    }
+
+    String getIdentifier() {
+        return this.identifier;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousAsyncValidator.java b/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousAsyncValidator.java
new file mode 100644
index 0000000..0d95fe2
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/AsynchronousAsyncValidator.java
@@ -0,0 +1,134 @@
+/*
+ * ====================================================================
+ * 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.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpHost;
+import org.apache.http.client.cache.HttpCacheContext;
+import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.methods.HttpRequestWrapper;
+
+/**
+ * Class used for asynchronous revalidations to be used when the "stale-
+ * while-revalidate" directive is present
+ */
+class AsynchronousAsyncValidator {
+    private final CachingHttpAsyncClient cachingAsyncClient;
+    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 CachingHttpAsyncClient}, and a {@link ThreadPoolExecutor}
+     * generated according to the thread 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()}.
+     */
+    public AsynchronousAsyncValidator(final CachingHttpAsyncClient cachingClient, final CacheConfig config) {
+        this(cachingClient, new ThreadPoolExecutor(config.getAsynchronousWorkersCore(),
+                config.getAsynchronousWorkersMax(), config.getAsynchronousWorkerIdleLifetimeSecs(),
+                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(
+                        config.getRevalidationQueueSize())));
+    }
+
+    /**
+     * Create AsynchronousValidator which will make revalidation requests using
+     * the supplied {@link CachingHttpAsyncClient} and {@link ExecutorService}.
+     *
+     * @param cachingClient
+     *            used to execute asynchronous requests
+     * @param executor
+     *            used to manage a thread pool of revalidation workers
+     */
+    AsynchronousAsyncValidator(final CachingHttpAsyncClient cachingClient, final ExecutorService executor) {
+        this.cachingAsyncClient = cachingClient;
+        this.executor = executor;
+        this.queued = new HashSet<String>();
+        this.cacheKeyGenerator = new CacheKeyGenerator();
+    }
+
+    /**
+     * Schedules an asynchronous revalidation
+     */
+    public synchronized void revalidateCacheEntry(final HttpHost target, final HttpRequestWrapper request,
+            final HttpCacheContext clientContext, final HttpCacheEntry entry) {
+        // getVariantURI will fall back on getURI if no variants exist
+        final String uri = this.cacheKeyGenerator.getVariantURI(target, request, entry);
+
+        if (!this.queued.contains(uri)) {
+            final AsynchronousAsyncValidationRequest asyncRevalidationRequest = new AsynchronousAsyncValidationRequest(
+                    this, this.cachingAsyncClient, target, request, clientContext, entry, uri);
+
+            try {
+                this.executor.execute(asyncRevalidationRequest);
+                this.queued.add(uri);
+            } catch (final RejectedExecutionException ree) {
+                this.log.debug("Revalidation for [" + uri + "] not scheduled: " + ree);
+            }
+        }
+    }
+
+    /**
+     * Removes an identifier from the internal list of revalidation jobs in
+     * progress. This is meant to be called by
+     * {@link AsynchronousValidationRequest#run()} once the revalidation is
+     * complete, using the identifier passed in during constructions.
+     */
+    synchronized void markComplete(final String identifier) {
+        this.queued.remove(identifier);
+    }
+
+    Set<String> getScheduledIdentifiers() {
+        return Collections.unmodifiableSet(this.queued);
+    }
+
+    ExecutorService getExecutor() {
+        return this.executor;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpAsyncClient.java b/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpAsyncClient.java
new file mode 100644
index 0000000..4a4bdce
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpAsyncClient.java
@@ -0,0 +1,1012 @@
+/*
+ * ====================================================================
+ * 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 java.net.URI;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+
+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.HttpHost;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.cache.CacheResponseStatus;
+import org.apache.http.client.cache.HeaderConstants;
+import org.apache.http.client.cache.HttpCacheContext;
+import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.cache.HttpCacheStorage;
+import org.apache.http.client.cache.ResourceFactory;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.client.utils.DateUtils;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.nio.client.HttpAsyncClient;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.nio.reactor.IOReactorException;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.Args;
+import org.apache.http.util.EntityUtils;
+import org.apache.http.util.VersionInfo;
+
+@Contract(threading = ThreadingBehavior.SAFE) // So long as the responseCache implementation is threadsafe
+public class CachingHttpAsyncClient implements HttpAsyncClient {
+
+    private final static boolean SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS = false;
+
+    private final AtomicLong cacheHits = new AtomicLong();
+    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 HttpAsyncClient backend;
+    private final HttpCache responseCache;
+    private final CacheValidityPolicy validityPolicy;
+    private final ResponseCachingPolicy responseCachingPolicy;
+    private final CachedHttpResponseGenerator responseGenerator;
+    private final CacheableRequestPolicy cacheableRequestPolicy;
+    private final CachedResponseSuitabilityChecker suitabilityChecker;
+
+    private final ConditionalRequestBuilder conditionalRequestBuilder;
+
+    private final long maxObjectSizeBytes;
+    private final boolean sharedCache;
+
+    private final ResponseProtocolCompliance responseCompliance;
+    private final RequestProtocolCompliance requestCompliance;
+
+    private final AsynchronousAsyncValidator asynchAsyncRevalidator;
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    CachingHttpAsyncClient(
+            final HttpAsyncClient client,
+            final HttpCache cache,
+            final CacheConfig config) {
+        super();
+        Args.notNull(client, "HttpClient");
+        Args.notNull(cache, "HttpCache");
+        Args.notNull(config, "CacheConfig");
+        this.maxObjectSizeBytes = config.getMaxObjectSize();
+        this.sharedCache = config.isSharedCache();
+        this.backend = client;
+        this.responseCache = cache;
+        this.validityPolicy = new CacheValidityPolicy();
+        this.responseCachingPolicy = new ResponseCachingPolicy(this.maxObjectSizeBytes, this.sharedCache, false, config.is303CachingEnabled());
+        this.responseGenerator = new CachedHttpResponseGenerator(this.validityPolicy);
+        this.cacheableRequestPolicy = new CacheableRequestPolicy();
+        this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy, config);
+        this.conditionalRequestBuilder = new ConditionalRequestBuilder();
+
+        this.responseCompliance = new ResponseProtocolCompliance();
+        this.requestCompliance = new RequestProtocolCompliance(config.isWeakETagOnPutDeleteAllowed());
+
+        this.asynchAsyncRevalidator = makeAsynchronousValidator(config);
+    }
+
+    public CachingHttpAsyncClient() throws IOReactorException {
+        this(HttpAsyncClients.createDefault(),
+                new BasicHttpCache(),
+                CacheConfig.DEFAULT);
+    }
+
+    public CachingHttpAsyncClient(final CacheConfig config) throws IOReactorException {
+        this(HttpAsyncClients.createDefault(),
+                new BasicHttpCache(config),
+                config);
+    }
+
+    public CachingHttpAsyncClient(final HttpAsyncClient client) {
+        this(client,
+                new BasicHttpCache(),
+                CacheConfig.DEFAULT);
+    }
+
+    public CachingHttpAsyncClient(final HttpAsyncClient client, final CacheConfig config) {
+        this(client,
+                new BasicHttpCache(config),
+                config);
+    }
+
+    public CachingHttpAsyncClient(
+            final HttpAsyncClient client,
+            final ResourceFactory resourceFactory,
+            final HttpCacheStorage storage,
+            final CacheConfig config) {
+        this(client,
+                new BasicHttpCache(resourceFactory, storage, config),
+                config);
+    }
+
+    public CachingHttpAsyncClient(
+            final HttpAsyncClient client,
+            final HttpCacheStorage storage,
+            final CacheConfig config) {
+        this(client,
+                new BasicHttpCache(new HeapResourceFactory(), storage, config),
+                config);
+    }
+
+    CachingHttpAsyncClient(
+            final HttpAsyncClient backend,
+            final CacheValidityPolicy validityPolicy,
+            final ResponseCachingPolicy responseCachingPolicy,
+            final HttpCache responseCache,
+            final CachedHttpResponseGenerator responseGenerator,
+            final CacheableRequestPolicy cacheableRequestPolicy,
+            final CachedResponseSuitabilityChecker suitabilityChecker,
+            final ConditionalRequestBuilder conditionalRequestBuilder,
+            final ResponseProtocolCompliance responseCompliance,
+            final RequestProtocolCompliance requestCompliance) {
+        final CacheConfig config = CacheConfig.DEFAULT;
+        this.maxObjectSizeBytes = config.getMaxObjectSize();
+        this.sharedCache = config.isSharedCache();
+        this.backend = backend;
+        this.validityPolicy = validityPolicy;
+        this.responseCachingPolicy = responseCachingPolicy;
+        this.responseCache = responseCache;
+        this.responseGenerator = responseGenerator;
+        this.cacheableRequestPolicy = cacheableRequestPolicy;
+        this.suitabilityChecker = suitabilityChecker;
+        this.conditionalRequestBuilder = conditionalRequestBuilder;
+        this.responseCompliance = responseCompliance;
+        this.requestCompliance = requestCompliance;
+        this.asynchAsyncRevalidator = makeAsynchronousValidator(config);
+    }
+
+    private AsynchronousAsyncValidator makeAsynchronousValidator(
+            final CacheConfig config) {
+        if (config.getAsynchronousWorkersMax() > 0) {
+            return new AsynchronousAsyncValidator(this, config);
+        }
+        return null;
+    }
+
+    /**
+     * 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
+     */
+    public long getCacheHits() {
+        return this.cacheHits.get();
+    }
+
+    /**
+     * Reports the number of times that the cache contacted the origin
+     * server because it had no appropriate response cached.
+     * @return the number of cache misses
+     */
+    public long getCacheMisses() {
+        return this.cacheMisses.get();
+    }
+
+    /**
+     * Reports the number of times that the cache was able to satisfy
+     * a response by revalidating an existing but stale cache entry.
+     * @return the number of cache revalidations
+     */
+    public long getCacheUpdates() {
+        return this.cacheUpdates.get();
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpHost target,
+            final HttpRequest request,
+            final FutureCallback<HttpResponse> callback) {
+        return execute(target, request, null, callback);
+    }
+
+    @Override
+    public <T> Future<T> execute(
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final FutureCallback<T> callback) {
+        return execute(requestProducer, responseConsumer, null, callback);
+    }
+
+    @Override
+    public <T> Future<T> execute(
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final HttpContext context,
+            final FutureCallback<T> callback) {
+        this.log.warn("CachingHttpAsyncClient does not support caching for streaming HTTP exchanges");
+        return this.backend.execute(requestProducer, responseConsumer, context, callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpUriRequest request,
+            final FutureCallback<HttpResponse> callback) {
+        return execute(request, null, callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpUriRequest request,
+            final HttpContext context,
+            final FutureCallback<HttpResponse> callback) {
+        final URI uri = request.getURI();
+        final HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
+        return execute(httpHost, request, context, callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpHost target,
+            final HttpRequest originalRequest,
+            final HttpContext context,
+            final FutureCallback<HttpResponse> futureCallback) {
+        final BasicFuture<HttpResponse> future = new BasicFuture<HttpResponse>(futureCallback);
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(originalRequest);
+        final HttpCacheContext clientContext = context != null ? HttpCacheContext.adapt(context) : HttpCacheContext.create();
+        // default response context
+        setResponseStatus(clientContext, CacheResponseStatus.CACHE_MISS);
+
+        final String via = generateViaHeader(request);
+
+        if (clientRequestsOurOptions(request)) {
+            setResponseStatus(clientContext, CacheResponseStatus.CACHE_MODULE_RESPONSE);
+            future.completed(new OptionsHttp11Response());
+            return future;
+        }
+
+        final HttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(
+                request, clientContext);
+        if (fatalErrorResponse != null) {
+            future.completed(fatalErrorResponse);
+            return future;
+        }
+
+        try {
+            this.requestCompliance.makeRequestCompliant(request);
+        } catch (final ClientProtocolException e) {
+            future.failed(e);
+            return future;
+        }
+        request.addHeader(HeaderConstants.VIA,via);
+
+        flushEntriesInvalidatedByRequest(target, request);
+
+        if (!this.cacheableRequestPolicy.isServableFromCache(request)) {
+            log.debug("Request is not servable from cache");
+            callBackend(future, target, request, clientContext);
+            return future;
+        }
+
+        final HttpCacheEntry entry = satisfyFromCache(target, request);
+        if (entry == null) {
+            log.debug("Cache miss");
+            handleCacheMiss(future, target, request, clientContext);
+        } else {
+            try {
+                handleCacheHit(future, target, request, clientContext, entry);
+            } catch (final IOException e) {
+                future.failed(e);
+            }
+        }
+        return future;
+    }
+
+    private void handleCacheHit(
+            final BasicFuture<HttpResponse> future,
+            final HttpHost target,
+            final HttpRequestWrapper request,
+            final HttpCacheContext clientContext,
+            final HttpCacheEntry entry) throws IOException {
+        recordCacheHit(target, request);
+        final HttpResponse out;
+        final Date now = getCurrentDate();
+        if (this.suitabilityChecker.canCachedResponseBeUsed(target, request, entry, now)) {
+            log.debug("Cache hit");
+            out = generateCachedResponse(request, clientContext, entry, now);
+        } else if (!mayCallBackend(request)) {
+            log.debug("Cache entry not suitable but only-if-cached requested");
+            out = generateGatewayTimeout(clientContext);
+        } else if (validityPolicy.isRevalidatable(entry)
+                && !(entry.getStatusCode() == HttpStatus.SC_NOT_MODIFIED
+                && !suitabilityChecker.isConditional(request))) {
+            log.debug("Revalidating cache entry");
+            revalidateCacheEntry(future, target, request, clientContext, entry, now);
+            return;
+        } else {
+            log.debug("Cache entry not usable; calling backend");
+            callBackend(future, target, request, clientContext);
+            return;
+        }
+        clientContext.setAttribute(HttpClientContext.HTTP_ROUTE, new HttpRoute(target));
+        clientContext.setAttribute(HttpClientContext.HTTP_TARGET_HOST, target);
+        clientContext.setAttribute(HttpClientContext.HTTP_REQUEST, request);
+        clientContext.setAttribute(HttpClientContext.HTTP_RESPONSE, out);
+        clientContext.setAttribute(HttpClientContext.HTTP_REQ_SENT, Boolean.TRUE);
+        future.completed(out);
+    }
+
+    private void revalidateCacheEntry(
+            final BasicFuture<HttpResponse> future,
+            final HttpHost target,
+            final HttpRequestWrapper request,
+            final HttpCacheContext clientContext,
+            final HttpCacheEntry entry,
+            final Date now) throws ClientProtocolException {
+
+        try {
+            if (this.asynchAsyncRevalidator != null
+                && !staleResponseNotAllowed(request, entry, now)
+                && this.validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) {
+                this.log.debug("Serving stale with asynchronous revalidation");
+                final HttpResponse resp = this.responseGenerator.generateResponse(request, entry);
+                resp.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
+
+                this.asynchAsyncRevalidator.revalidateCacheEntry(target, request, clientContext, entry);
+
+                future.completed(resp);
+                return;
+            }
+
+            final ChainedFutureCallback<HttpResponse> chainedFutureCallback = new ChainedFutureCallback<HttpResponse>(future) {
+
+                @Override
+                public void failed(final Exception ex) {
+                    if(ex instanceof IOException) {
+                        super.completed(handleRevalidationFailure(request, clientContext, entry, now));
+                    } else {
+                        super.failed(ex);
+                    }
+                }
+
+            };
+
+            final BasicFuture<HttpResponse> compositeFuture = new BasicFuture<HttpResponse>(chainedFutureCallback);
+            revalidateCacheEntry(compositeFuture, target, request, clientContext, entry);
+        } catch (final ProtocolException e) {
+            throw new ClientProtocolException(e);
+        }
+    }
+
+    private void handleCacheMiss(
+            final BasicFuture<HttpResponse> future,
+            final HttpHost target,
+            final HttpRequestWrapper request,
+            final HttpCacheContext clientContext) {
+        recordCacheMiss(target, request);
+
+        if (!mayCallBackend(request)) {
+            future.completed(new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout"));
+            return;
+        }
+
+        final Map<String, Variant> variants = getExistingCacheVariants(target, request);
+        if (variants != null && variants.size() > 0) {
+            negotiateResponseFromVariants(future, target, request, clientContext, variants);
+            return;
+        }
+
+        callBackend(future, target, request, clientContext);
+    }
+
+    private HttpCacheEntry satisfyFromCache(
+            final HttpHost target,
+            final HttpRequest request) {
+        HttpCacheEntry entry = null;
+        try {
+            entry = this.responseCache.getCacheEntry(target, request);
+        } catch (final IOException ioe) {
+            this.log.warn("Unable to retrieve entries from cache", ioe);
+        }
+        return entry;
+    }
+
+    private HttpResponse getFatallyNoncompliantResponse(
+            final HttpRequest request,
+            final HttpCacheContext clientContext) {
+        HttpResponse fatalErrorResponse = null;
+        final List<RequestProtocolError> fatalError = this.requestCompliance.requestIsFatallyNonCompliant(request);
+
+        for (final RequestProtocolError error : fatalError) {
+            setResponseStatus(clientContext, CacheResponseStatus.CACHE_MODULE_RESPONSE);
+            fatalErrorResponse = this.requestCompliance.getErrorForRequest(error);
+        }
+        return fatalErrorResponse;
+    }
+
+    private Map<String, Variant> getExistingCacheVariants(
+            final HttpHost target,
+            final HttpRequest request) {
+        Map<String,Variant> variants = null;
+        try {
+            variants = this.responseCache.getVariantCacheEntriesWithEtags(target, request);
+        } catch (final IOException ioe) {
+            this.log.warn("Unable to retrieve variant entries from cache", ioe);
+        }
+        return variants;
+    }
+
+    private void recordCacheMiss(final HttpHost target, final HttpRequest request) {
+        this.cacheMisses.getAndIncrement();
+        if (this.log.isDebugEnabled()) {
+            final RequestLine rl = request.getRequestLine();
+            this.log.debug("Cache miss [host: " + target + "; uri: " + rl.getUri() + "]");
+        }
+    }
+
+    private void recordCacheHit(final HttpHost target, final HttpRequest request) {
+        this.cacheHits.getAndIncrement();
+        if (this.log.isDebugEnabled()) {
+            final RequestLine rl = request.getRequestLine();
+            this.log.debug("Cache hit [host: " + target + "; uri: " + rl.getUri() + "]");
+        }
+    }
+
+    private void recordCacheUpdate(final HttpCacheContext clientContext) {
+        this.cacheUpdates.getAndIncrement();
+        setResponseStatus(clientContext, CacheResponseStatus.VALIDATED);
+    }
+
+    private void flushEntriesInvalidatedByRequest(final HttpHost target,
+            final HttpRequest request) {
+        try {
+            this.responseCache.flushInvalidatedCacheEntriesFor(target, request);
+        } catch (final IOException ioe) {
+            this.log.warn("Unable to flush invalidated entries from cache", ioe);
+        }
+    }
+
+    private HttpResponse generateCachedResponse(
+            final HttpRequestWrapper request,
+            final HttpCacheContext clientContext,
+            final HttpCacheEntry entry,
+            final Date now) {
+        final HttpResponse cachedResponse;
+        if (request.containsHeader(HeaderConstants.IF_NONE_MATCH)
+                || request.containsHeader(HeaderConstants.IF_MODIFIED_SINCE)) {
+            cachedResponse = this.responseGenerator.generateNotModifiedResponse(entry);
+        } else {
+            cachedResponse = this.responseGenerator.generateResponse(request, entry);
+        }
+        setResponseStatus(clientContext, CacheResponseStatus.CACHE_HIT);
+        if (this.validityPolicy.getStalenessSecs(entry, now) > 0L) {
+            cachedResponse.addHeader("Warning","110 localhost \"Response is stale\"");
+        }
+        return cachedResponse;
+    }
+
+    private HttpResponse handleRevalidationFailure(
+            final HttpRequestWrapper request,
+            final HttpCacheContext clientContext,
+            final HttpCacheEntry entry,
+            final Date now) {
+        if (staleResponseNotAllowed(request, entry, now)) {
+            return generateGatewayTimeout(clientContext);
+        }
+        return unvalidatedCacheHit(clientContext, request, entry);
+    }
+
+    private HttpResponse generateGatewayTimeout(final HttpCacheContext clientContext) {
+        setResponseStatus(clientContext, CacheResponseStatus.CACHE_MODULE_RESPONSE);
+        return new BasicHttpResponse(HttpVersion.HTTP_1_1,
+                HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout");
+    }
+
+    private HttpResponse unvalidatedCacheHit(
+            final HttpCacheContext clientContext,
+            final HttpRequestWrapper request,
+            final HttpCacheEntry entry) {
+        final HttpResponse cachedResponse = this.responseGenerator.generateResponse(request, entry);
+        setResponseStatus(clientContext, CacheResponseStatus.CACHE_HIT);
+        cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\"");
+        return cachedResponse;
+    }
+
+    private boolean staleResponseNotAllowed(
+            final HttpRequest request,
+            final HttpCacheEntry entry,
+            final Date now) {
+        return this.validityPolicy.mustRevalidate(entry)
+            || (isSharedCache() && this.validityPolicy.proxyRevalidate(entry))
+            || explicitFreshnessRequest(request, entry, now);
+    }
+
+    private boolean mayCallBackend(final HttpRequest request) {
+        for (final Header h: request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
+            for (final HeaderElement elt : h.getElements()) {
+                if ("only-if-cached".equals(elt.getName())) {
+                    this.log.debug("Request marked only-if-cached");
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private boolean explicitFreshnessRequest(
+            final HttpRequest request,
+            final HttpCacheEntry entry,
+            final Date now) {
+        for(final Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) {
+            for(final HeaderElement elt : h.getElements()) {
+                if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) {
+                    try {
+                        final int maxstale = Integer.parseInt(elt.getValue());
+                        final long age = this.validityPolicy.getCurrentAgeSecs(entry, now);
+                        final long lifetime = this.validityPolicy.getFreshnessLifetimeSecs(entry);
+                        if (age - lifetime > maxstale) {
+                            return true;
+                        }
+                    } catch (final NumberFormatException nfe) {
+                        return true;
+                    }
+                } 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(final HttpMessage msg) {
+
+        final ProtocolVersion pv = msg.getProtocolVersion();
+        final 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 String value;
+        if ("http".equalsIgnoreCase(pv.getProtocol())) {
+            value = String.format("%d.%d localhost (Apache-HttpClient/%s (cache))", pv.getMajor(), pv.getMinor(),
+                    release);
+        } else {
+            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 HttpCacheContext clientContext, final CacheResponseStatus value) {
+        if (clientContext != null) {
+            clientContext.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, value);
+        }
+    }
+
+    /**
+     * Reports whether this {@code CachingHttpClient} implementation
+     * supports byte-range requests as specified by the {@code Range}
+     * and {@code Content-Range} headers.
+     * @return {@code true} if byte-range requests are supported
+     */
+    public boolean supportsRangeAndContentRangeHeaders() {
+        return SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS;
+    }
+
+    /**
+     * Reports whether this {@code CachingHttpClient} is configured as
+     * a shared (public) or non-shared (private) cache. See {@link
+     * CacheConfig#setSharedCache(boolean)}.
+     * @return {@code true} if we are behaving as a shared (public)
+     *   cache
+     */
+    public boolean isSharedCache() {
+        return this.sharedCache;
+    }
+
+    Date getCurrentDate() {
+        return new Date();
+    }
+
+    boolean clientRequestsOurOptions(final HttpRequest request) {
+        final RequestLine line = request.getRequestLine();
+
+        if (!HeaderConstants.OPTIONS_METHOD.equals(line.getMethod())) {
+            return false;
+        }
+
+        if (!"*".equals(line.getUri())) {
+            return false;
+        }
+        return "0".equals(request.getFirstHeader(HeaderConstants.MAX_FORWARDS).getValue());
+    }
+
+    void callBackend(
+            final BasicFuture<HttpResponse> future,
+            final HttpHost target,
+            final HttpRequestWrapper request,
+            final HttpCacheContext clientContext) {
+        final Date requestDate = getCurrentDate();
+        this.log.trace("Calling the backend");
+
+        final ChainedFutureCallback<HttpResponse> chainedFutureCallback = new ChainedFutureCallback<HttpResponse>(future) {
+
+            @Override
+            public void completed(final HttpResponse httpResponse) {
+                httpResponse.addHeader(HeaderConstants.VIA, generateViaHeader(httpResponse));
+                try {
+                    final CloseableHttpResponse backendResponse = handleBackendResponse(
+                            target, request, requestDate, getCurrentDate(),
+                            Proxies.enhanceResponse(httpResponse));
+                    super.completed(backendResponse);
+                } catch (final IOException e) {
+                    super.failed(e);
+                }
+
+            }
+
+        };
+        this.backend.execute(target, request, clientContext, chainedFutureCallback);
+    }
+
+    private boolean revalidationResponseIsTooOld(
+            final HttpResponse backendResponse,
+            final HttpCacheEntry cacheEntry) {
+        final Header entryDateHeader = cacheEntry.getFirstHeader(HTTP.DATE_HEADER);
+        final Header responseDateHeader = backendResponse.getFirstHeader(HTTP.DATE_HEADER);
+        if (entryDateHeader != null && responseDateHeader != null) {
+            final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
+            final Date respDate = DateUtils.parseDate(responseDateHeader.getValue());
+            if (respDate != null && respDate.before(entryDate)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void negotiateResponseFromVariants(
+            final BasicFuture<HttpResponse> future,
+            final HttpHost target,
+            final HttpRequestWrapper request,
+            final HttpCacheContext clientContext,
+            final Map<String, Variant> variants) {
+        final HttpRequest conditionalRequest = this.conditionalRequestBuilder.buildConditionalRequestFromVariants(request, variants);
+
+        final Date requestDate = getCurrentDate();
+
+        final ChainedFutureCallback<HttpResponse> chainedFutureCallback = new ChainedFutureCallback<HttpResponse>(future) {
+
+            @Override
+            public void completed(final HttpResponse httpResponse) {
+                final Date responseDate = getCurrentDate();
+
+                httpResponse.addHeader(HeaderConstants.VIA, generateViaHeader(httpResponse));
+
+                if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_NOT_MODIFIED) {
+                    try {
+                        future.completed(handleBackendResponse(
+                                target, request, requestDate, responseDate,
+                                Proxies.enhanceResponse(httpResponse)));
+                        return;
+                    } catch (final IOException e) {
+                        future.failed(e);
+                        return;
+                    }
+                }
+
+                final Header resultEtagHeader = httpResponse.getFirstHeader(HeaderConstants.ETAG);
+                if (resultEtagHeader == null) {
+                    CachingHttpAsyncClient.this.log.warn("304 response did not contain ETag");
+                    callBackend(future, target, request, clientContext);
+                    return;
+                }
+
+                final String resultEtag = resultEtagHeader.getValue();
+                final Variant matchingVariant = variants.get(resultEtag);
+                if (matchingVariant == null) {
+                    CachingHttpAsyncClient.this.log.debug("304 response did not contain ETag matching one sent in If-None-Match");
+                    callBackend(future, target, request, clientContext);
+                }
+
+                final HttpCacheEntry matchedEntry = matchingVariant.getEntry();
+
+                if (revalidationResponseIsTooOld(httpResponse, matchedEntry)) {
+                    EntityUtils.consumeQuietly(httpResponse.getEntity());
+                    retryRequestUnconditionally(future, target, request, clientContext, matchedEntry);
+                    return;
+                }
+
+                recordCacheUpdate(clientContext);
+
+                final HttpCacheEntry responseEntry = getUpdatedVariantEntry(target,
+                        conditionalRequest, requestDate, responseDate, httpResponse,
+                        matchingVariant, matchedEntry);
+
+                final HttpResponse resp = CachingHttpAsyncClient.this.responseGenerator.generateResponse(request, responseEntry);
+                tryToUpdateVariantMap(target, request, matchingVariant);
+
+                if (shouldSendNotModifiedResponse(request, responseEntry)) {
+                    future.completed(CachingHttpAsyncClient.this.responseGenerator.generateNotModifiedResponse(responseEntry));
+                    return;
+                }
+
+                future.completed(resp);
+            }
+
+        };
+
+        this.backend.execute(target, conditionalRequest, clientContext, chainedFutureCallback);
+    }
+
+    private void retryRequestUnconditionally(
+            final BasicFuture<HttpResponse> future,
+            final HttpHost target,
+            final HttpRequestWrapper request,
+            final HttpCacheContext clientContext,
+            final HttpCacheEntry matchedEntry) {
+        final HttpRequestWrapper unconditional = this.conditionalRequestBuilder
+            .buildUnconditionalRequest(request, matchedEntry);
+        callBackend(future, target, unconditional, clientContext);
+    }
+
+    private HttpCacheEntry getUpdatedVariantEntry(
+            final HttpHost target,
+            final HttpRequest conditionalRequest,
+            final Date requestDate,
+            final Date responseDate,
+            final HttpResponse backendResponse,
+            final Variant matchingVariant,
+            final HttpCacheEntry matchedEntry) {
+        HttpCacheEntry responseEntry = matchedEntry;
+        try {
+            responseEntry = this.responseCache.updateVariantCacheEntry(target, conditionalRequest,
+                    matchedEntry, backendResponse, requestDate, responseDate, matchingVariant.getCacheKey());
+        } catch (final IOException ioe) {
+            this.log.warn("Could not update cache entry", ioe);
+        }
+        return responseEntry;
+    }
+
+    private void tryToUpdateVariantMap(
+            final HttpHost target,
+            final HttpRequest request,
+            final Variant matchingVariant) {
+        try {
+            this.responseCache.reuseVariantEntryFor(target, request, matchingVariant);
+        } catch (final IOException ioe) {
+            this.log.warn("Could not update cache entry to reuse variant", ioe);
+        }
+    }
+
+    private boolean shouldSendNotModifiedResponse(
+            final HttpRequest request,
+            final HttpCacheEntry responseEntry) {
+        return (this.suitabilityChecker.isConditional(request)
+                && this.suitabilityChecker.allConditionalsMatch(request, responseEntry, new Date()));
+    }
+
+    void revalidateCacheEntry(
+            final BasicFuture<HttpResponse> future,
+            final HttpHost target,
+            final HttpRequestWrapper request,
+            final HttpCacheContext clientContext,
+            final HttpCacheEntry cacheEntry) throws ProtocolException {
+
+        final HttpRequestWrapper conditionalRequest = this.conditionalRequestBuilder.buildConditionalRequest(request, cacheEntry);
+        final Date requestDate = getCurrentDate();
+
+        final ChainedFutureCallback<HttpResponse> chainedFutureCallback = new ChainedFutureCallback<HttpResponse>(future) {
+
+            @Override
+            public void completed(final HttpResponse httpResponse) {
+                final Date responseDate = getCurrentDate();
+
+                if (revalidationResponseIsTooOld(httpResponse, cacheEntry)) {
+                    final HttpRequest unconditional = CachingHttpAsyncClient.this.conditionalRequestBuilder.buildUnconditionalRequest(request, cacheEntry);
+                    final Date innerRequestDate = getCurrentDate();
+
+                    final ChainedFutureCallback<HttpResponse> chainedFutureCallback2 = new ChainedFutureCallback<HttpResponse>(future) {
+
+                        @Override
+                        public void completed(final HttpResponse innerHttpResponse) {
+                            final Date innerResponseDate = getCurrentDate();
+                            revalidateCacheEntryCompleted(future,
+                                    target, request, clientContext, cacheEntry,
+                                    conditionalRequest, innerRequestDate,
+                                    innerHttpResponse, innerResponseDate);
+                        }
+
+                    };
+                    CachingHttpAsyncClient.this.backend.execute(
+                            target, unconditional, clientContext, chainedFutureCallback2);
+                }
+
+                revalidateCacheEntryCompleted(future,
+                        target, request, clientContext, cacheEntry,
+                        conditionalRequest, requestDate,
+                        httpResponse, responseDate);
+            }
+
+        };
+
+        this.backend.execute(target, conditionalRequest, clientContext, chainedFutureCallback);
+    }
+
+    private void revalidateCacheEntryCompleted(
+            final BasicFuture<HttpResponse> future,
+            final HttpHost target,
+            final HttpRequestWrapper request,
+            final HttpCacheContext clientContext,
+            final HttpCacheEntry cacheEntry,
+            final HttpRequestWrapper conditionalRequest,
+            final Date requestDate,
+            final HttpResponse httpResponse,
+            final Date responseDate) {
+
+        httpResponse.addHeader(HeaderConstants.VIA, generateViaHeader(httpResponse));
+
+        final int statusCode = httpResponse.getStatusLine().getStatusCode();
+        if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) {
+            recordCacheUpdate(clientContext);
+        }
+
+        if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
+            final HttpCacheEntry updatedEntry;
+            try {
+                updatedEntry = CachingHttpAsyncClient.this.responseCache.updateCacheEntry(target, request, cacheEntry,
+                        httpResponse, requestDate, responseDate);
+            } catch (final IOException e) {
+                future.failed(e);
+                return;
+            }
+            if (CachingHttpAsyncClient.this.suitabilityChecker.isConditional(request)
+                    && CachingHttpAsyncClient.this.suitabilityChecker.allConditionalsMatch(request, updatedEntry, new Date())) {
+                future.completed(CachingHttpAsyncClient.this.responseGenerator.generateNotModifiedResponse(updatedEntry));
+                return;
+            }
+            future.completed(CachingHttpAsyncClient.this.responseGenerator.generateResponse(request, updatedEntry));
+            return;
+        }
+
+        if (staleIfErrorAppliesTo(statusCode)
+            && !staleResponseNotAllowed(request, cacheEntry, getCurrentDate())
+            && CachingHttpAsyncClient.this.validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) {
+            final HttpResponse cachedResponse = CachingHttpAsyncClient.this.responseGenerator.generateResponse(request, cacheEntry);
+            cachedResponse.addHeader(HeaderConstants.WARNING, "110 localhost \"Response is stale\"");
+            future.completed(cachedResponse);
+            return;
+        }
+
+        try {
+            final CloseableHttpResponse backendResponse = handleBackendResponse(
+                    target, conditionalRequest, requestDate, responseDate,
+                    Proxies.enhanceResponse(httpResponse));
+            future.completed(backendResponse);
+        } catch (final IOException e) {
+            future.failed(e);
+        }
+    }
+
+    private boolean staleIfErrorAppliesTo(final int statusCode) {
+        return statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR
+                || statusCode == HttpStatus.SC_BAD_GATEWAY
+                || statusCode == HttpStatus.SC_SERVICE_UNAVAILABLE
+                || statusCode == HttpStatus.SC_GATEWAY_TIMEOUT;
+    }
+
+    CloseableHttpResponse handleBackendResponse(
+            final HttpHost target,
+            final HttpRequestWrapper request,
+            final Date requestDate,
+            final Date responseDate,
+            final CloseableHttpResponse backendResponse) throws IOException {
+
+        this.log.debug("Handling Backend response");
+        this.responseCompliance.ensureProtocolCompliance(request, backendResponse);
+
+        final boolean cacheable = this.responseCachingPolicy.isResponseCacheable(request, backendResponse);
+        this.responseCache.flushInvalidatedCacheEntriesFor(target, request, backendResponse);
+        if (cacheable &&
+            !alreadyHaveNewerCacheEntry(target, request, backendResponse)) {
+            storeRequestIfModifiedSinceFor304Response(request, backendResponse);
+            return this.responseCache.cacheAndReturnResponse(target, request, backendResponse, requestDate,
+                    responseDate);
+        }
+        if (!cacheable) {
+            try {
+                this.responseCache.flushCacheEntriesFor(target, request);
+            } catch (final IOException ioe) {
+                this.log.warn("Unable to flush invalid cache entries", ioe);
+            }
+        }
+        return backendResponse;
+    }
+
+    /**
+     * For 304 Not modified responses, adds a "Last-Modified" header with the
+     * value of the "If-Modified-Since" header passed in the request. This
+     * header is required to be able to reuse match the cache entry for
+     * subsequent requests but as defined in http specifications it is not
+     * included in 304 responses by backend servers. This header will not be
+     * included in the resulting response.
+     */
+    private void storeRequestIfModifiedSinceFor304Response(
+            final HttpRequest request,
+            final HttpResponse backendResponse) {
+        if (backendResponse.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED) {
+            final Header h = request.getFirstHeader("If-Modified-Since");
+            if (h != null) {
+                backendResponse.addHeader("Last-Modified", h.getValue());
+            }
+        }
+    }
+
+    private boolean alreadyHaveNewerCacheEntry(
+            final HttpHost target,
+            final HttpRequest request,
+            final HttpResponse backendResponse) {
+        HttpCacheEntry existing = null;
+        try {
+            existing = this.responseCache.getCacheEntry(target, request);
+        } catch (final IOException ioe) {
+            // nop
+        }
+        if (existing == null) {
+            return false;
+        }
+        final Header entryDateHeader = existing.getFirstHeader(HTTP.DATE_HEADER);
+        if (entryDateHeader == null) {
+            return false;
+        }
+        final Header responseDateHeader = backendResponse.getFirstHeader(HTTP.DATE_HEADER);
+        if (responseDateHeader == null) {
+            return false;
+        }
+        final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
+        final Date responseDate = DateUtils.parseDate(responseDateHeader.getValue());
+        return responseDate != null && responseDate.before(entryDate);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/ChainedFutureCallback.java b/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/ChainedFutureCallback.java
new file mode 100644
index 0000000..b813b11
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/ChainedFutureCallback.java
@@ -0,0 +1,55 @@
+/*
+ * ====================================================================
+ * 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 org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+
+class ChainedFutureCallback<T> implements FutureCallback<T> {
+
+    private final BasicFuture<T> wrapped;
+
+    public ChainedFutureCallback(final BasicFuture<T> delegate) {
+        this.wrapped = delegate;
+    }
+
+    @Override
+    public void completed(final T result) {
+        this.wrapped.completed(result);
+    }
+
+    @Override
+    public void failed(final Exception ex) {
+        this.wrapped.failed(ex);
+    }
+
+    @Override
+    public void cancelled() {
+        this.wrapped.cancel();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/package-info.java b/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/package-info.java
new file mode 100644
index 0000000..91931ad
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/main/java/org/apache/http/impl/client/cache/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * ====================================================================
+ * 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/>.
+ *
+ */
+
+/**
+ * HTTP/1.1 client-side caching.
+ */
+package org.apache.http.impl.client.cache;
diff --git a/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/CachingHttpAsyncClientExecChain.java b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/CachingHttpAsyncClientExecChain.java
new file mode 100644
index 0000000..23000f4
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/CachingHttpAsyncClientExecChain.java
@@ -0,0 +1,135 @@
+/*
+ * ====================================================================
+ * 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 java.lang.reflect.UndeclaredThrowableException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpExecutionAware;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.execchain.ClientExecChain;
+
+public class CachingHttpAsyncClientExecChain implements ClientExecChain {
+
+    private final CachingHttpAsyncClient client;
+
+    public CachingHttpAsyncClientExecChain(final ClientExecChain backend) {
+        this(backend, new BasicHttpCache(), CacheConfig.DEFAULT);
+    }
+
+    public CachingHttpAsyncClientExecChain(
+            final ClientExecChain backend,
+            final HttpCache cache,
+            final CacheConfig config) {
+        this.client = new CachingHttpAsyncClient(
+                new ClientExecChainAsyncClient(backend), cache, config);
+    }
+
+    CachingHttpAsyncClientExecChain(
+            final ClientExecChain backend, final HttpCache responseCache,
+            final CacheValidityPolicy validityPolicy,
+            final ResponseCachingPolicy responseCachingPolicy,
+            final CachedHttpResponseGenerator responseGenerator,
+            final CacheableRequestPolicy cacheableRequestPolicy,
+            final CachedResponseSuitabilityChecker suitabilityChecker,
+            final ConditionalRequestBuilder conditionalRequestBuilder,
+            final ResponseProtocolCompliance responseCompliance,
+            final RequestProtocolCompliance requestCompliance) {
+        this.client = new CachingHttpAsyncClient(
+                new ClientExecChainAsyncClient(backend), validityPolicy,
+                responseCachingPolicy, responseCache, responseGenerator,
+                cacheableRequestPolicy, suitabilityChecker,
+                conditionalRequestBuilder, responseCompliance,
+                requestCompliance);
+    }
+
+    public boolean supportsRangeAndContentRangeHeaders() {
+        return client.supportsRangeAndContentRangeHeaders();
+    }
+
+    public CloseableHttpResponse execute(
+            final HttpRoute route,
+            final HttpRequestWrapper request) throws IOException, HttpException {
+        return execute(route, request, HttpClientContext.create(), null);
+    }
+
+    public CloseableHttpResponse execute(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context) throws IOException, HttpException {
+        return execute(route, request, context, null);
+    }
+
+    @Override
+    public CloseableHttpResponse execute(
+            final HttpRoute route,
+            final HttpRequestWrapper request,
+            final HttpClientContext context,
+            final HttpExecutionAware execAware) throws IOException, HttpException {
+        try {
+            final Future<HttpResponse> future = client.execute(route.getTargetHost(), request, context, null);
+            return Proxies.enhanceResponse(future.get());
+        } catch (final InterruptedException e) {
+            Thread.currentThread().interrupt();
+            return null;
+        } catch (final ExecutionException e) {
+            try {
+                throw e.getCause();
+            } catch (final IOException ex) {
+                throw ex;
+            } catch (final HttpException ex) {
+                throw ex;
+            } catch (final RuntimeException ex) {
+                throw ex;
+            } catch (final Error ex) {
+                throw ex;
+            } catch (final Throwable ex) {
+                throw new UndeclaredThrowableException(ex);
+            }
+        }
+    }
+
+    public long getCacheHits() {
+        return client.getCacheHits();
+    }
+
+    public long getCacheMisses() {
+        return client.getCacheMisses();
+    }
+
+    public long getCacheUpdates() {
+        return client.getCacheUpdates();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/ClientExecChainAsyncClient.java b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/ClientExecChainAsyncClient.java
new file mode 100644
index 0000000..5e6d451
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/ClientExecChainAsyncClient.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.cache;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.execchain.ClientExecChain;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.nio.conn.ClientAsyncConnectionManager;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.nio.reactor.IOReactorStatus;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+
+public class ClientExecChainAsyncClient extends CloseableHttpAsyncClient {
+
+    private final ClientExecChain backend;
+
+    public ClientExecChainAsyncClient(final ClientExecChain backend) {
+        super();
+        this.backend = backend;
+    }
+
+    @Override
+    public void start() {
+        // no-op
+    }
+
+    @Override
+    public boolean isRunning() {
+        return true;
+    }
+
+    public void shutdown() throws InterruptedException {
+        // no-op
+    }
+
+    public IOReactorStatus getStatus() {
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    public ClientAsyncConnectionManager getConnectionManager() {
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    public HttpParams getParams() {
+        return null;
+    }
+
+    @Override
+    public void close() throws IOException {
+        // no-op
+    }
+
+    @Override
+    public <T> Future<T> execute(
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final HttpContext context,
+            final FutureCallback<T> callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpHost target,
+            final HttpRequest request,
+            final HttpContext context,
+            final FutureCallback<HttpResponse> callback) {
+        final BasicFuture<HttpResponse> future = new BasicFuture<HttpResponse>(
+                callback);
+        try {
+            final HttpResponse result = backend.execute(new HttpRoute(target),
+                    HttpRequestWrapper.wrap(request),
+                    HttpClientContext.adapt(context), null);
+            future.completed(result);
+        } catch (final IOException e) {
+            future.failed(e);
+        } catch (final HttpException e) {
+            future.failed(e);
+        }
+        return future;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsyncProtocolAllowedBehavior.java b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsyncProtocolAllowedBehavior.java
new file mode 100644
index 0000000..dcba732
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsyncProtocolAllowedBehavior.java
@@ -0,0 +1,52 @@
+/*
+ * ====================================================================
+ * 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 org.apache.http.impl.execchain.ClientExecChain;
+
+/**
+ * This class tests behavior that is allowed (MAY) by the HTTP/1.1 protocol specification and for
+ * which we have implemented the behavior in the caching HTTP client.
+ */
+public class TestAsyncProtocolAllowedBehavior extends TestProtocolAllowedBehavior {
+
+    @Override
+    protected ClientExecChain createCachingExecChain(
+            final ClientExecChain backend,
+            final HttpCache cache,
+            final CacheConfig config) {
+        return new CachingHttpAsyncClientExecChain(backend, cache, config);
+    }
+
+    @Override
+    protected boolean supportsRangeAndContentRangeHeaders(final ClientExecChain impl) {
+        return impl instanceof CachingHttpAsyncClientExecChain
+                && ((CachingHttpAsyncClientExecChain) impl)
+                .supportsRangeAndContentRangeHeaders();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsyncProtocolDeviations.java b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsyncProtocolDeviations.java
new file mode 100644
index 0000000..72f22d7
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsyncProtocolDeviations.java
@@ -0,0 +1,52 @@
+/*
+ * ====================================================================
+ * 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 org.apache.http.impl.execchain.ClientExecChain;
+
+/**
+ * We are a conditionally-compliant HTTP/1.1 client with a cache. However, a lot of the rules for
+ * proxies apply to us, as far as proper operation of the requests that pass through us. Generally
+ * speaking, we want to make sure that any response returned from our HttpClient.execute() methods
+ * is conditionally compliant with the rules for an HTTP/1.1 server, and that any requests we pass
+ * downstream to the backend HttpClient are are conditionally compliant with the rules for an
+ * HTTP/1.1 client.
+ * <p>
+ * There are some cases where strictly behaving as a compliant caching proxy would result in strange
+ * behavior, since we're attached as part of a client and are expected to be a drop-in replacement.
+ * The test cases captured here document the places where we differ from the HTTP RFC.
+ */
+public class TestAsyncProtocolDeviations extends TestProtocolDeviations {
+
+    @Override
+    protected ClientExecChain createCachingExecChain(
+            final ClientExecChain backend,
+            final HttpCache cache,
+            final CacheConfig config) {
+        return new CachingHttpAsyncClientExecChain(backend, cache, config);
+    }
+}
diff --git a/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsyncProtocolRequirements.java b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsyncProtocolRequirements.java
new file mode 100644
index 0000000..fc814df
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestAsyncProtocolRequirements.java
@@ -0,0 +1,55 @@
+/*
+ * ====================================================================
+ * 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 org.apache.http.impl.execchain.ClientExecChain;
+
+/**
+ * We are a conditionally-compliant HTTP/1.1 client with a cache. However, a lot of the rules for
+ * proxies apply to us, as far as proper operation of the requests that pass through us. Generally
+ * speaking, we want to make sure that any response returned from our HttpClient.execute() methods
+ * is conditionally compliant with the rules for an HTTP/1.1 server, and that any requests we pass
+ * downstream to the backend HttpClient are are conditionally compliant with the rules for an
+ * HTTP/1.1 client.
+ */
+public class TestAsyncProtocolRequirements extends TestProtocolRequirements {
+
+    @Override
+    protected ClientExecChain createCachingExecChain(
+            final ClientExecChain backend,
+            final HttpCache cache,
+            final CacheConfig config) {
+        return new CachingHttpAsyncClientExecChain(backend, cache, config);
+    }
+
+    @Override
+    protected boolean supportsRangeAndContentRangeHeaders(final ClientExecChain impl) {
+        return impl instanceof CachingHttpAsyncClientExecChain
+                && ((CachingHttpAsyncClientExecChain) impl)
+                .supportsRangeAndContentRangeHeaders();
+    }
+}
diff --git a/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpAsyncClient.java b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpAsyncClient.java
new file mode 100644
index 0000000..fda6696
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpAsyncClient.java
@@ -0,0 +1,69 @@
+/*
+ * ====================================================================
+ * 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 org.apache.http.impl.execchain.ClientExecChain;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class TestCachingHttpAsyncClient extends TestCachingExecChain {
+
+    @Override
+    public ClientExecChain createCachingExecChain(
+            final ClientExecChain backend,
+            final HttpCache responseCache,
+            final CacheValidityPolicy validityPolicy,
+            final ResponseCachingPolicy responseCachingPolicy,
+            final CachedHttpResponseGenerator responseGenerator,
+            final CacheableRequestPolicy cacheableRequestPolicy,
+            final CachedResponseSuitabilityChecker suitabilityChecker,
+            final ConditionalRequestBuilder conditionalRequestBuilder,
+            final ResponseProtocolCompliance responseCompliance,
+            final RequestProtocolCompliance requestCompliance,
+            final CacheConfig config,
+            final AsynchronousValidator asynchRevalidator) {
+        return new CachingHttpAsyncClientExecChain(backend, responseCache, validityPolicy,
+                responseCachingPolicy, responseGenerator,
+                cacheableRequestPolicy, suitabilityChecker,
+                conditionalRequestBuilder, responseCompliance,
+                requestCompliance);
+    }
+
+    @Override
+    public ClientExecChain createCachingExecChain(
+            final ClientExecChain backend,
+            final HttpCache cache,
+            final CacheConfig config) {
+        return new CachingHttpAsyncClientExecChain(backend, cache, config);
+    }
+
+    @Override @Test @Ignore // TODO: virtual host support is presently broken
+    public void testUsesVirtualHostForCacheKey() throws Exception {
+        super.testUsesVirtualHostForCacheKey();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpAsyncCacheJiraNumber1147.java b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpAsyncCacheJiraNumber1147.java
new file mode 100644
index 0000000..98c3ec5
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/test/java/org/apache/http/impl/client/cache/TestHttpAsyncCacheJiraNumber1147.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;
+
+import org.apache.http.impl.execchain.ClientExecChain;
+
+public class TestHttpAsyncCacheJiraNumber1147 extends TestHttpCacheJiraNumber1147 {
+
+    @Override
+    protected ClientExecChain createCachingExecChain(
+            final ClientExecChain backend,
+            final BasicHttpCache cache,
+            final CacheConfig config) {
+        return new CachingHttpAsyncClientExecChain(backend, cache, config);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient-cache/src/test/resources/commons-logging.properties b/4.1.x/httpasyncclient-cache/src/test/resources/commons-logging.properties
new file mode 100644
index 0000000..2fa1b3b
--- /dev/null
+++ b/4.1.x/httpasyncclient-cache/src/test/resources/commons-logging.properties
@@ -0,0 +1,26 @@
+# ====================================================================
+# 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/>.
+
+# Disable logging for unit tests
+org.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog
diff --git a/4.1.x/httpasyncclient-osgi/pom.xml b/4.1.x/httpasyncclient-osgi/pom.xml
new file mode 100644
index 0000000..e0b6cc4
--- /dev/null
+++ b/4.1.x/httpasyncclient-osgi/pom.xml
@@ -0,0 +1,168 @@
+<?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-asyncclient</artifactId>
+    <version>4.1.4</version>
+  </parent>
+  <artifactId>httpasyncclient-osgi</artifactId>
+  <name>Apache HttpAsyncClient OSGi bundle</name>
+  <description>
+   Apache HttpComponents AsyncClient (OSGi bundle)
+  </description>
+  <url>http://hc.apache.org/httpcomponents-asyncclient</url>
+  <packaging>bundle</packaging>
+
+  <properties>
+    <httpcore.osgi.import.version>"[4.4.0, 4.5.0)"</httpcore.osgi.import.version>
+    <httpclient.osgi.import.version>"[4.5.0, 4.6.0)"</httpclient.osgi.import.version>
+    <commons-logging.osgi.import.version>"[1.1.0, 1.3.0)"</commons-logging.osgi.import.version>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpasyncclient</artifactId>
+      <version>${project.version}</version>
+      <scope>compile</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Name>Apache ${project.name}</Bundle-Name>
+            <Bundle-SymbolicName>${project.groupId}.httpasyncclient</Bundle-SymbolicName>
+            <_exportcontents>
+            org.apache.http.nio.client.*;version=${project.version},
+            org.apache.http.nio.conn.*;version=${project.version},
+            org.apache.http.impl.nio.client.*;version=${project.version},
+            org.apache.http.impl.nio.conn.*;version=${project.version}
+            </_exportcontents>
+            <Embed-Dependency>*;scope=compile|runtime;inline=true</Embed-Dependency>
+            <Import-Package>
+            javax.crypto,
+            javax.crypto.spec,
+            javax.net.ssl,
+            javax.security.auth.x500,
+            org.ietf.jgss,
+            org.apache.commons.logging;version=${commons-logging.osgi.import.version},
+            org.apache.http;version=${httpcore.osgi.import.version},
+            org.apache.http.entity;version=${httpcore.osgi.import.version},
+            org.apache.http.concurrent;version=${httpcore.osgi.import.version},
+            org.apache.http.config;version=${httpcore.osgi.import.version},
+            org.apache.http.io;version=${httpcore.osgi.import.version},
+            org.apache.http.message;version=${httpcore.osgi.import.version},
+            org.apache.http.params;version=${httpcore.osgi.import.version},
+            org.apache.http.pool;version=${httpcore.osgi.import.version},
+            org.apache.http.protocol;version=${httpcore.osgi.import.version},
+            org.apache.http.util;version=${httpcore.osgi.import.version},
+            org.apache.http.impl;version=${httpcore.osgi.import.version},
+            org.apache.http.nio;version=${httpcore.osgi.import.version},
+            org.apache.http.nio.entity;version=${httpcore.osgi.import.version},
+            org.apache.http.nio.params;version=${httpcore.osgi.import.version},
+            org.apache.http.nio.pool;version=${httpcore.osgi.import.version},
+            org.apache.http.nio.protocol;version=${httpcore.osgi.import.version},
+            org.apache.http.nio.reactor;version=${httpcore.osgi.import.version},
+            org.apache.http.nio.reactor.ssl;version=${httpcore.osgi.import.version},
+            org.apache.http.nio.util;version=${httpcore.osgi.import.version},
+            org.apache.http.impl.nio.codecs;version=${httpcore.osgi.import.version},
+            org.apache.http.impl.nio;version=${httpcore.osgi.import.version},
+            org.apache.http.impl.nio.reactor;version=${httpcore.osgi.import.version},
+            org.apache.http.auth;version=${httpclient.osgi.import.version},
+            org.apache.http.cookie;version=${httpclient.osgi.import.version},
+            org.apache.http.conn;version=${httpclient.osgi.import.version},
+            org.apache.http.conn.params;version=${httpclient.osgi.import.version},
+            org.apache.http.conn.routing;version=${httpclient.osgi.import.version},
+            org.apache.http.conn.ssl;version=${httpclient.osgi.import.version},
+            org.apache.http.conn.util;version=${httpclient.osgi.import.version},
+            org.apache.http.client;version=${httpclient.osgi.import.version},
+            org.apache.http.client.config;version=${httpclient.osgi.import.version},
+            org.apache.http.client.methods;version=${httpclient.osgi.import.version},
+            org.apache.http.client.params;version=${httpclient.osgi.import.version},
+            org.apache.http.client.protocol;version=${httpclient.osgi.import.version},
+            org.apache.http.client.utils;version=${httpclient.osgi.import.version},
+            org.apache.http.impl.auth;version=${httpclient.osgi.import.version},
+            org.apache.http.impl.cookie;version=${httpclient.osgi.import.version},
+            org.apache.http.impl.conn;version=${httpclient.osgi.import.version},
+            org.apache.http.impl.client;version=${httpclient.osgi.import.version},
+            org.apache.http.impl.execchain;version=${httpclient.osgi.import.version},
+            org.apache.http.ssl;version=${httpcore.osgi.import.version},
+            *
+            </Import-Package>
+            <Include-Resource />
+            <!-- Stop the JAVA_1_n_HOME variables from being treated as headers by Bnd -->
+            <_removeheaders>JAVA_1_3_HOME,JAVA_1_4_HOME</_removeheaders>
+          </instructions>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+    </plugins>
+    <finalName>org.apache.httpcomponents.httpasyncclient_${project.version}</finalName>
+  </build>
+
+  <reporting>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+        <version>${hc.clirr.version}</version>
+        <configuration>
+          <skip>true</skip>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-project-info-reports-plugin</artifactId>
+        <version>${hc.project-info.version}</version>
+        <inherited>false</inherited>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>dependencies</report>
+              <report>dependency-info</report>
+              <report>summary</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+
+    </plugins>
+  </reporting>
+
+</project>
diff --git a/4.1.x/httpasyncclient/pom.xml b/4.1.x/httpasyncclient/pom.xml
new file mode 100644
index 0000000..77f91d1
--- /dev/null
+++ b/4.1.x/httpasyncclient/pom.xml
@@ -0,0 +1,184 @@
+<?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-asyncclient</artifactId>
+    <version>4.1.4</version>
+  </parent>
+  <artifactId>httpasyncclient</artifactId>
+  <name>Apache HttpAsyncClient</name>
+  <description>
+   Apache HttpComponents AsyncClient
+  </description>
+  <url>http://hc.apache.org/httpcomponents-asyncclient</url>
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpcore-nio</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.httpcomponents</groupId>
+      <artifactId>httpclient</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-logging</groupId>
+      <artifactId>commons-logging</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+        <includes>
+            <include>**/*.properties</include>
+        </includes>
+      </resource>
+    </resources>
+    <plugins>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>build-helper-maven-plugin</artifactId>
+        <version>1.8</version>
+        <executions>
+          <execution>
+          <id>add-source</id>
+          <phase>generate-sources</phase>
+          <goals>
+            <goal>add-source</goal>
+          </goals>
+          <configuration>
+            <sources>
+              <source>src/main/java-deprecated</source>
+            </sources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>default-jar</id>
+            <phase>package</phase>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive combine.children="append">
+                <manifestEntries>
+                  <Automatic-Module-Name>org.apache.httpcomponents.httpasyncclient</Automatic-Module-Name>
+                </manifestEntries>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <version>${hc.javadoc.version}</version>
+        <configuration>
+          <source>${maven.compiler.source}</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-project-info-reports-plugin</artifactId>
+        <version>${hc.project-info.version}</version>
+        <inherited>false</inherited>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>dependencies</report>
+              <report>dependency-info</report>
+              <report>summary</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/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientAuthentication.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientAuthentication.java
new file mode 100644
index 0000000..bcbaae2
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientAuthentication.java
@@ -0,0 +1,67 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+
+/**
+ * A simple example that uses HttpClient to execute an HTTP request against
+ * a target site that requires user authentication.
+ */
+public class AsyncClientAuthentication {
+
+    public static void main(String[] args) throws Exception {
+        CredentialsProvider credsProvider = new BasicCredentialsProvider();
+        credsProvider.setCredentials(
+                new AuthScope("httpbin.org", 80),
+                new UsernamePasswordCredentials("user", "passwd"));
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
+                .setDefaultCredentialsProvider(credsProvider)
+                .build();
+        try {
+            httpclient.start();
+            HttpGet httpget = new HttpGet("http://httpbin.org/basic-auth/user/passwd");
+
+            System.out.println("Executing request " + httpget.getRequestLine());
+            Future<HttpResponse> future = httpclient.execute(httpget, null);
+            HttpResponse response = future.get();
+            System.out.println("Response: " + response.getStatusLine());
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+    }
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientConfiguration.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientConfiguration.java
new file mode 100644
index 0000000..6fcc43d
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientConfiguration.java
@@ -0,0 +1,280 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.charset.CodingErrorAction;
+import java.util.Arrays;
+import java.util.concurrent.Future;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.Consts;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.ParseException;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.config.AuthSchemes;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.config.ConnectionConfig;
+import org.apache.http.config.MessageConstraints;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.DnsResolver;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.ssl.SSLContexts;
+import org.apache.http.impl.DefaultHttpResponseFactory;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.conn.SystemDefaultDnsResolver;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.impl.nio.codecs.DefaultHttpRequestWriterFactory;
+import org.apache.http.impl.nio.codecs.DefaultHttpResponseParser;
+import org.apache.http.impl.nio.codecs.DefaultHttpResponseParserFactory;
+import org.apache.http.impl.nio.conn.ManagedNHttpClientConnectionFactory;
+import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
+import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
+import org.apache.http.impl.nio.reactor.IOReactorConfig;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicLineParser;
+import org.apache.http.message.LineParser;
+import org.apache.http.nio.NHttpMessageParser;
+import org.apache.http.nio.NHttpMessageParserFactory;
+import org.apache.http.nio.NHttpMessageWriterFactory;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.nio.conn.NHttpConnectionFactory;
+import org.apache.http.nio.conn.NoopIOSessionStrategy;
+import org.apache.http.nio.conn.SchemeIOSessionStrategy;
+import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
+import org.apache.http.nio.reactor.ConnectingIOReactor;
+import org.apache.http.nio.reactor.SessionInputBuffer;
+import org.apache.http.nio.util.HeapByteBufferAllocator;
+import org.apache.http.util.CharArrayBuffer;
+
+/**
+ * This example demonstrates how to customize and configure the most common aspects
+ * of HTTP request execution and connection management.
+ */
+public class AsyncClientConfiguration {
+
+    public final static void main(String[] args) throws Exception {
+
+        // Use custom message parser / writer to customize the way HTTP
+        // messages are parsed from and written out to the data stream.
+        NHttpMessageParserFactory<HttpResponse> responseParserFactory = new DefaultHttpResponseParserFactory() {
+
+            @Override
+            public NHttpMessageParser<HttpResponse> create(
+                    final SessionInputBuffer buffer,
+                    final MessageConstraints constraints) {
+                LineParser lineParser = new BasicLineParser() {
+
+                    @Override
+                    public Header parseHeader(final CharArrayBuffer buffer) {
+                        try {
+                            return super.parseHeader(buffer);
+                        } catch (ParseException ex) {
+                            return new BasicHeader(buffer.toString(), null);
+                        }
+                    }
+
+                };
+                return new DefaultHttpResponseParser(
+                        buffer, lineParser, DefaultHttpResponseFactory.INSTANCE, constraints);
+            }
+
+        };
+        NHttpMessageWriterFactory<HttpRequest> requestWriterFactory = new DefaultHttpRequestWriterFactory();
+
+        // Use a custom connection factory to customize the process of
+        // initialization of outgoing HTTP connections. Beside standard connection
+        // configuration parameters HTTP connection factory can define message
+        // parser / writer routines to be employed by individual connections.
+        NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory = new ManagedNHttpClientConnectionFactory(
+                requestWriterFactory, responseParserFactory, HeapByteBufferAllocator.INSTANCE);
+
+        // Client HTTP connection objects when fully initialized can be bound to
+        // an arbitrary network socket. The process of network socket initialization,
+        // its connection to a remote address and binding to a local one is controlled
+        // by a connection socket factory.
+
+        // SSL context for secure connections can be created either based on
+        // system or application specific properties.
+        SSLContext sslcontext = SSLContexts.createSystemDefault();
+        // Use custom hostname verifier to customize SSL hostname verification.
+        HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier();
+
+        // Create a registry of custom connection session strategies for supported
+        // protocol schemes.
+        Registry<SchemeIOSessionStrategy> sessionStrategyRegistry = RegistryBuilder.<SchemeIOSessionStrategy>create()
+            .register("http", NoopIOSessionStrategy.INSTANCE)
+            .register("https", new SSLIOSessionStrategy(sslcontext, hostnameVerifier))
+            .build();
+
+        // Use custom DNS resolver to override the system DNS resolution.
+        DnsResolver dnsResolver = new SystemDefaultDnsResolver() {
+
+            @Override
+            public InetAddress[] resolve(final String host) throws UnknownHostException {
+                if (host.equalsIgnoreCase("myhost")) {
+                    return new InetAddress[] { InetAddress.getByAddress(new byte[] {127, 0, 0, 1}) };
+                } else {
+                    return super.resolve(host);
+                }
+            }
+
+        };
+
+        // Create I/O reactor configuration
+        IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
+                .setIoThreadCount(Runtime.getRuntime().availableProcessors())
+                .setConnectTimeout(30000)
+                .setSoTimeout(30000)
+                .build();
+
+        // Create a custom I/O reactort
+        ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);
+
+        // Create a connection manager with custom configuration.
+        PoolingNHttpClientConnectionManager connManager = new PoolingNHttpClientConnectionManager(
+                ioReactor, connFactory, sessionStrategyRegistry, dnsResolver);
+
+        // Create message constraints
+        MessageConstraints messageConstraints = MessageConstraints.custom()
+            .setMaxHeaderCount(200)
+            .setMaxLineLength(2000)
+            .build();
+        // Create connection configuration
+        ConnectionConfig connectionConfig = ConnectionConfig.custom()
+            .setMalformedInputAction(CodingErrorAction.IGNORE)
+            .setUnmappableInputAction(CodingErrorAction.IGNORE)
+            .setCharset(Consts.UTF_8)
+            .setMessageConstraints(messageConstraints)
+            .build();
+        // Configure the connection manager to use connection configuration either
+        // by default or for a specific host.
+        connManager.setDefaultConnectionConfig(connectionConfig);
+        connManager.setConnectionConfig(new HttpHost("somehost", 80), ConnectionConfig.DEFAULT);
+
+        // Configure total max or per route limits for persistent connections
+        // that can be kept in the pool or leased by the connection manager.
+        connManager.setMaxTotal(100);
+        connManager.setDefaultMaxPerRoute(10);
+        connManager.setMaxPerRoute(new HttpRoute(new HttpHost("somehost", 80)), 20);
+
+        // Use custom cookie store if necessary.
+        CookieStore cookieStore = new BasicCookieStore();
+        // Use custom credentials provider if necessary.
+        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(new AuthScope("localhost", 8889), new UsernamePasswordCredentials("squid", "nopassword"));
+        // Create global request configuration
+        RequestConfig defaultRequestConfig = RequestConfig.custom()
+            .setCookieSpec(CookieSpecs.DEFAULT)
+            .setExpectContinueEnabled(true)
+            .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
+            .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
+            .build();
+
+        // Create an HttpClient with the given custom dependencies and configuration.
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
+            .setConnectionManager(connManager)
+            .setDefaultCookieStore(cookieStore)
+            .setDefaultCredentialsProvider(credentialsProvider)
+            .setProxy(new HttpHost("localhost", 8889))
+            .setDefaultRequestConfig(defaultRequestConfig)
+            .build();
+
+        try {
+            HttpGet httpget = new HttpGet("http://httpbin.org/get");
+            // Request configuration can be overridden at the request level.
+            // They will take precedence over the one set at the client level.
+            RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig)
+                .setSocketTimeout(5000)
+                .setConnectTimeout(5000)
+                .setConnectionRequestTimeout(5000)
+                .setProxy(new HttpHost("localhost", 8888))
+                .build();
+            httpget.setConfig(requestConfig);
+
+            // Execution context can be customized locally.
+            HttpClientContext localContext = HttpClientContext.create();
+            // Contextual attributes set the local context level will take
+            // precedence over those set at the client level.
+            localContext.setCookieStore(cookieStore);
+            localContext.setCredentialsProvider(credentialsProvider);
+
+            System.out.println("Executing request " + httpget.getRequestLine());
+
+            httpclient.start();
+
+            // Pass local context as a parameter
+            Future<HttpResponse> future = httpclient.execute(httpget, localContext, null);
+
+            // Please note that it may be unsafe to access HttpContext instance
+            // while the request is still being executed
+
+            HttpResponse response = future.get();
+            System.out.println("Response: " + response.getStatusLine());
+
+            // Once the request has been executed the local context can
+            // be used to examine updated state and various objects affected
+            // by the request execution.
+
+            // Last executed request
+            localContext.getRequest();
+            // Execution route
+            localContext.getHttpRoute();
+            // Target auth state
+            localContext.getTargetAuthState();
+            // Proxy auth state
+            localContext.getTargetAuthState();
+            // Cookie origin
+            localContext.getCookieOrigin();
+            // Cookie spec used
+            localContext.getCookieSpec();
+            // User security token
+            localContext.getUserToken();
+        } finally {
+            httpclient.close();
+        }
+    }
+
+}
+
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientCustomContext.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientCustomContext.java
new file mode 100644
index 0000000..43da521
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientCustomContext.java
@@ -0,0 +1,89 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.List;
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+
+/**
+ * This example demonstrates the use of a local HTTP context populated with
+ * custom attributes.
+ */
+public class AsyncClientCustomContext {
+
+    public final static void main(String[] args) throws Exception {
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
+        try {
+            httpclient.start();
+
+            // Create a local instance of cookie store
+            CookieStore cookieStore = new BasicCookieStore();
+
+            // Create local HTTP context
+            HttpClientContext localContext = HttpClientContext.create();
+            // Bind custom cookie store to the local context
+            localContext.setCookieStore(cookieStore);
+
+            BasicClientCookie cookie = new BasicClientCookie("stuff", "important");
+            cookie.setDomain("httpbin.org");
+            cookie.setPath("/");
+            cookieStore.addCookie(cookie);
+
+            HttpGet httpget = new HttpGet("http://httpbin.org/cookies");
+            System.out.println("Executing request " + httpget.getRequestLine());
+
+            // Pass local context as a parameter
+            Future<HttpResponse> future = httpclient.execute(httpget, localContext, null);
+
+            // Please note that it may be unsafe to access HttpContext instance
+            // while the request is still being executed
+
+            HttpResponse response = future.get();
+            System.out.println("Response: " + response.getStatusLine());
+            List<Cookie> cookies = cookieStore.getCookies();
+            for (int i = 0; i < cookies.size(); i++) {
+                System.out.println("Local cookie: " + cookies.get(i));
+            }
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+    }
+
+}
+
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientCustomSSL.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientCustomSSL.java
new file mode 100644
index 0000000..4def409
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientCustomSSL.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.examples.nio.client;
+
+import java.util.concurrent.Future;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
+import org.apache.http.ssl.SSLContexts;
+
+/**
+ * This example demonstrates how to create secure connections with a custom SSL
+ * context.
+ */
+public class AsyncClientCustomSSL {
+
+    public final static void main(String[] args) throws Exception {
+        // Trust standard CAs and all self-signed certs
+        SSLContext sslcontext = SSLContexts.custom()
+                .loadTrustMaterial(new TrustSelfSignedStrategy())
+                .build();
+        // Allow TLSv1 protocol only
+        SSLIOSessionStrategy sslSessionStrategy = new SSLIOSessionStrategy(
+                sslcontext,
+                new String[] { "TLSv1" },
+                null,
+                SSLIOSessionStrategy.getDefaultHostnameVerifier());
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
+                .setSSLStrategy(sslSessionStrategy)
+                .build();
+        try {
+            httpclient.start();
+            HttpGet httpget = new HttpGet("https://httpbin.org/");
+            Future<HttpResponse> future = httpclient.execute(httpget, null);
+            HttpResponse response = future.get();
+            System.out.println("Response: " + response.getStatusLine());
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+        System.out.println("Done");
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientEvictExpiredConnections.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientEvictExpiredConnections.java
new file mode 100644
index 0000000..95adf13
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientEvictExpiredConnections.java
@@ -0,0 +1,145 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
+import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.reactor.ConnectingIOReactor;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Example demonstrating how to evict expired and idle connections
+ * from the connection pool.
+ */
+public class AsyncClientEvictExpiredConnections {
+
+    public static void main(String[] args) throws Exception {
+        ConnectingIOReactor ioReactor = new DefaultConnectingIOReactor();
+        PoolingNHttpClientConnectionManager cm = new PoolingNHttpClientConnectionManager(ioReactor);
+        cm.setMaxTotal(100);
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
+                .setConnectionManager(cm)
+                .build();
+        try {
+            httpclient.start();
+
+            // create an array of URIs to perform GETs on
+            String[] urisToGet = {
+                "http://hc.apache.org/",
+                "http://hc.apache.org/httpcomponents-core-ga/",
+                "http://hc.apache.org/httpcomponents-client-ga/",
+            };
+
+            IdleConnectionEvictor connEvictor = new IdleConnectionEvictor(cm);
+            connEvictor.start();
+
+            final CountDownLatch latch = new CountDownLatch(urisToGet.length);
+            for (final String uri: urisToGet) {
+                final HttpGet httpget = new HttpGet(uri);
+                httpclient.execute(httpget, new FutureCallback<HttpResponse>() {
+
+                    @Override
+                    public void completed(final HttpResponse response) {
+                        latch.countDown();
+                        System.out.println(httpget.getRequestLine() + "->" + response.getStatusLine());
+                    }
+
+                    @Override
+                    public void failed(final Exception ex) {
+                        latch.countDown();
+                        System.out.println(httpget.getRequestLine() + "->" + ex);
+                    }
+
+                    @Override
+                    public void cancelled() {
+                        latch.countDown();
+                        System.out.println(httpget.getRequestLine() + " cancelled");
+                    }
+
+                });
+            }
+            latch.await();
+
+            // Sleep 10 sec and let the connection evictor do its job
+            Thread.sleep(20000);
+
+            // Shut down the evictor thread
+            connEvictor.shutdown();
+            connEvictor.join();
+
+        } finally {
+            httpclient.close();
+        }
+    }
+
+    public static class IdleConnectionEvictor extends Thread {
+
+        private final NHttpClientConnectionManager connMgr;
+
+        private volatile boolean shutdown;
+
+        public IdleConnectionEvictor(NHttpClientConnectionManager connMgr) {
+            super();
+            this.connMgr = connMgr;
+        }
+
+        @Override
+        public void run() {
+            try {
+                while (!shutdown) {
+                    synchronized (this) {
+                        wait(5000);
+                        // Close expired connections
+                        connMgr.closeExpiredConnections();
+                        // Optionally, close connections
+                        // that have been idle longer than 5 sec
+                        connMgr.closeIdleConnections(5, TimeUnit.SECONDS);
+                    }
+                }
+            } catch (InterruptedException ex) {
+                // terminate
+            }
+        }
+
+        public void shutdown() {
+            shutdown = true;
+            synchronized (this) {
+                notifyAll();
+            }
+        }
+
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientExecuteProxy.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientExecuteProxy.java
new file mode 100644
index 0000000..c51fde8
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientExecuteProxy.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.examples.nio.client;
+
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+
+/**
+ * This example demonstrates a basic asynchronous HTTP request / response exchange
+ * via an HTTP proxy.
+ */
+public class AsyncClientExecuteProxy {
+
+    public static void main(String[] args)throws Exception {
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
+        try {
+            httpclient.start();
+            HttpHost proxy = new HttpHost("localhost", 8888);
+            RequestConfig config = RequestConfig.custom()
+                    .setProxy(proxy)
+                    .build();
+            HttpGet request = new HttpGet("https://httpbin.org/");
+            request.setConfig(config);
+            Future<HttpResponse> future = httpclient.execute(request, null);
+            HttpResponse response = future.get();
+            System.out.println("Response: " + response.getStatusLine());
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientHttpExchange.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientHttpExchange.java
new file mode 100644
index 0000000..62ecc26
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientHttpExchange.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.examples.nio.client;
+
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+
+/**
+ * This example demonstrates a basic asynchronous HTTP request / response exchange.
+ * Response content is buffered in memory for simplicity.
+ */
+public class AsyncClientHttpExchange {
+
+    public static void main(final String[] args) throws Exception {
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
+        try {
+            httpclient.start();
+            HttpGet request = new HttpGet("http://httpbin.org/get");
+            Future<HttpResponse> future = httpclient.execute(request, null);
+            HttpResponse response = future.get();
+            System.out.println("Response: " + response.getStatusLine());
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+        System.out.println("Done");
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientHttpExchangeFutureCallback.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientHttpExchangeFutureCallback.java
new file mode 100644
index 0000000..9044860
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientHttpExchangeFutureCallback.java
@@ -0,0 +1,90 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.concurrent.CountDownLatch;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+
+/**
+ * This example demonstrates a fully asynchronous execution of multiple HTTP exchanges
+ * where the result of an individual operation is reported using a callback interface.
+ */
+public class AsyncClientHttpExchangeFutureCallback {
+
+    public static void main(final String[] args) throws Exception {
+        RequestConfig requestConfig = RequestConfig.custom()
+            .setSocketTimeout(3000)
+            .setConnectTimeout(3000).build();
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
+            .setDefaultRequestConfig(requestConfig)
+            .build();
+        try {
+            httpclient.start();
+            final HttpGet[] requests = new HttpGet[] {
+                    new HttpGet("http://httpbin.org/ip"),
+                    new HttpGet("https://httpbin.org/ip"),
+                    new HttpGet("http://httpbin.org/headers")
+            };
+            final CountDownLatch latch = new CountDownLatch(requests.length);
+            for (final HttpGet request: requests) {
+                httpclient.execute(request, new FutureCallback<HttpResponse>() {
+
+                    @Override
+                    public void completed(final HttpResponse response) {
+                        latch.countDown();
+                        System.out.println(request.getRequestLine() + "->" + response.getStatusLine());
+                    }
+
+                    @Override
+                    public void failed(final Exception ex) {
+                        latch.countDown();
+                        System.out.println(request.getRequestLine() + "->" + ex);
+                    }
+
+                    @Override
+                    public void cancelled() {
+                        latch.countDown();
+                        System.out.println(request.getRequestLine() + " cancelled");
+                    }
+
+                });
+            }
+            latch.await();
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+        System.out.println("Done");
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientHttpExchangeStreaming.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientHttpExchangeStreaming.java
new file mode 100644
index 0000000..8174006
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientHttpExchangeStreaming.java
@@ -0,0 +1,91 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.client.methods.AsyncCharConsumer;
+import org.apache.http.nio.client.methods.HttpAsyncMethods;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * This example demonstrates an asynchronous HTTP request / response exchange with
+ * a full content streaming.
+ */
+public class AsyncClientHttpExchangeStreaming {
+
+    public static void main(final String[] args) throws Exception {
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
+        try {
+            httpclient.start();
+            Future<Boolean> future = httpclient.execute(
+                    HttpAsyncMethods.createGet("http://httpbin.org/"),
+                    new MyResponseConsumer(), null);
+            Boolean result = future.get();
+            if (result != null && result.booleanValue()) {
+                System.out.println("Request successfully executed");
+            } else {
+                System.out.println("Request failed");
+            }
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+        System.out.println("Done");
+    }
+
+    static class MyResponseConsumer extends AsyncCharConsumer<Boolean> {
+
+        @Override
+        protected void onResponseReceived(final HttpResponse response) {
+        }
+
+        @Override
+        protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
+            while (buf.hasRemaining()) {
+                System.out.print(buf.get());
+            }
+        }
+
+        @Override
+        protected void releaseResources() {
+        }
+
+        @Override
+        protected Boolean buildResult(final HttpContext context) {
+            return Boolean.TRUE;
+        }
+
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientPipelined.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientPipelined.java
new file mode 100644
index 0000000..c02c0b1
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientPipelined.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.examples.nio.client;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.nio.client.CloseableHttpPipeliningClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+
+/**
+ * This example demonstrates a pipelinfed execution of multiple HTTP request / response exchanges
+ * Response content is buffered in memory for simplicity.
+ */
+public class AsyncClientPipelined {
+
+    public static void main(final String[] args) throws Exception {
+        CloseableHttpPipeliningClient httpclient = HttpAsyncClients.createPipelining();
+        try {
+            httpclient.start();
+
+            HttpHost targetHost = new HttpHost("httpbin.org", 80);
+            HttpGet[] resquests = {
+                    new HttpGet("/"),
+                    new HttpGet("/ip"),
+                    new HttpGet("/headers"),
+                    new HttpGet("/get")
+            };
+
+            Future<List<HttpResponse>> future = httpclient.execute(targetHost,
+                    Arrays.<HttpRequest>asList(resquests), null);
+            List<HttpResponse> responses = future.get();
+            for (HttpResponse response: responses) {
+                System.out.println(response.getStatusLine());
+            }
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+        System.out.println("Done");
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientPipelinedStreaming.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientPipelinedStreaming.java
new file mode 100644
index 0000000..61357cf
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientPipelinedStreaming.java
@@ -0,0 +1,136 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.nio.client.CloseableHttpPipeliningClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.client.methods.AsyncCharConsumer;
+import org.apache.http.nio.protocol.BasicAsyncRequestProducer;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * This example demonstrates a pipelinfed execution of multiple HTTP request / response exchanges
+ * with a full content streaming.
+ */
+public class AsyncClientPipelinedStreaming {
+
+    public static void main(final String[] args) throws Exception {
+        CloseableHttpPipeliningClient httpclient = HttpAsyncClients.createPipelining();
+        try {
+            httpclient.start();
+
+            HttpHost targetHost = new HttpHost("httpbin.org", 80);
+            HttpGet[] resquests = {
+                    new HttpGet("/"),
+                    new HttpGet("/ip"),
+                    new HttpGet("/headers"),
+                    new HttpGet("/get")
+            };
+
+            List<MyRequestProducer> requestProducers = new ArrayList<MyRequestProducer>();
+            List<MyResponseConsumer> responseConsumers = new ArrayList<MyResponseConsumer>();
+            for (HttpGet request: resquests) {
+                requestProducers.add(new MyRequestProducer(targetHost, request));
+                responseConsumers.add(new MyResponseConsumer(request));
+            }
+
+            Future<List<Boolean>> future = httpclient.execute(
+                    targetHost, requestProducers, responseConsumers, null);
+            future.get();
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+        System.out.println("Done");
+    }
+
+    static class MyRequestProducer extends BasicAsyncRequestProducer {
+
+        private final HttpRequest request;
+
+        MyRequestProducer(final HttpHost target, final HttpRequest request) {
+            super(target, request);
+            this.request = request;
+        }
+
+        @Override
+        public void requestCompleted(final HttpContext context) {
+            super.requestCompleted(context);
+            System.out.println();
+            System.out.println("Request sent: " + this.request.getRequestLine());
+            System.out.println("=================================================");
+        }
+    }
+
+    static class MyResponseConsumer extends AsyncCharConsumer<Boolean> {
+
+        private final HttpRequest request;
+
+        MyResponseConsumer(final HttpRequest request) {
+            this.request = request;
+        }
+
+        @Override
+        protected void onResponseReceived(final HttpResponse response) {
+            System.out.println();
+            System.out.println("Response received: " + response.getStatusLine() + " -> " + this.request.getRequestLine());
+            System.out.println("=================================================");
+        }
+
+        @Override
+        protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
+            while (buf.hasRemaining()) {
+                System.out.print(buf.get());
+            }
+        }
+
+        @Override
+        protected void releaseResources() {
+        }
+
+        @Override
+        protected Boolean buildResult(final HttpContext context) {
+            System.out.println();
+            System.out.println("=================================================");
+            System.out.println();
+            return Boolean.TRUE;
+        }
+
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientProxyAuthentication.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientProxyAuthentication.java
new file mode 100644
index 0000000..1a93804
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/AsyncClientProxyAuthentication.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.examples.nio.client;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+
+import java.util.concurrent.Future;
+
+/**
+ * This example demonstrates a basic asynchronous HTTP request / response exchange
+ * over a secure connection tunneled through an authenticating proxy.
+ */
+public class AsyncClientProxyAuthentication {
+
+    public static void main(String[] args)throws Exception {
+        CredentialsProvider credsProvider = new BasicCredentialsProvider();
+        credsProvider.setCredentials(
+                new AuthScope("localhost", 8889),
+                new UsernamePasswordCredentials("squid", "nopassword"));
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
+                .setDefaultCredentialsProvider(credsProvider)
+                .build();
+        try {
+            httpclient.start();
+            HttpHost proxy = new HttpHost("localhost", 8889);
+            RequestConfig config = RequestConfig.custom()
+                    .setProxy(proxy)
+                    .build();
+            HttpGet httpget = new HttpGet("https://httpbin.org/");
+            httpget.setConfig(config);
+            Future<HttpResponse> future = httpclient.execute(httpget, null);
+            HttpResponse response = future.get();
+            System.out.println("Response: " + response.getStatusLine());
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/QuickStart.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/QuickStart.java
new file mode 100644
index 0000000..2cbdffe
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/QuickStart.java
@@ -0,0 +1,143 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.client.methods.AsyncCharConsumer;
+import org.apache.http.nio.client.methods.HttpAsyncMethods;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.protocol.HttpContext;
+
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+
+public class QuickStart {
+
+    public static void main(String[] args) throws Exception {
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
+        try {
+            // Start the client
+            httpclient.start();
+
+            // Execute request
+            final HttpGet request1 = new HttpGet("http://www.apache.org/");
+            Future<HttpResponse> future = httpclient.execute(request1, null);
+            // and wait until response is received
+            HttpResponse response1 = future.get();
+            System.out.println(request1.getRequestLine() + "->" + response1.getStatusLine());
+
+            // One most likely would want to use a callback for operation result
+            final CountDownLatch latch1 = new CountDownLatch(1);
+            final HttpGet request2 = new HttpGet("http://www.apache.org/");
+            httpclient.execute(request2, new FutureCallback<HttpResponse>() {
+
+                @Override
+                public void completed(final HttpResponse response2) {
+                    latch1.countDown();
+                    System.out.println(request2.getRequestLine() + "->" + response2.getStatusLine());
+                }
+
+                @Override
+                public void failed(final Exception ex) {
+                    latch1.countDown();
+                    System.out.println(request2.getRequestLine() + "->" + ex);
+                }
+
+                @Override
+                public void cancelled() {
+                    latch1.countDown();
+                    System.out.println(request2.getRequestLine() + " cancelled");
+                }
+
+            });
+            latch1.await();
+
+            // In real world one most likely would want also want to stream
+            // request and response body content
+            final CountDownLatch latch2 = new CountDownLatch(1);
+            final HttpGet request3 = new HttpGet("http://www.apache.org/");
+            HttpAsyncRequestProducer producer3 = HttpAsyncMethods.create(request3);
+            AsyncCharConsumer<HttpResponse> consumer3 = new AsyncCharConsumer<HttpResponse>() {
+
+                HttpResponse response;
+
+                @Override
+                protected void onResponseReceived(final HttpResponse response) {
+                    this.response = response;
+                }
+
+                @Override
+                protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
+                    // Do something useful
+                }
+
+                @Override
+                protected void releaseResources() {
+                }
+
+                @Override
+                protected HttpResponse buildResult(final HttpContext context) {
+                    return this.response;
+                }
+
+            };
+            httpclient.execute(producer3, consumer3, new FutureCallback<HttpResponse>() {
+
+                @Override
+                public void completed(final HttpResponse response3) {
+                    latch2.countDown();
+                    System.out.println(request3.getRequestLine() + "->" + response3.getStatusLine());
+                }
+
+                @Override
+                public void failed(final Exception ex) {
+                    latch2.countDown();
+                    System.out.println(request3.getRequestLine() + "->" + ex);
+                }
+
+                @Override
+                public void cancelled() {
+                    latch2.countDown();
+                    System.out.println(request3.getRequestLine() + " cancelled");
+                }
+
+            });
+            latch2.await();
+
+        } finally {
+            httpclient.close();
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/ZeroCopyHttpExchange.java b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/ZeroCopyHttpExchange.java
new file mode 100644
index 0000000..045805e
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/examples/org/apache/http/examples/nio/client/ZeroCopyHttpExchange.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.examples.nio.client;
+
+import java.io.File;
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.nio.client.methods.ZeroCopyConsumer;
+import org.apache.http.nio.client.methods.ZeroCopyPost;
+
+/**
+ * This example demonstrates how HttpAsyncClient can be used to upload or download files
+ * without creating an intermediate content buffer in memory (zero copy file transfer).
+ */
+public class ZeroCopyHttpExchange {
+
+    public static void main(final String[] args) throws Exception {
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
+        try {
+            httpclient.start();
+            File upload = new File(args[0]);
+            File download = new File(args[1]);
+            ZeroCopyPost httpost = new ZeroCopyPost("http://localhost:8080/", upload,
+                    ContentType.create("text/plain"));
+            ZeroCopyConsumer<File> consumer = new ZeroCopyConsumer<File>(download) {
+
+                @Override
+                protected File process(
+                        final HttpResponse response,
+                        final File file,
+                        final ContentType contentType) throws Exception {
+                    if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+                        throw new ClientProtocolException("Upload failed: " + response.getStatusLine());
+                    }
+                    return file;
+                }
+
+            };
+            Future<File> future = httpclient.execute(httpost, consumer, null);
+            File result = future.get();
+            System.out.println("Response file length: " + result.length());
+            System.out.println("Shutting down");
+        } finally {
+            httpclient.close();
+        }
+        System.out.println("Done");
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/AbstractHttpAsyncClient.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/AbstractHttpAsyncClient.java
new file mode 100644
index 0000000..413c745
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/AbstractHttpAsyncClient.java
@@ -0,0 +1,611 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Future;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.ConnectionReuseStrategy;
+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.auth.AuthSchemeRegistry;
+import org.apache.http.client.AuthenticationStrategy;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.params.AuthPolicy;
+import org.apache.http.client.params.CookiePolicy;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+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.SPNegoSchemeFactory;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
+import org.apache.http.impl.client.DefaultRedirectStrategy;
+import org.apache.http.impl.client.DefaultUserTokenHandler;
+import org.apache.http.impl.client.ProxyAuthenticationStrategy;
+import org.apache.http.impl.client.TargetAuthenticationStrategy;
+import org.apache.http.impl.cookie.BestMatchSpecFactory;
+import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
+import org.apache.http.impl.cookie.IgnoreSpecFactory;
+import org.apache.http.impl.cookie.NetscapeDraftSpecFactory;
+import org.apache.http.impl.cookie.RFC2109SpecFactory;
+import org.apache.http.impl.cookie.RFC2965SpecFactory;
+import org.apache.http.impl.nio.DefaultHttpClientIODispatch;
+import org.apache.http.impl.nio.conn.DefaultHttpAsyncRoutePlanner;
+import org.apache.http.impl.nio.conn.PoolingClientAsyncConnectionManager;
+import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
+import org.apache.http.impl.nio.reactor.IOReactorConfig;
+import org.apache.http.nio.client.HttpAsyncClient;
+import org.apache.http.nio.client.methods.HttpAsyncMethods;
+import org.apache.http.nio.conn.ClientAsyncConnectionManager;
+import org.apache.http.nio.protocol.HttpAsyncRequestExecutionHandler;
+import org.apache.http.nio.protocol.HttpAsyncRequestExecutor;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.nio.reactor.IOReactorException;
+import org.apache.http.nio.reactor.IOReactorStatus;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.DefaultedHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.ImmutableHttpProcessor;
+
+@Deprecated
+public abstract class AbstractHttpAsyncClient implements HttpAsyncClient {
+
+    private final Log log = LogFactory.getLog(getClass());
+    private final ClientAsyncConnectionManager connmgr;
+    private final Queue<HttpAsyncRequestExecutionHandler<?>> queue;
+
+    private Thread reactorThread;
+    private BasicHttpProcessor mutableProcessor;
+    private ImmutableHttpProcessor protocolProcessor;
+    private ConnectionReuseStrategy reuseStrategy;
+    private ConnectionKeepAliveStrategy keepAliveStrategy;
+    private RedirectStrategy redirectStrategy;
+    private CookieSpecRegistry supportedCookieSpecs;
+    private CookieStore cookieStore;
+    private AuthSchemeRegistry supportedAuthSchemes;
+    private AuthenticationStrategy targetAuthStrategy;
+    private AuthenticationStrategy proxyAuthStrategy;
+    private CredentialsProvider credsProvider;
+    private HttpRoutePlanner routePlanner;
+    private UserTokenHandler userTokenHandler;
+    private HttpParams params;
+
+    private volatile boolean terminated;
+
+    protected AbstractHttpAsyncClient(final ClientAsyncConnectionManager connmgr) {
+        super();
+        this.connmgr = connmgr;
+        this.queue = new ConcurrentLinkedQueue<HttpAsyncRequestExecutionHandler<?>>();
+    }
+
+    protected AbstractHttpAsyncClient(final IOReactorConfig config) throws IOReactorException {
+        super();
+        final DefaultConnectingIOReactor defaultioreactor = new DefaultConnectingIOReactor(config);
+        defaultioreactor.setExceptionHandler(new InternalIOReactorExceptionHandler(this.log));
+        this.connmgr = new PoolingClientAsyncConnectionManager(defaultioreactor);
+        this.queue = new ConcurrentLinkedQueue<HttpAsyncRequestExecutionHandler<?>>();
+    }
+
+    protected abstract HttpParams createHttpParams();
+
+    protected abstract BasicHttpProcessor createHttpProcessor();
+
+    protected HttpContext createHttpContext() {
+        final HttpContext context = new BasicHttpContext();
+        context.setAttribute(
+                ClientContext.SCHEME_REGISTRY,
+                getConnectionManager().getSchemeRegistry());
+        context.setAttribute(
+                ClientContext.AUTHSCHEME_REGISTRY,
+                getAuthSchemes());
+        context.setAttribute(
+                ClientContext.COOKIESPEC_REGISTRY,
+                getCookieSpecs());
+        context.setAttribute(
+                ClientContext.COOKIE_STORE,
+                getCookieStore());
+        context.setAttribute(
+                ClientContext.CREDS_PROVIDER,
+                getCredentialsProvider());
+        return context;
+    }
+
+    protected ConnectionReuseStrategy createConnectionReuseStrategy() {
+        return new DefaultConnectionReuseStrategy();
+    }
+
+    protected ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy() {
+        return new DefaultConnectionKeepAliveStrategy();
+    }
+
+    protected AuthSchemeRegistry createAuthSchemeRegistry() {
+        final AuthSchemeRegistry registry = new AuthSchemeRegistry();
+        registry.register(
+                AuthPolicy.BASIC,
+                new BasicSchemeFactory());
+        registry.register(
+                AuthPolicy.DIGEST,
+                new DigestSchemeFactory());
+        registry.register(
+                AuthPolicy.NTLM,
+                new NTLMSchemeFactory());
+        registry.register(
+                AuthPolicy.SPNEGO,
+                new SPNegoSchemeFactory());
+        registry.register(
+                AuthPolicy.KERBEROS,
+                new KerberosSchemeFactory());
+        return registry;
+    }
+
+    protected CookieSpecRegistry createCookieSpecRegistry() {
+        final CookieSpecRegistry registry = new CookieSpecRegistry();
+        registry.register(
+                CookiePolicy.BEST_MATCH,
+                new BestMatchSpecFactory());
+        registry.register(
+                CookiePolicy.BROWSER_COMPATIBILITY,
+                new BrowserCompatSpecFactory());
+        registry.register(
+                CookiePolicy.NETSCAPE,
+                new NetscapeDraftSpecFactory());
+        registry.register(
+                CookiePolicy.RFC_2109,
+                new RFC2109SpecFactory());
+        registry.register(
+                CookiePolicy.RFC_2965,
+                new RFC2965SpecFactory());
+        registry.register(
+                CookiePolicy.IGNORE_COOKIES,
+                new IgnoreSpecFactory());
+        return registry;
+    }
+
+    protected AuthenticationStrategy createTargetAuthenticationStrategy() {
+        return new TargetAuthenticationStrategy();
+    }
+
+    protected AuthenticationStrategy createProxyAuthenticationStrategy() {
+        return new ProxyAuthenticationStrategy();
+    }
+
+    protected CookieStore createCookieStore() {
+        return new BasicCookieStore();
+    }
+
+    protected CredentialsProvider createCredentialsProvider() {
+        return new BasicCredentialsProvider();
+    }
+
+    protected HttpRoutePlanner createHttpRoutePlanner() {
+        return new DefaultHttpAsyncRoutePlanner(getConnectionManager().getSchemeRegistry());
+    }
+
+    protected UserTokenHandler createUserTokenHandler() {
+        return new DefaultUserTokenHandler();
+    }
+
+    public synchronized final HttpParams getParams() {
+        if (this.params == null) {
+            this.params = createHttpParams();
+        }
+        return this.params;
+    }
+
+    public synchronized void setParams(final HttpParams params) {
+        this.params = params;
+    }
+
+    public synchronized ClientAsyncConnectionManager getConnectionManager() {
+        return this.connmgr;
+    }
+
+    public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() {
+        if (this.reuseStrategy == null) {
+            this.reuseStrategy = createConnectionReuseStrategy();
+        }
+        return this.reuseStrategy;
+    }
+
+    public synchronized void setReuseStrategy(final ConnectionReuseStrategy reuseStrategy) {
+        this.reuseStrategy = reuseStrategy;
+    }
+
+    public synchronized final ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
+        if (this.keepAliveStrategy == null) {
+            this.keepAliveStrategy = createConnectionKeepAliveStrategy();
+        }
+        return this.keepAliveStrategy;
+    }
+
+    public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy keepAliveStrategy) {
+        this.keepAliveStrategy = keepAliveStrategy;
+    }
+
+    public synchronized final RedirectStrategy getRedirectStrategy() {
+        if (this.redirectStrategy == null) {
+            this.redirectStrategy = new DefaultRedirectStrategy();
+        }
+        return this.redirectStrategy;
+    }
+
+    public synchronized void setRedirectStrategy(final RedirectStrategy redirectStrategy) {
+        this.redirectStrategy = redirectStrategy;
+    }
+
+    public synchronized final AuthSchemeRegistry getAuthSchemes() {
+        if (this.supportedAuthSchemes == null) {
+            this.supportedAuthSchemes = createAuthSchemeRegistry();
+        }
+        return this.supportedAuthSchemes;
+    }
+
+    public synchronized void setAuthSchemes(final AuthSchemeRegistry authSchemeRegistry) {
+        this.supportedAuthSchemes = authSchemeRegistry;
+    }
+
+    public synchronized final CookieSpecRegistry getCookieSpecs() {
+        if (this.supportedCookieSpecs == null) {
+            this.supportedCookieSpecs = createCookieSpecRegistry();
+        }
+        return this.supportedCookieSpecs;
+    }
+
+    public synchronized void setCookieSpecs(final CookieSpecRegistry cookieSpecRegistry) {
+        this.supportedCookieSpecs = cookieSpecRegistry;
+    }
+
+    public synchronized final AuthenticationStrategy getTargetAuthenticationStrategy() {
+        if (this.targetAuthStrategy == null) {
+            this.targetAuthStrategy = createTargetAuthenticationStrategy();
+        }
+        return this.targetAuthStrategy;
+    }
+
+    public synchronized void setTargetAuthenticationStrategy(
+            final AuthenticationStrategy targetAuthStrategy) {
+        this.targetAuthStrategy = targetAuthStrategy;
+    }
+
+    public synchronized final AuthenticationStrategy getProxyAuthenticationStrategy() {
+        if (this.proxyAuthStrategy == null) {
+            this.proxyAuthStrategy = createProxyAuthenticationStrategy();
+        }
+        return this.proxyAuthStrategy;
+    }
+
+    public synchronized void setProxyAuthenticationStrategy(
+            final AuthenticationStrategy proxyAuthStrategy) {
+        this.proxyAuthStrategy = proxyAuthStrategy;
+    }
+
+    public synchronized final CookieStore getCookieStore() {
+        if (this.cookieStore == null) {
+            this.cookieStore = createCookieStore();
+        }
+        return this.cookieStore;
+    }
+
+    public synchronized void setCookieStore(final CookieStore cookieStore) {
+        this.cookieStore = cookieStore;
+    }
+
+    public synchronized final CredentialsProvider getCredentialsProvider() {
+        if (this.credsProvider == null) {
+            this.credsProvider = createCredentialsProvider();
+        }
+        return this.credsProvider;
+    }
+
+    public synchronized void setCredentialsProvider(final CredentialsProvider credsProvider) {
+        this.credsProvider = credsProvider;
+    }
+
+    public synchronized final HttpRoutePlanner getRoutePlanner() {
+        if (this.routePlanner == null) {
+            this.routePlanner = createHttpRoutePlanner();
+        }
+        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();
+        }
+        return this.userTokenHandler;
+    }
+
+
+    public synchronized void setUserTokenHandler(final UserTokenHandler userTokenHandler) {
+        this.userTokenHandler = userTokenHandler;
+    }
+
+    protected synchronized final BasicHttpProcessor getHttpProcessor() {
+        if (this.mutableProcessor == null) {
+            this.mutableProcessor = createHttpProcessor();
+        }
+        return this.mutableProcessor;
+    }
+
+    private synchronized final HttpProcessor getProtocolProcessor() {
+        if (this.protocolProcessor == null) {
+            // Get mutable HTTP processor
+            final BasicHttpProcessor proc = getHttpProcessor();
+            // and upgrade an immutable copy of it
+            final int reqc = proc.getRequestInterceptorCount();
+            final HttpRequestInterceptor[] reqinterceptors = new HttpRequestInterceptor[reqc];
+            for (int i = 0; i < reqc; i++) {
+                reqinterceptors[i] = proc.getRequestInterceptor(i);
+            }
+            final int resc = proc.getResponseInterceptorCount();
+            final HttpResponseInterceptor[] resinterceptors = new HttpResponseInterceptor[resc];
+            for (int i = 0; i < resc; i++) {
+                resinterceptors[i] = proc.getResponseInterceptor(i);
+            }
+            this.protocolProcessor = new ImmutableHttpProcessor(reqinterceptors, resinterceptors);
+        }
+        return this.protocolProcessor;
+    }
+
+    public synchronized int getResponseInterceptorCount() {
+        return getHttpProcessor().getResponseInterceptorCount();
+    }
+
+    public synchronized HttpResponseInterceptor getResponseInterceptor(final int index) {
+        return getHttpProcessor().getResponseInterceptor(index);
+    }
+
+    public synchronized HttpRequestInterceptor getRequestInterceptor(final int index) {
+        return getHttpProcessor().getRequestInterceptor(index);
+    }
+
+    public synchronized int getRequestInterceptorCount() {
+        return getHttpProcessor().getRequestInterceptorCount();
+    }
+
+    public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp) {
+        getHttpProcessor().addInterceptor(itcp);
+        this.protocolProcessor = null;
+    }
+
+    public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp, final int index) {
+        getHttpProcessor().addInterceptor(itcp, index);
+        this.protocolProcessor = null;
+    }
+
+    public synchronized void clearResponseInterceptors() {
+        getHttpProcessor().clearResponseInterceptors();
+        this.protocolProcessor = null;
+    }
+
+    public synchronized void removeResponseInterceptorByClass(final Class<? extends HttpResponseInterceptor> clazz) {
+        getHttpProcessor().removeResponseInterceptorByClass(clazz);
+        this.protocolProcessor = null;
+    }
+
+    public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp) {
+        getHttpProcessor().addInterceptor(itcp);
+        this.protocolProcessor = null;
+    }
+
+    public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp, final int index) {
+        getHttpProcessor().addInterceptor(itcp, index);
+        this.protocolProcessor = null;
+    }
+
+    public synchronized void clearRequestInterceptors() {
+        getHttpProcessor().clearRequestInterceptors();
+        this.protocolProcessor = null;
+    }
+
+    public synchronized void removeRequestInterceptorByClass(final Class<? extends HttpRequestInterceptor> clazz) {
+        getHttpProcessor().removeRequestInterceptorByClass(clazz);
+        this.protocolProcessor = null;
+    }
+
+    private void doExecute() {
+        final InternalRequestExecutor handler = new InternalRequestExecutor(this.log, new HttpAsyncRequestExecutor());
+        try {
+            final IOEventDispatch ioEventDispatch = new DefaultHttpClientIODispatch(handler, getParams());
+            this.connmgr.execute(ioEventDispatch);
+        } catch (final Exception ex) {
+            this.log.error("I/O reactor terminated abnormally", ex);
+        } finally {
+            this.terminated = true;
+            while (!this.queue.isEmpty()) {
+                final HttpAsyncRequestExecutionHandler<?> exchangeHandler = this.queue.remove();
+                exchangeHandler.cancel();
+            }
+        }
+    }
+
+    public IOReactorStatus getStatus() {
+        return this.connmgr.getStatus();
+    }
+
+    public synchronized void start() {
+        this.reactorThread = new Thread() {
+
+            @Override
+            public void run() {
+                doExecute();
+            }
+
+        };
+        this.reactorThread.start();
+    }
+
+    public void shutdown() throws InterruptedException {
+        try {
+            this.connmgr.shutdown(5000);
+        } catch (final IOException ex) {
+            this.log.error("I/O error shutting down", ex);
+        }
+        if (this.reactorThread != null) {
+            this.reactorThread.join();
+        }
+    }
+
+    @Override
+    public <T> Future<T> execute(
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final HttpContext context,
+            final FutureCallback<T> callback) {
+        if (this.terminated) {
+            throw new IllegalStateException("Client has been shut down");
+        }
+        final BasicFuture<T> future = new BasicFuture<T>(callback);
+        final ResultCallback<T> resultCallback = new DefaultResultCallback<T>(future, this.queue);
+        final DefaultAsyncRequestDirector<T> httpexchange;
+        synchronized (this) {
+            final HttpContext defaultContext = createHttpContext();
+            final HttpContext execContext;
+            if (context == null) {
+                execContext = defaultContext;
+            } else {
+                execContext = new DefaultedHttpContext(context, defaultContext);
+            }
+            httpexchange = new DefaultAsyncRequestDirector<T>(
+                    this.log,
+                    requestProducer,
+                    responseConsumer,
+                    execContext,
+                    resultCallback,
+                    this.connmgr,
+                    getProtocolProcessor(),
+                    getRoutePlanner(),
+                    getConnectionReuseStrategy(),
+                    getConnectionKeepAliveStrategy(),
+                    getRedirectStrategy(),
+                    getTargetAuthenticationStrategy(),
+                    getProxyAuthenticationStrategy(),
+                    getUserTokenHandler(),
+                    getParams());
+        }
+        this.queue.add(httpexchange);
+        httpexchange.start();
+        return future;
+    }
+
+    @Override
+    public <T> Future<T> execute(
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final FutureCallback<T> callback) {
+        return execute(requestProducer, responseConsumer, new BasicHttpContext(), callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpHost target, final HttpRequest request, final HttpContext context,
+            final FutureCallback<HttpResponse> callback) {
+        return execute(
+                HttpAsyncMethods.create(target, request),
+                HttpAsyncMethods.createConsumer(),
+                context, callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpHost target, final HttpRequest request,
+            final FutureCallback<HttpResponse> callback) {
+        return execute(target, request, new BasicHttpContext(), callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpUriRequest request,
+            final FutureCallback<HttpResponse> callback) {
+        return execute(request, new BasicHttpContext(), callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpUriRequest request,
+            final HttpContext context,
+            final FutureCallback<HttpResponse> callback) {
+        final HttpHost target;
+        try {
+            target = determineTarget(request);
+        } catch (final ClientProtocolException ex) {
+            final BasicFuture<HttpResponse> future = new BasicFuture<HttpResponse>(callback);
+            future.failed(ex);
+            return future;
+        }
+        return execute(target, request, context, callback);
+    }
+
+    private HttpHost determineTarget(final HttpUriRequest request) throws ClientProtocolException {
+        // A null target may be acceptable if there is a default target.
+        // Otherwise, the null target is detected in the director.
+        HttpHost target = null;
+
+        final URI requestURI = request.getURI();
+        if (requestURI.isAbsolute()) {
+            target = URIUtils.extractHost(requestURI);
+            if (target == null) {
+                throw new ClientProtocolException(
+                        "URI does not specify a valid host name: " + requestURI);
+            }
+        }
+        return target;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/DefaultAsyncRequestDirector.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/DefaultAsyncRequestDirector.java
new file mode 100644
index 0000000..7cc987e
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/DefaultAsyncRequestDirector.java
@@ -0,0 +1,896 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.ConnectionClosedException;
+import org.apache.http.ConnectionReuseStrategy;
+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.HttpStatus;
+import org.apache.http.ProtocolException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.auth.AuthProtocolState;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthenticationStrategy;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.NonRepeatableRequestException;
+import org.apache.http.client.RedirectException;
+import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.AbortableHttpRequest;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.params.HttpClientParams;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.ConnectionReleaseTrigger;
+import org.apache.http.conn.routing.BasicRouteDirector;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRouteDirector;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.ClientParamsStack;
+import org.apache.http.impl.client.EntityEnclosingRequestWrapper;
+import org.apache.http.impl.client.HttpAuthenticator;
+import org.apache.http.impl.client.RequestWrapper;
+import org.apache.http.impl.client.RoutedRequest;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.conn.ClientAsyncConnectionManager;
+import org.apache.http.nio.conn.ManagedClientAsyncConnection;
+import org.apache.http.nio.conn.scheme.AsyncScheme;
+import org.apache.http.nio.conn.scheme.AsyncSchemeRegistry;
+import org.apache.http.nio.protocol.HttpAsyncRequestExecutionHandler;
+import org.apache.http.nio.protocol.HttpAsyncRequestExecutor;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.ExecutionContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+
+@Deprecated
+class DefaultAsyncRequestDirector<T> implements HttpAsyncRequestExecutionHandler<T> {
+
+    private static final AtomicLong COUNTER = new AtomicLong(1);
+
+    private final Log log;
+
+    private final HttpAsyncRequestProducer requestProducer;
+    private final HttpAsyncResponseConsumer<T> responseConsumer;
+    private final HttpContext localContext;
+    private final ResultCallback<T> resultCallback;
+    private final ClientAsyncConnectionManager connmgr;
+    private final HttpProcessor httppocessor;
+    private final HttpRoutePlanner routePlanner;
+    private final HttpRouteDirector routeDirector;
+    private final ConnectionReuseStrategy reuseStrategy;
+    private final ConnectionKeepAliveStrategy keepaliveStrategy;
+    private final RedirectStrategy redirectStrategy;
+    private final AuthenticationStrategy targetAuthStrategy;
+    private final AuthenticationStrategy proxyAuthStrategy;
+    private final UserTokenHandler userTokenHandler;
+    private final AuthState targetAuthState;
+    private final AuthState proxyAuthState;
+    private final HttpAuthenticator authenticator;
+    private final HttpParams clientParams;
+    private final long id;
+
+    private volatile boolean closed;
+    private volatile InternalFutureCallback connRequestCallback;
+    private volatile ManagedClientAsyncConnection managedConn;
+
+    private RoutedRequest mainRequest;
+    private RoutedRequest followup;
+    private HttpResponse finalResponse;
+
+    private ClientParamsStack params;
+    private RequestWrapper currentRequest;
+    private HttpResponse currentResponse;
+    private boolean routeEstablished;
+    private int redirectCount;
+    private ByteBuffer tmpbuf;
+    private boolean requestContentProduced;
+    private boolean requestSent;
+    private int execCount;
+
+    public DefaultAsyncRequestDirector(
+            final Log log,
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final HttpContext localContext,
+            final ResultCallback<T> callback,
+            final ClientAsyncConnectionManager connmgr,
+            final HttpProcessor httppocessor,
+            final HttpRoutePlanner routePlanner,
+            final ConnectionReuseStrategy reuseStrategy,
+            final ConnectionKeepAliveStrategy keepaliveStrategy,
+            final RedirectStrategy redirectStrategy,
+            final AuthenticationStrategy targetAuthStrategy,
+            final AuthenticationStrategy proxyAuthStrategy,
+            final UserTokenHandler userTokenHandler,
+            final HttpParams clientParams) {
+        super();
+        this.log = log;
+        this.requestProducer = requestProducer;
+        this.responseConsumer = responseConsumer;
+        this.localContext = localContext;
+        this.resultCallback = callback;
+        this.connmgr = connmgr;
+        this.httppocessor = httppocessor;
+        this.routePlanner = routePlanner;
+        this.reuseStrategy = reuseStrategy;
+        this.keepaliveStrategy = keepaliveStrategy;
+        this.redirectStrategy = redirectStrategy;
+        this.routeDirector = new BasicRouteDirector();
+        this.targetAuthStrategy = targetAuthStrategy;
+        this.proxyAuthStrategy = proxyAuthStrategy;
+        this.userTokenHandler = userTokenHandler;
+        this.targetAuthState = new AuthState();
+        this.proxyAuthState = new AuthState();
+        this.authenticator     = new HttpAuthenticator(log);
+        this.clientParams = clientParams;
+        this.id = COUNTER.getAndIncrement();
+    }
+
+    @Override
+    public void close() {
+        if (this.closed) {
+            return;
+        }
+        this.closed = true;
+        final ManagedClientAsyncConnection localConn = this.managedConn;
+        if (localConn != null) {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("[exchange: " + this.id + "] aborting connection " + localConn);
+            }
+            try {
+                localConn.abortConnection();
+            } catch (final IOException ioex) {
+                this.log.debug("I/O error releasing connection", ioex);
+            }
+        }
+        try {
+            this.requestProducer.close();
+        } catch (final IOException ex) {
+            this.log.debug("I/O error closing request producer", ex);
+        }
+        try {
+            this.responseConsumer.close();
+        } catch (final IOException ex) {
+            this.log.debug("I/O error closing response consumer", ex);
+        }
+    }
+
+    public synchronized void start() {
+        try {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("[exchange: " + this.id + "] start execution");
+            }
+            this.localContext.setAttribute(ClientContext.TARGET_AUTH_STATE, this.targetAuthState);
+            this.localContext.setAttribute(ClientContext.PROXY_AUTH_STATE, this.proxyAuthState);
+
+            final HttpHost target = this.requestProducer.getTarget();
+            final HttpRequest request = this.requestProducer.generateRequest();
+            if (request instanceof AbortableHttpRequest) {
+                ((AbortableHttpRequest) request).setReleaseTrigger(new ConnectionReleaseTrigger() {
+
+                    @Override
+                    public void releaseConnection() throws IOException {
+                    }
+
+                    @Override
+                    public void abortConnection() throws IOException {
+                        cancel();
+                    }
+
+                });
+            }
+            this.params = new ClientParamsStack(null, this.clientParams, request.getParams(), null);
+            final RequestWrapper wrapper = wrapRequest(request);
+            wrapper.setParams(this.params);
+            final HttpRoute route = determineRoute(target, wrapper, this.localContext);
+            this.mainRequest = new RoutedRequest(wrapper, route);
+            final RequestConfig config = ParamConfig.getRequestConfig(params);
+            this.localContext.setAttribute(ClientContext.REQUEST_CONFIG, config);
+            this.requestContentProduced = false;
+            requestConnection();
+        } catch (final Exception ex) {
+            failed(ex);
+        }
+    }
+
+    @Override
+    public HttpHost getTarget() {
+        return this.requestProducer.getTarget();
+    }
+
+    @Override
+    public synchronized HttpRequest generateRequest() throws IOException, HttpException {
+        final HttpRoute route = this.mainRequest.getRoute();
+        if (!this.routeEstablished) {
+            int step;
+            do {
+                final HttpRoute fact = this.managedConn.getRoute();
+                step = this.routeDirector.nextStep(route, fact);
+                switch (step) {
+                case HttpRouteDirector.CONNECT_TARGET:
+                case HttpRouteDirector.CONNECT_PROXY:
+                    break;
+                case HttpRouteDirector.TUNNEL_TARGET:
+                    if (this.log.isDebugEnabled()) {
+                        this.log.debug("[exchange: " + this.id + "] Tunnel required");
+                    }
+                    final HttpRequest connect = createConnectRequest(route);
+                    this.currentRequest = wrapRequest(connect);
+                    this.currentRequest.setParams(this.params);
+                    break;
+                case HttpRouteDirector.TUNNEL_PROXY:
+                    throw new HttpException("Proxy chains are not supported");
+                case HttpRouteDirector.LAYER_PROTOCOL:
+                    managedConn.layerProtocol(this.localContext, this.params);
+                    break;
+                case HttpRouteDirector.UNREACHABLE:
+                    throw new HttpException("Unable to establish route: " +
+                            "planned = " + route + "; current = " + fact);
+                case HttpRouteDirector.COMPLETE:
+                    this.routeEstablished = true;
+                    break;
+                default:
+                    throw new IllegalStateException("Unknown step indicator "
+                            + step + " from RouteDirector.");
+                }
+            } while (step > HttpRouteDirector.COMPLETE && this.currentRequest == null);
+        }
+
+        HttpHost target = (HttpHost) this.params.getParameter(ClientPNames.VIRTUAL_HOST);
+        if (target == null) {
+            target = route.getTargetHost();
+        }
+        final HttpHost proxy = route.getProxyHost();
+        this.localContext.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
+        this.localContext.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy);
+        this.localContext.setAttribute(ExecutionContext.HTTP_CONNECTION, this.managedConn);
+        this.localContext.setAttribute(ClientContext.ROUTE, route);
+
+        if (this.currentRequest == null) {
+            this.currentRequest = this.mainRequest.getRequest();
+
+            final String userinfo = this.currentRequest.getURI().getUserInfo();
+            if (userinfo != null) {
+                this.targetAuthState.update(
+                        new BasicScheme(), new UsernamePasswordCredentials(userinfo));
+            }
+
+            // Re-write request URI if needed
+            rewriteRequestURI(this.currentRequest, route);
+        }
+        // Reset headers on the request wrapper
+        this.currentRequest.resetHeaders();
+
+        this.currentRequest.incrementExecCount();
+        if (this.currentRequest.getExecCount() > 1
+                && !this.requestProducer.isRepeatable()
+                && this.requestContentProduced) {
+            throw new NonRepeatableRequestException("Cannot retry request " +
+                "with a non-repeatable request entity.");
+        }
+        this.execCount++;
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Attempt " + this.execCount + " to execute request");
+        }
+        return this.currentRequest;
+    }
+
+    @Override
+    public synchronized void produceContent(
+            final ContentEncoder encoder, final IOControl ioctrl) throws IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] produce content");
+        }
+        this.requestContentProduced = true;
+        this.requestProducer.produceContent(encoder, ioctrl);
+        if (encoder.isCompleted()) {
+            this.requestProducer.resetRequest();
+        }
+    }
+
+    @Override
+    public void requestCompleted(final HttpContext context) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Request completed");
+        }
+        this.requestSent = true;
+        this.requestProducer.requestCompleted(context);
+    }
+
+    @Override
+    public boolean isRepeatable() {
+        return this.requestProducer.isRepeatable();
+    }
+
+    @Override
+    public void resetRequest() throws IOException {
+        this.requestSent = false;
+        this.requestProducer.resetRequest();
+    }
+
+    @Override
+    public synchronized void responseReceived(
+            final HttpResponse response) throws IOException, HttpException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Response received " + response.getStatusLine());
+        }
+        this.currentResponse = response;
+        this.currentResponse.setParams(this.params);
+
+        final int status = this.currentResponse.getStatusLine().getStatusCode();
+
+        if (!this.routeEstablished) {
+            final String method = this.currentRequest.getMethod();
+            if (method.equalsIgnoreCase("CONNECT") && status == HttpStatus.SC_OK) {
+                this.managedConn.tunnelTarget(this.params);
+            } else {
+                this.followup = handleConnectResponse();
+                if (this.followup == null) {
+                    this.finalResponse = response;
+                }
+            }
+        } else {
+            this.followup = handleResponse();
+            if (this.followup == null) {
+                this.finalResponse = response;
+            }
+
+            Object userToken = this.localContext.getAttribute(ClientContext.USER_TOKEN);
+            if (managedConn != null) {
+                if (userToken == null) {
+                    userToken = userTokenHandler.getUserToken(this.localContext);
+                    this.localContext.setAttribute(ClientContext.USER_TOKEN, userToken);
+                }
+                if (userToken != null) {
+                    managedConn.setState(userToken);
+                }
+            }
+        }
+        if (this.finalResponse != null) {
+            this.responseConsumer.responseReceived(response);
+        }
+    }
+
+    @Override
+    public synchronized void consumeContent(
+            final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Consume content");
+        }
+        if (this.finalResponse != null) {
+            this.responseConsumer.consumeContent(decoder, ioctrl);
+        } else {
+            if (this.tmpbuf == null) {
+                this.tmpbuf = ByteBuffer.allocate(2048);
+            }
+            this.tmpbuf.clear();
+            decoder.read(this.tmpbuf);
+        }
+    }
+
+    private void releaseConnection() {
+        if (this.managedConn != null) {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("[exchange: " + this.id + "] releasing connection " + this.managedConn);
+            }
+            try {
+                this.managedConn.getContext().removeAttribute(HttpAsyncRequestExecutor.HTTP_HANDLER);
+                this.managedConn.releaseConnection();
+            } catch (final IOException ioex) {
+                this.log.debug("I/O error releasing connection", ioex);
+            }
+            this.managedConn = null;
+        }
+    }
+
+    @Override
+    public synchronized void failed(final Exception ex) {
+        try {
+            if (!this.requestSent) {
+                this.requestProducer.failed(ex);
+            }
+            this.responseConsumer.failed(ex);
+        } finally {
+            try {
+                this.resultCallback.failed(ex, this);
+            } finally {
+                close();
+            }
+        }
+    }
+
+    @Override
+    public synchronized void responseCompleted(final HttpContext context) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Response fully read");
+        }
+        try {
+            if (this.resultCallback.isDone()) {
+                return;
+            }
+            if (this.managedConn.isOpen()) {
+                final long duration = this.keepaliveStrategy.getKeepAliveDuration(
+                        this.currentResponse, this.localContext);
+                if (this.log.isDebugEnabled()) {
+                    final String s;
+                    if (duration > 0) {
+                        s = "for " + duration + " " + TimeUnit.MILLISECONDS;
+                    } else {
+                        s = "indefinitely";
+                    }
+                    this.log.debug("[exchange: " + this.id + "] Connection can be kept alive " + s);
+                }
+                this.managedConn.setIdleDuration(duration, TimeUnit.MILLISECONDS);
+            } else {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + this.id + "] Connection cannot be kept alive");
+                }
+                this.managedConn.unmarkReusable();
+                if (this.proxyAuthState.getState() == AuthProtocolState.SUCCESS
+                        && this.proxyAuthState.getAuthScheme() != null
+                        && this.proxyAuthState.getAuthScheme().isConnectionBased()) {
+                    if (this.log.isDebugEnabled()) {
+                        this.log.debug("[exchange: " + this.id + "] Resetting proxy auth state");
+                    }
+                    this.proxyAuthState.reset();
+                }
+                if (this.targetAuthState.getState() == AuthProtocolState.SUCCESS
+                        && this.targetAuthState.getAuthScheme() != null
+                        && this.targetAuthState.getAuthScheme().isConnectionBased()) {
+                    if (this.log.isDebugEnabled()) {
+                        this.log.debug("[exchange: " + this.id + "] Resetting target auth state");
+                    }
+                    this.targetAuthState.reset();
+                }
+            }
+
+            if (this.finalResponse != null) {
+                this.responseConsumer.responseCompleted(this.localContext);
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + this.id + "] Response processed");
+                }
+                releaseConnection();
+                final T result = this.responseConsumer.getResult();
+                final Exception ex = this.responseConsumer.getException();
+                if (ex == null) {
+                    this.resultCallback.completed(result, this);
+                } else {
+                    this.resultCallback.failed(ex, this);
+                }
+            } else {
+                if (this.followup != null) {
+                    final HttpRoute actualRoute = this.mainRequest.getRoute();
+                    final HttpRoute newRoute = this.followup.getRoute();
+                    if (!actualRoute.equals(newRoute)) {
+                        releaseConnection();
+                    }
+                    this.mainRequest = this.followup;
+                }
+                if (this.managedConn != null && !this.managedConn.isOpen()) {
+                    releaseConnection();
+                }
+                if (this.managedConn != null) {
+                    this.managedConn.requestOutput();
+                } else {
+                    requestConnection();
+                }
+            }
+            this.followup = null;
+            this.currentRequest = null;
+            this.currentResponse = null;
+        } catch (final RuntimeException runex) {
+            failed(runex);
+            throw runex;
+        }
+    }
+
+    @Override
+    public synchronized boolean cancel() {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Cancelled");
+        }
+        try {
+            final boolean cancelled = this.responseConsumer.cancel();
+
+            final T result = this.responseConsumer.getResult();
+            final Exception ex = this.responseConsumer.getException();
+            if (ex != null) {
+                this.resultCallback.failed(ex, this);
+            } else if (result != null) {
+                this.resultCallback.completed(result, this);
+            } else {
+                this.resultCallback.cancelled(this);
+            }
+            return cancelled;
+        } catch (final RuntimeException runex) {
+            this.resultCallback.failed(runex, this);
+            throw runex;
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public boolean isDone() {
+        return this.resultCallback.isDone();
+    }
+
+    @Override
+    public T getResult() {
+        return this.responseConsumer.getResult();
+    }
+
+    @Override
+    public Exception getException() {
+        return this.responseConsumer.getException();
+    }
+
+    private synchronized void connectionRequestCompleted(final ManagedClientAsyncConnection conn) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Connection allocated: " + conn);
+        }
+        this.connRequestCallback = null;
+        try {
+            this.managedConn = conn;
+            if (this.closed) {
+                conn.releaseConnection();
+                return;
+            }
+            final HttpRoute route = this.mainRequest.getRoute();
+            if (!conn.isOpen()) {
+                conn.open(route, this.localContext, this.params);
+            }
+            conn.getContext().setAttribute(HttpAsyncRequestExecutor.HTTP_HANDLER, this);
+            conn.requestOutput();
+            this.routeEstablished = route.equals(conn.getRoute());
+            if (!conn.isOpen()) {
+                throw new ConnectionClosedException("Connection closed");
+            }
+        } catch (final IOException ex) {
+            failed(ex);
+        } catch (final RuntimeException runex) {
+            failed(runex);
+            throw runex;
+        }
+    }
+
+    private synchronized void connectionRequestFailed(final Exception ex) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] connection request failed");
+        }
+        this.connRequestCallback = null;
+        try {
+            this.resultCallback.failed(ex, this);
+        } finally {
+            close();
+        }
+    }
+
+    private synchronized void connectionRequestCancelled() {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Connection request cancelled");
+        }
+        this.connRequestCallback = null;
+        try {
+            this.resultCallback.cancelled(this);
+        } finally {
+            close();
+        }
+    }
+
+    class InternalFutureCallback implements FutureCallback<ManagedClientAsyncConnection> {
+
+        @Override
+        public void completed(final ManagedClientAsyncConnection session) {
+            connectionRequestCompleted(session);
+        }
+
+        @Override
+        public void failed(final Exception ex) {
+            connectionRequestFailed(ex);
+        }
+
+        @Override
+        public void cancelled() {
+            connectionRequestCancelled();
+        }
+
+    }
+
+    private void requestConnection() {
+        final HttpRoute route = this.mainRequest.getRoute();
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Request connection for " + route);
+        }
+        final long connectTimeout = HttpConnectionParams.getConnectionTimeout(this.params);
+        final Object userToken = this.localContext.getAttribute(ClientContext.USER_TOKEN);
+        this.connRequestCallback = new InternalFutureCallback();
+        this.connmgr.leaseConnection(
+                route, userToken,
+                connectTimeout, TimeUnit.MILLISECONDS,
+                this.connRequestCallback);
+    }
+
+    public synchronized void endOfStream() {
+        if (this.managedConn != null) {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("[exchange: " + this.id + "] Unexpected end of data stream");
+            }
+            releaseConnection();
+            if (this.connRequestCallback == null) {
+                requestConnection();
+            }
+        }
+    }
+
+    protected HttpRoute determineRoute(
+            final HttpHost target,
+            final HttpRequest request,
+            final HttpContext context) throws HttpException {
+        final HttpHost t = target != null ? target :
+                (HttpHost) request.getParams().getParameter(ClientPNames.DEFAULT_HOST);
+        if (t == null) {
+            throw new IllegalStateException("Target host could not be resolved");
+        }
+        return this.routePlanner.determineRoute(t, request, context);
+    }
+
+    private RequestWrapper wrapRequest(final HttpRequest request) throws ProtocolException {
+        if (request instanceof HttpEntityEnclosingRequest) {
+            return new EntityEnclosingRequestWrapper((HttpEntityEnclosingRequest) request);
+        } else {
+            return new RequestWrapper(request);
+        }
+    }
+
+    protected void rewriteRequestURI(
+            final RequestWrapper request, final HttpRoute route) throws ProtocolException {
+        try {
+            URI uri = request.getURI();
+            if (route.getProxyHost() != null && !route.isTunnelled()) {
+                // Make sure the request URI is absolute
+                if (!uri.isAbsolute()) {
+                    final HttpHost target = route.getTargetHost();
+                    uri = URIUtils.rewriteURI(uri, target);
+                    request.setURI(uri);
+                }
+            } else {
+                // Make sure the request URI is relative
+                if (uri.isAbsolute()) {
+                    uri = URIUtils.rewriteURI(uri, null);
+                    request.setURI(uri);
+                }
+            }
+        } catch (final URISyntaxException ex) {
+            throw new ProtocolException("Invalid URI: " + request.getRequestLine().getUri(), ex);
+        }
+    }
+
+    private AsyncSchemeRegistry getSchemeRegistry(final HttpContext context) {
+        AsyncSchemeRegistry reg = (AsyncSchemeRegistry) context.getAttribute(
+                ClientContext.SCHEME_REGISTRY);
+        if (reg == null) {
+            reg = this.connmgr.getSchemeRegistry();
+        }
+        return reg;
+    }
+
+    private HttpRequest createConnectRequest(final HttpRoute route) {
+        // see RFC 2817, section 5.2 and
+        // INTERNET-DRAFT: Tunneling TCP based protocols through
+        // Web proxy servers
+        final HttpHost target = route.getTargetHost();
+        final String host = target.getHostName();
+        int port = target.getPort();
+        if (port < 0) {
+            final AsyncSchemeRegistry registry = getSchemeRegistry(this.localContext);
+            final AsyncScheme scheme = registry.getScheme(target.getSchemeName());
+            port = scheme.getDefaultPort();
+        }
+        final StringBuilder buffer = new StringBuilder(host.length() + 6);
+        buffer.append(host);
+        buffer.append(':');
+        buffer.append(Integer.toString(port));
+        final ProtocolVersion ver = HttpProtocolParams.getVersion(this.params);
+        final HttpRequest req = new BasicHttpRequest("CONNECT", buffer.toString(), ver);
+        return req;
+    }
+
+    private RoutedRequest handleResponse() throws HttpException {
+        RoutedRequest followup = null;
+        if (HttpClientParams.isAuthenticating(this.params)) {
+            final CredentialsProvider credsProvider = (CredentialsProvider) this.localContext.getAttribute(
+                    ClientContext.CREDS_PROVIDER);
+            if (credsProvider != null) {
+                followup = handleTargetChallenge(credsProvider);
+                if (followup != null) {
+                    return followup;
+                }
+                followup = handleProxyChallenge(credsProvider);
+                if (followup != null) {
+                    return followup;
+                }
+            }
+        }
+        if (HttpClientParams.isRedirecting(this.params)) {
+            followup = handleRedirect();
+            if (followup != null) {
+                return followup;
+            }
+        }
+        return null;
+    }
+
+    private RoutedRequest handleConnectResponse() throws HttpException {
+        RoutedRequest followup = null;
+        if (HttpClientParams.isAuthenticating(this.params)) {
+            final CredentialsProvider credsProvider = (CredentialsProvider) this.localContext.getAttribute(
+                    ClientContext.CREDS_PROVIDER);
+            if (credsProvider != null) {
+                followup = handleProxyChallenge(credsProvider);
+                if (followup != null) {
+                    return followup;
+                }
+            }
+        }
+        return null;
+    }
+
+    private RoutedRequest handleRedirect() throws HttpException {
+        if (this.redirectStrategy.isRedirected(
+                this.currentRequest, this.currentResponse, this.localContext)) {
+
+            final HttpRoute route = this.mainRequest.getRoute();
+            final RequestWrapper request = this.mainRequest.getRequest();
+
+            final int maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100);
+            if (this.redirectCount >= maxRedirects) {
+                throw new RedirectException("Maximum redirects ("
+                        + maxRedirects + ") exceeded");
+            }
+            this.redirectCount++;
+
+            final HttpUriRequest redirect = this.redirectStrategy.getRedirect(
+                    this.currentRequest, this.currentResponse, this.localContext);
+            final HttpRequest orig = request.getOriginal();
+            redirect.setHeaders(orig.getAllHeaders());
+
+            final URI uri = redirect.getURI();
+            if (uri.getHost() == null) {
+                throw new ProtocolException("Redirect URI does not specify a valid host name: " + uri);
+            }
+            final HttpHost newTarget = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
+
+            // Reset auth states if redirecting to another host
+            if (!route.getTargetHost().equals(newTarget)) {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + this.id + "] Resetting target auth state");
+                }
+                this.targetAuthState.reset();
+                final AuthScheme authScheme = this.proxyAuthState.getAuthScheme();
+                if (authScheme != null && authScheme.isConnectionBased()) {
+                    if (this.log.isDebugEnabled()) {
+                        this.log.debug("[exchange: " + this.id + "] Resetting proxy auth state");
+                    }
+                    this.proxyAuthState.reset();
+                }
+            }
+
+            final RequestWrapper newRequest = wrapRequest(redirect);
+            newRequest.setParams(this.params);
+
+            final HttpRoute newRoute = determineRoute(newTarget, newRequest, this.localContext);
+
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("[exchange: " + this.id + "] Redirecting to '" + uri + "' via " + newRoute);
+            }
+            return new RoutedRequest(newRequest, newRoute);
+        }
+        return null;
+    }
+
+    private RoutedRequest handleTargetChallenge(
+            final CredentialsProvider credsProvider) throws HttpException {
+        final HttpRoute route = this.mainRequest.getRoute();
+        HttpHost target = (HttpHost) this.localContext.getAttribute(
+                ExecutionContext.HTTP_TARGET_HOST);
+        if (target == null) {
+            target = route.getTargetHost();
+        }
+        if (this.authenticator.isAuthenticationRequested(target, this.currentResponse,
+                this.targetAuthStrategy, this.targetAuthState, this.localContext)) {
+            if (this.authenticator.authenticate(target, this.currentResponse,
+                    this.targetAuthStrategy, this.targetAuthState, this.localContext)) {
+                // Re-try the same request via the same route
+                return this.mainRequest;
+            } else {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    private RoutedRequest handleProxyChallenge(
+            final CredentialsProvider credsProvider) throws HttpException {
+        final HttpRoute route = this.mainRequest.getRoute();
+        final HttpHost proxy = route.getProxyHost();
+        if (this.authenticator.isAuthenticationRequested(proxy, this.currentResponse,
+                this.proxyAuthStrategy, this.proxyAuthState, this.localContext)) {
+            if (this.authenticator.authenticate(proxy, this.currentResponse,
+                    this.proxyAuthStrategy, this.proxyAuthState, this.localContext)) {
+                // Re-try the same request via the same route
+                return this.mainRequest;
+            } else {
+                return null;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public HttpContext getContext() {
+        return this.localContext;
+    }
+
+    @Override
+    public HttpProcessor getHttpProcessor() {
+        return this.httppocessor;
+    }
+
+    @Override
+    public ConnectionReuseStrategy getConnectionReuseStrategy() {
+        return this.reuseStrategy;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/DefaultHttpAsyncClient.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/DefaultHttpAsyncClient.java
new file mode 100644
index 0000000..3c9763a
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/DefaultHttpAsyncClient.java
@@ -0,0 +1,105 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import org.apache.http.HttpVersion;
+import org.apache.http.client.protocol.RequestAddCookies;
+import org.apache.http.client.protocol.RequestAuthCache;
+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.ResponseProcessCookies;
+import org.apache.http.impl.nio.reactor.IOReactorConfig;
+import org.apache.http.nio.conn.ClientAsyncConnectionManager;
+import org.apache.http.nio.reactor.IOReactorException;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.params.SyncBasicHttpParams;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.RequestContent;
+import org.apache.http.protocol.RequestExpectContinue;
+import org.apache.http.protocol.RequestTargetHost;
+import org.apache.http.protocol.RequestUserAgent;
+import org.apache.http.util.VersionInfo;
+
+@Deprecated
+public class DefaultHttpAsyncClient extends AbstractHttpAsyncClient {
+
+    public DefaultHttpAsyncClient(final ClientAsyncConnectionManager connmgr) {
+        super(connmgr);
+    }
+
+    public DefaultHttpAsyncClient(final IOReactorConfig config) throws IOReactorException {
+        super(config);
+    }
+
+    public DefaultHttpAsyncClient() throws IOReactorException {
+        super(new IOReactorConfig());
+    }
+
+    @Override
+    protected HttpParams createHttpParams() {
+        final HttpParams params = new SyncBasicHttpParams();
+        setDefaultHttpParams(params);
+        return params;
+    }
+
+    public static void setDefaultHttpParams(final HttpParams params) {
+        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
+        HttpProtocolParams.setContentCharset(params, HTTP.DEF_CONTENT_CHARSET.name());
+        HttpConnectionParams.setTcpNoDelay(params, true);
+        HttpConnectionParams.setSocketBufferSize(params, 8192);
+        HttpProtocolParams.setUserAgent(params, VersionInfo.getUserAgent(
+                "Apache-HttpAsyncClient",
+                "org.apache.http.nio.client", DefaultHttpAsyncClient.class));
+    }
+
+    @Override
+    protected BasicHttpProcessor createHttpProcessor() {
+        final BasicHttpProcessor httpproc = new BasicHttpProcessor();
+        httpproc.addInterceptor(new RequestDefaultHeaders());
+        // Required protocol interceptors
+        httpproc.addInterceptor(new RequestContent());
+        httpproc.addInterceptor(new RequestTargetHost());
+        // Recommended protocol interceptors
+        httpproc.addInterceptor(new RequestClientConnControl());
+        httpproc.addInterceptor(new RequestUserAgent());
+        httpproc.addInterceptor(new RequestExpectContinue());
+        // HTTP state management interceptors
+        httpproc.addInterceptor(new RequestAddCookies());
+        httpproc.addInterceptor(new ResponseProcessCookies());
+        // HTTP authentication interceptors
+        httpproc.addInterceptor(new RequestAuthCache());
+        httpproc.addInterceptor(new RequestTargetAuthentication());
+        httpproc.addInterceptor(new RequestProxyAuthentication());
+        return httpproc;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/DefaultResultCallback.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/DefaultResultCallback.java
new file mode 100644
index 0000000..ecf0acf
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/DefaultResultCallback.java
@@ -0,0 +1,70 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.Queue;
+
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.nio.protocol.HttpAsyncRequestExecutionHandler;
+
+@Deprecated
+class DefaultResultCallback<T> implements ResultCallback<T> {
+
+    private final BasicFuture<T> future;
+    private final Queue<HttpAsyncRequestExecutionHandler<?>> queue;
+
+    DefaultResultCallback(
+            final BasicFuture<T> future, final Queue<HttpAsyncRequestExecutionHandler<?>> queue) {
+        super();
+        this.future = future;
+        this.queue = queue;
+    }
+
+    @Override
+    public void completed(final T result, final HttpAsyncRequestExecutionHandler<T> handler) {
+        this.future.completed(result);
+        this.queue.remove(handler);
+    }
+
+    @Override
+    public void failed(final Exception ex, final HttpAsyncRequestExecutionHandler<T> handler) {
+        this.future.failed(ex);
+        this.queue.remove(handler);
+    }
+
+    @Override
+    public void cancelled(final HttpAsyncRequestExecutionHandler<T> handler) {
+        this.future.cancel(true);
+        this.queue.remove(handler);
+    }
+
+    @Override
+    public boolean isDone() {
+        return this.future.isDone();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/ParamConfig.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/ParamConfig.java
new file mode 100644
index 0000000..9239c3c
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/ParamConfig.java
@@ -0,0 +1,69 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.Collection;
+
+import org.apache.http.auth.params.AuthPNames;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.params.HttpClientParams;
+import org.apache.http.conn.params.ConnRouteParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+
+@Deprecated
+final class ParamConfig {
+
+    private ParamConfig() {
+    }
+
+    @SuppressWarnings("unchecked")
+    public static RequestConfig getRequestConfig(final HttpParams params) {
+        return RequestConfig.custom()
+                .setAuthenticationEnabled(HttpClientParams.isAuthenticating(params))
+                .setCircularRedirectsAllowed(params.getBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS, false))
+                .setConnectionRequestTimeout((int) HttpClientParams.getConnectionManagerTimeout(params))
+                .setConnectTimeout(HttpConnectionParams.getConnectionTimeout(params))
+                .setCookieSpec(HttpClientParams.getCookiePolicy(params))
+                .setProxy(ConnRouteParams.getDefaultProxy(params))
+                .setExpectContinueEnabled(HttpProtocolParams.useExpectContinue(params))
+                .setLocalAddress(ConnRouteParams.getLocalAddress(params))
+                .setMaxRedirects(params.getIntParameter(ClientPNames.MAX_REDIRECTS, 50))
+                .setProxyPreferredAuthSchemes((Collection<String>) params.getParameter(
+                        AuthPNames.PROXY_AUTH_PREF))
+                .setTargetPreferredAuthSchemes((Collection<String>) params.getParameter(
+                        AuthPNames.TARGET_AUTH_PREF))
+                .setRedirectsEnabled(HttpClientParams.isRedirecting(params))
+                .setRelativeRedirectsAllowed(!params.getBooleanParameter(ClientPNames.REJECT_RELATIVE_REDIRECT, false))
+                .setSocketTimeout(HttpConnectionParams.getSoTimeout(params))
+                .setStaleConnectionCheckEnabled(HttpConnectionParams.isStaleCheckingEnabled(params))
+                .build();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/ResultCallback.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/ResultCallback.java
new file mode 100644
index 0000000..f53e97c
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/client/ResultCallback.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.nio.client;
+
+import org.apache.http.nio.protocol.HttpAsyncRequestExecutionHandler;
+
+@Deprecated
+interface ResultCallback<T> {
+
+    void completed(T result, HttpAsyncRequestExecutionHandler<T> handler);
+
+    void failed(Exception ex, HttpAsyncRequestExecutionHandler<T> handler);
+
+    void cancelled(HttpAsyncRequestExecutionHandler<T> handler);
+
+    boolean isDone();
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/AsyncSchemeRegistryFactory.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/AsyncSchemeRegistryFactory.java
new file mode 100644
index 0000000..a010ce7
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/AsyncSchemeRegistryFactory.java
@@ -0,0 +1,50 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+
+import org.apache.http.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
+import org.apache.http.nio.conn.scheme.AsyncScheme;
+import org.apache.http.nio.conn.scheme.AsyncSchemeRegistry;
+import org.apache.http.nio.conn.ssl.SSLLayeringStrategy;
+
+@Deprecated
+@Contract(threading = ThreadingBehavior.SAFE)
+public final class AsyncSchemeRegistryFactory {
+
+    public static AsyncSchemeRegistry createDefault() {
+        final AsyncSchemeRegistry registry = new AsyncSchemeRegistry();
+        registry.register(
+                new AsyncScheme("http", 80, null));
+        registry.register(
+                new AsyncScheme("https", 443, SSLLayeringStrategy.getDefaultStrategy()));
+        return registry;
+    }
+
+}
+
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/DefaultClientAsyncConnection.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/DefaultClientAsyncConnection.java
new file mode 100644
index 0000000..6dae8d9
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/DefaultClientAsyncConnection.java
@@ -0,0 +1,132 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.impl.nio.DefaultNHttpClientConnection;
+import org.apache.http.nio.conn.ClientAsyncConnection;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.nio.util.ByteBufferAllocator;
+import org.apache.http.params.HttpParams;
+
+@Deprecated
+public class DefaultClientAsyncConnection
+                    extends DefaultNHttpClientConnection implements ClientAsyncConnection {
+
+    private final Log headerlog = LogFactory.getLog("org.apache.http.headers");
+    private final Log wirelog   = LogFactory.getLog("org.apache.http.wire");
+    private final Log log;
+
+    private final String id;
+    private IOSession original;
+
+    public DefaultClientAsyncConnection(
+            final String id,
+            final IOSession iosession,
+            final HttpResponseFactory responseFactory,
+            final ByteBufferAllocator allocator,
+            final HttpParams params) {
+        super(iosession, responseFactory, allocator, params);
+        this.id = id;
+        this.original = iosession;
+        this.log = LogFactory.getLog(iosession.getClass());
+        if (this.log.isDebugEnabled() || this.wirelog.isDebugEnabled()) {
+            bind(new LoggingIOSession(iosession, this.id, this.log, this.wirelog));
+        }
+    }
+
+    @Override
+    public void upgrade(final IOSession iosession) {
+        this.original = iosession;
+        if (this.log.isDebugEnabled() || this.wirelog.isDebugEnabled()) {
+            this.log.debug(this.id + " Upgrade session " + iosession);
+            bind(new LoggingIOSession(iosession, this.id, this.headerlog, this.wirelog));
+        } else {
+            bind(iosession);
+        }
+    }
+
+    @Override
+    public IOSession getIOSession() {
+        return this.original;
+    }
+
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    protected void onResponseReceived(final HttpResponse response) {
+        if (response != null && this.headerlog.isDebugEnabled()) {
+            this.headerlog.debug(this.id + " << " + response.getStatusLine().toString());
+            final Header[] headers = response.getAllHeaders();
+            for (final Header header : headers) {
+                this.headerlog.debug(this.id + " << " + header.toString());
+            }
+        }
+    }
+
+    @Override
+    protected void onRequestSubmitted(final HttpRequest request) {
+        if (request != null && this.headerlog.isDebugEnabled()) {
+            this.headerlog.debug(this.id + " >> " + request.getRequestLine().toString());
+            final Header[] headers = request.getAllHeaders();
+            for (final Header header : headers) {
+                this.headerlog.debug(this.id + " >> " + header.toString());
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder buf = new StringBuilder();
+        buf.append(this.id);
+        buf.append(" [");
+        switch (this.status) {
+        case ACTIVE:
+            buf.append("ACTIVE");
+            if (this.inbuf.hasData()) {
+                buf.append("(").append(this.inbuf.length()).append(")");
+            }
+            break;
+        case CLOSING:
+            buf.append("CLOSING");
+            break;
+        case CLOSED:
+            buf.append("CLOSED");
+            break;
+        }
+        buf.append("]");
+        return buf.toString();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/DefaultClientAsyncConnectionFactory.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/DefaultClientAsyncConnectionFactory.java
new file mode 100644
index 0000000..3c19cab
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/DefaultClientAsyncConnectionFactory.java
@@ -0,0 +1,143 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CodingErrorAction;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpResponseFactory;
+import org.apache.http.config.ConnectionConfig;
+import org.apache.http.impl.DefaultHttpResponseFactory;
+import org.apache.http.impl.nio.codecs.DefaultHttpResponseParserFactory;
+import org.apache.http.message.BasicLineParser;
+import org.apache.http.nio.NHttpMessageParserFactory;
+import org.apache.http.nio.conn.ClientAsyncConnection;
+import org.apache.http.nio.conn.ClientAsyncConnectionFactory;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.nio.conn.NHttpConnectionFactory;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.nio.util.ByteBufferAllocator;
+import org.apache.http.nio.util.HeapByteBufferAllocator;
+import org.apache.http.params.HttpParams;
+
+@Deprecated
+public class DefaultClientAsyncConnectionFactory
+    implements ClientAsyncConnectionFactory, NHttpConnectionFactory<ManagedNHttpClientConnection> {
+
+    private final Log headerlog = LogFactory.getLog("org.apache.http.headers");
+    private final Log wirelog = LogFactory.getLog("org.apache.http.wire");
+    private final Log log = LogFactory.getLog(ManagedNHttpClientConnectionImpl.class);
+
+    public static final DefaultClientAsyncConnectionFactory INSTANCE = new DefaultClientAsyncConnectionFactory(null, null);
+
+    private static AtomicLong COUNTER = new AtomicLong();
+
+    private final HttpResponseFactory responseFactory;
+    private final NHttpMessageParserFactory<HttpResponse> responseParserFactory;
+    private final ByteBufferAllocator allocator;
+
+    public DefaultClientAsyncConnectionFactory(
+            final NHttpMessageParserFactory<HttpResponse> responseParserFactory,
+            final ByteBufferAllocator allocator) {
+        super();
+        this.responseFactory = createHttpResponseFactory();
+        this.responseParserFactory = responseParserFactory != null ? responseParserFactory :
+            DefaultHttpResponseParserFactory.INSTANCE;
+        this.allocator = allocator != null ? allocator : HeapByteBufferAllocator.INSTANCE;
+    }
+
+    public DefaultClientAsyncConnectionFactory() {
+        super();
+        this.responseFactory = createHttpResponseFactory();
+        this.responseParserFactory = new DefaultHttpResponseParserFactory(
+            BasicLineParser.INSTANCE, this.responseFactory);
+        this.allocator = createByteBufferAllocator();
+    }
+
+    @Override
+    @Deprecated
+    public ClientAsyncConnection create(
+            final String id,
+            final IOSession iosession,
+            final HttpParams params) {
+        return new DefaultClientAsyncConnection(
+                id, iosession, this.responseFactory, this.allocator, params);
+    }
+
+    @Deprecated
+    protected ByteBufferAllocator createByteBufferAllocator() {
+        return HeapByteBufferAllocator.INSTANCE;
+    }
+
+    @Deprecated
+    protected HttpResponseFactory createHttpResponseFactory() {
+        return DefaultHttpResponseFactory.INSTANCE;
+    }
+
+    @Override
+    public ManagedNHttpClientConnection create(
+            final IOSession iosession, final ConnectionConfig config) {
+        final String id = "http-outgoing-" + Long.toString(COUNTER.getAndIncrement());
+        CharsetDecoder chardecoder = null;
+        CharsetEncoder charencoder = null;
+        final Charset charset = config.getCharset();
+        final CodingErrorAction malformedInputAction = config.getMalformedInputAction() != null ?
+                config.getMalformedInputAction() : CodingErrorAction.REPORT;
+        final CodingErrorAction unmappableInputAction = config.getUnmappableInputAction() != null ?
+                config.getUnmappableInputAction() : CodingErrorAction.REPORT;
+        if (charset != null) {
+            chardecoder = charset.newDecoder();
+            chardecoder.onMalformedInput(malformedInputAction);
+            chardecoder.onUnmappableCharacter(unmappableInputAction);
+            charencoder = charset.newEncoder();
+            charencoder.onMalformedInput(malformedInputAction);
+            charencoder.onUnmappableCharacter(unmappableInputAction);
+        }
+        final ManagedNHttpClientConnection conn = new ManagedNHttpClientConnectionImpl(
+                id,
+                this.log,
+                this.headerlog,
+                this.wirelog,
+                iosession,
+                config.getBufferSize(),
+                config.getFragmentSizeHint(),
+                this.allocator,
+                chardecoder, charencoder, config.getMessageConstraints(),
+                null, null, null,
+                this.responseParserFactory);
+        iosession.setAttribute(IOEventDispatch.CONNECTION_KEY, conn);
+        return conn;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/DefaultHttpAsyncRoutePlanner.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/DefaultHttpAsyncRoutePlanner.java
new file mode 100644
index 0000000..065b0ff
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/DefaultHttpAsyncRoutePlanner.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.nio.conn;
+
+import java.net.InetAddress;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.conn.params.ConnRouteParams;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.nio.conn.scheme.AsyncScheme;
+import org.apache.http.nio.conn.scheme.AsyncSchemeRegistry;
+import org.apache.http.nio.conn.scheme.LayeringStrategy;
+import org.apache.http.protocol.HttpContext;
+
+@Deprecated
+public class DefaultHttpAsyncRoutePlanner implements HttpRoutePlanner {
+
+    private final AsyncSchemeRegistry schemeRegistry;
+
+    public DefaultHttpAsyncRoutePlanner(final AsyncSchemeRegistry schemeRegistry) {
+        super();
+        this.schemeRegistry = schemeRegistry;
+    }
+
+    private AsyncSchemeRegistry getSchemeRegistry(final HttpContext context) {
+        AsyncSchemeRegistry reg = (AsyncSchemeRegistry) context.getAttribute(
+                ClientContext.SCHEME_REGISTRY);
+        if (reg == null) {
+            reg = this.schemeRegistry;
+        }
+        return reg;
+    }
+
+    @Override
+    public HttpRoute determineRoute(
+            final HttpHost target,
+            final HttpRequest request,
+            final HttpContext context) throws HttpException {
+        if (request == null) {
+            throw new IllegalStateException("Request may not be null");
+        }
+        HttpRoute route = ConnRouteParams.getForcedRoute(request.getParams());
+        if (route != null) {
+            return route;
+        }
+        if (target == null) {
+            throw new IllegalStateException("Target host may be null");
+        }
+        final InetAddress local = ConnRouteParams.getLocalAddress(request.getParams());
+        final HttpHost proxy = ConnRouteParams.getDefaultProxy(request.getParams());
+        final AsyncScheme scheme;
+        try {
+            final AsyncSchemeRegistry registry = getSchemeRegistry(context);
+            scheme = registry.getScheme(target);
+        } catch (final IllegalStateException ex) {
+            throw new HttpException(ex.getMessage());
+        }
+        final LayeringStrategy layeringStrategy = scheme.getLayeringStrategy();
+        final boolean secure = layeringStrategy != null && layeringStrategy.isSecure();
+        if (proxy == null) {
+            route = new HttpRoute(target, local, secure);
+        } else {
+            route = new HttpRoute(target, local, proxy, secure);
+        }
+        return route;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/HttpNIOConnPool.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/HttpNIOConnPool.java
new file mode 100644
index 0000000..385edca
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/HttpNIOConnPool.java
@@ -0,0 +1,91 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.HttpHost;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.conn.scheme.AsyncScheme;
+import org.apache.http.nio.conn.scheme.AsyncSchemeRegistry;
+import org.apache.http.nio.pool.AbstractNIOConnPool;
+import org.apache.http.nio.reactor.ConnectingIOReactor;
+import org.apache.http.nio.reactor.IOSession;
+
+@Deprecated
+class HttpNIOConnPool extends AbstractNIOConnPool<HttpRoute, IOSession, HttpPoolEntry> {
+
+    private static final AtomicLong COUNTER = new AtomicLong(1);
+
+    private final Log log;
+    private final AsyncSchemeRegistry schemeRegistry;
+    private final long connTimeToLive;
+    private final TimeUnit tunit;
+
+    HttpNIOConnPool(
+            final Log log,
+            final ConnectingIOReactor ioreactor,
+            final AsyncSchemeRegistry schemeRegistry,
+            final long connTimeToLive, final TimeUnit tunit) {
+        super(ioreactor, new HttpNIOConnPoolFactory(), 2, 20);
+        this.log = log;
+        this.schemeRegistry = schemeRegistry;
+        this.connTimeToLive = connTimeToLive;
+        this.tunit = tunit;
+    }
+
+    @Override
+    protected SocketAddress resolveLocalAddress(final HttpRoute route) {
+        return new InetSocketAddress(route.getLocalAddress(), 0);
+    }
+
+    @Override
+    protected SocketAddress resolveRemoteAddress(final HttpRoute route) {
+        HttpHost firsthop = route.getProxyHost();
+        if (firsthop == null) {
+            firsthop = route.getTargetHost();
+        }
+        final String hostname = firsthop.getHostName();
+        int port = firsthop.getPort();
+        if (port < 0) {
+            final AsyncScheme scheme = this.schemeRegistry.getScheme(firsthop);
+            port = scheme.resolvePort(port);
+        }
+        return new InetSocketAddress(hostname, port);
+    }
+
+    @Override
+    protected HttpPoolEntry createEntry(final HttpRoute route, final IOSession session) {
+        final String id = Long.toString(COUNTER.getAndIncrement());
+        return new HttpPoolEntry(this.log, id, route, session, this.connTimeToLive, this.tunit);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/HttpNIOConnPoolFactory.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/HttpNIOConnPoolFactory.java
new file mode 100644
index 0000000..9ef747d
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/HttpNIOConnPoolFactory.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.nio.conn;
+
+import java.io.IOException;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.pool.NIOConnFactory;
+import org.apache.http.nio.reactor.IOSession;
+
+@Deprecated
+class HttpNIOConnPoolFactory implements NIOConnFactory<HttpRoute, IOSession> {
+
+    @Override
+    public IOSession create(final HttpRoute route, final IOSession session) throws IOException {
+        return session;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/HttpPoolEntry.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/HttpPoolEntry.java
new file mode 100644
index 0000000..bacf08d
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/HttpPoolEntry.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.nio.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.routing.HttpRoute;
+import org.apache.http.conn.routing.RouteTracker;
+import org.apache.http.nio.conn.ClientAsyncConnection;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.pool.PoolEntry;
+
+@Deprecated
+class HttpPoolEntry extends PoolEntry<HttpRoute, IOSession> {
+
+    private final Log log;
+    private final RouteTracker tracker;
+
+    HttpPoolEntry(final Log log, final String id, final HttpRoute route, final IOSession session,
+            final long timeToLive, final TimeUnit tunit) {
+        super(id, route, session, timeToLive, tunit);
+        this.log = log;
+        this.tracker = new RouteTracker(route);
+    }
+
+    @Override
+    public boolean isExpired(final long now) {
+        final boolean expired = super.isExpired(now);
+        if (expired && this.log.isDebugEnabled()) {
+            this.log.debug("Connection " + this + " expired @ " + new Date(getExpiry()));
+        }
+        return expired;
+    }
+
+    public ClientAsyncConnection getOperatedClientConnection() {
+        final IOSession session = getConnection();
+        return (ClientAsyncConnection) session.getAttribute(IOEventDispatch.CONNECTION_KEY);
+    }
+
+    @Override
+    public void close() {
+        try {
+            getOperatedClientConnection().shutdown();
+        } catch (final IOException ex) {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("I/O error shutting down connection", ex);
+            }
+        }
+    }
+
+    @Override
+    public boolean isClosed() {
+        final IOSession session = getConnection();
+        return session.isClosed();
+    }
+
+    HttpRoute getPlannedRoute() {
+        return super.getRoute();
+    }
+
+    RouteTracker getTracker() {
+        return this.tracker;
+    }
+
+    HttpRoute getEffectiveRoute() {
+        return this.tracker.toRoute();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/ManagedClientAsyncConnectionImpl.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/ManagedClientAsyncConnectionImpl.java
new file mode 100644
index 0000000..77f183a
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/ManagedClientAsyncConnectionImpl.java
@@ -0,0 +1,455 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.protocol.ClientContext;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.RouteTracker;
+import org.apache.http.impl.conn.ConnectionShutdownException;
+import org.apache.http.nio.conn.ClientAsyncConnection;
+import org.apache.http.nio.conn.ClientAsyncConnectionFactory;
+import org.apache.http.nio.conn.ClientAsyncConnectionManager;
+import org.apache.http.nio.conn.ManagedClientAsyncConnection;
+import org.apache.http.nio.conn.scheme.AsyncScheme;
+import org.apache.http.nio.conn.scheme.AsyncSchemeRegistry;
+import org.apache.http.nio.conn.scheme.LayeringStrategy;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.nio.reactor.ssl.SSLIOSession;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+@Deprecated
+class ManagedClientAsyncConnectionImpl implements ManagedClientAsyncConnection {
+
+    private final ClientAsyncConnectionManager manager;
+    private final ClientAsyncConnectionFactory connFactory;
+    private volatile HttpPoolEntry poolEntry;
+    private volatile boolean reusable;
+    private volatile long duration;
+
+    ManagedClientAsyncConnectionImpl(
+            final ClientAsyncConnectionManager manager,
+            final ClientAsyncConnectionFactory connFactory,
+            final HttpPoolEntry poolEntry) {
+        super();
+        this.manager = manager;
+        this.connFactory = connFactory;
+        this.poolEntry = poolEntry;
+        this.reusable = true;
+        this.duration = Long.MAX_VALUE;
+    }
+
+    HttpPoolEntry getPoolEntry() {
+        return this.poolEntry;
+    }
+
+    HttpPoolEntry detach() {
+        final HttpPoolEntry local = this.poolEntry;
+        this.poolEntry = null;
+        return local;
+    }
+
+    public ClientAsyncConnectionManager getManager() {
+        return this.manager;
+    }
+
+    private ClientAsyncConnection getConnection() {
+        final HttpPoolEntry local = this.poolEntry;
+        if (local == null) {
+            return null;
+        }
+        final IOSession session = local.getConnection();
+        return (ClientAsyncConnection) session.getAttribute(IOEventDispatch.CONNECTION_KEY);
+    }
+
+    private ClientAsyncConnection ensureConnection() {
+        final HttpPoolEntry local = this.poolEntry;
+        if (local == null) {
+            throw new ConnectionShutdownException();
+        }
+        final IOSession session = local.getConnection();
+        return (ClientAsyncConnection) session.getAttribute(IOEventDispatch.CONNECTION_KEY);
+    }
+
+    private HttpPoolEntry ensurePoolEntry() {
+        final HttpPoolEntry local = this.poolEntry;
+        if (local == null) {
+            throw new ConnectionShutdownException();
+        }
+        return local;
+    }
+
+    @Override
+    public void close() throws IOException {
+        final ClientAsyncConnection conn = getConnection();
+        if (conn != null) {
+            conn.close();
+        }
+    }
+
+    @Override
+    public void shutdown() throws IOException {
+        final ClientAsyncConnection conn = getConnection();
+        if (conn != null) {
+            conn.shutdown();
+        }
+    }
+
+    @Override
+    public boolean isOpen() {
+        final ClientAsyncConnection conn = getConnection();
+        if (conn != null) {
+            return conn.isOpen();
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isStale() {
+        return isOpen();
+    }
+
+    @Override
+    public void setSocketTimeout(final int timeout) {
+        final ClientAsyncConnection conn = ensureConnection();
+        conn.setSocketTimeout(timeout);
+    }
+
+    @Override
+    public int getSocketTimeout() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getSocketTimeout();
+    }
+
+    @Override
+    public HttpConnectionMetrics getMetrics() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getMetrics();
+    }
+
+    @Override
+    public InetAddress getLocalAddress() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getLocalAddress();
+    }
+
+    @Override
+    public int getLocalPort() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getLocalPort();
+    }
+
+    @Override
+    public InetAddress getRemoteAddress() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getRemoteAddress();
+    }
+
+    @Override
+    public int getRemotePort() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getRemotePort();
+    }
+
+    @Override
+    public int getStatus() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getStatus();
+    }
+
+    @Override
+    public HttpRequest getHttpRequest() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getHttpRequest();
+    }
+
+    @Override
+    public HttpResponse getHttpResponse() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getHttpResponse();
+    }
+
+    @Override
+    public HttpContext getContext() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getContext();
+    }
+
+    @Override
+    public void requestInput() {
+        final ClientAsyncConnection conn = ensureConnection();
+        conn.requestInput();
+    }
+
+    @Override
+    public void suspendInput() {
+        final ClientAsyncConnection conn = ensureConnection();
+        conn.suspendInput();
+    }
+
+    @Override
+    public void requestOutput() {
+        final ClientAsyncConnection conn = ensureConnection();
+        conn.requestOutput();
+    }
+
+    @Override
+    public void suspendOutput() {
+        final ClientAsyncConnection conn = ensureConnection();
+        conn.suspendOutput();
+    }
+
+    @Override
+    public void submitRequest(final HttpRequest request) throws IOException, HttpException {
+        final ClientAsyncConnection conn = ensureConnection();
+        conn.submitRequest(request);
+    }
+
+    @Override
+    public boolean isRequestSubmitted() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.isRequestSubmitted();
+    }
+
+    @Override
+    public void resetOutput() {
+        final ClientAsyncConnection conn = ensureConnection();
+        conn.resetOutput();
+    }
+
+    @Override
+    public void resetInput() {
+        final ClientAsyncConnection conn = ensureConnection();
+        conn.resetInput();
+    }
+
+    @Override
+    public boolean isSecure() {
+        final ClientAsyncConnection conn = ensureConnection();
+        return conn.getIOSession() instanceof SSLIOSession;
+    }
+
+    @Override
+    public HttpRoute getRoute() {
+        final HttpPoolEntry entry = ensurePoolEntry();
+        return entry.getEffectiveRoute();
+    }
+
+    @Override
+    public SSLSession getSSLSession() {
+        final ClientAsyncConnection conn = ensureConnection();
+        final IOSession iosession = conn.getIOSession();
+        if (iosession instanceof SSLIOSession) {
+            return ((SSLIOSession) iosession).getSSLSession();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public Object getState() {
+        final HttpPoolEntry entry = ensurePoolEntry();
+        return entry.getState();
+    }
+
+    @Override
+    public void setState(final Object state) {
+        final HttpPoolEntry entry = ensurePoolEntry();
+        entry.setState(state);
+    }
+
+    @Override
+    public void markReusable() {
+        this.reusable = true;
+    }
+
+    @Override
+    public void unmarkReusable() {
+        this.reusable = false;
+    }
+
+    @Override
+    public boolean isMarkedReusable() {
+        return this.reusable;
+    }
+
+    @Override
+    public void setIdleDuration(final long duration, final TimeUnit unit) {
+        if(duration > 0) {
+            this.duration = unit.toMillis(duration);
+        } else {
+            this.duration = -1;
+        }
+    }
+
+    private AsyncSchemeRegistry getSchemeRegistry(final HttpContext context) {
+        AsyncSchemeRegistry reg = (AsyncSchemeRegistry) context.getAttribute(
+                ClientContext.SCHEME_REGISTRY);
+        if (reg == null) {
+            reg = this.manager.getSchemeRegistry();
+        }
+        return reg;
+    }
+
+    @Override
+    public synchronized void open(
+            final HttpRoute route,
+            final HttpContext context,
+            final HttpParams params) throws IOException {
+        final HttpPoolEntry entry = ensurePoolEntry();
+        final RouteTracker tracker = entry.getTracker();
+        if (tracker.isConnected()) {
+            throw new IllegalStateException("Connection already open");
+        }
+
+        final HttpHost target = route.getTargetHost();
+        final HttpHost proxy = route.getProxyHost();
+        IOSession iosession = entry.getConnection();
+
+        if (proxy == null) {
+            final AsyncScheme scheme = getSchemeRegistry(context).getScheme(target);
+            final LayeringStrategy layeringStrategy = scheme.getLayeringStrategy();
+            if (layeringStrategy != null) {
+                iosession = layeringStrategy.layer(iosession);
+            }
+        }
+
+        final ClientAsyncConnection conn = this.connFactory.create(
+                "http-outgoing-" + entry.getId(),
+                iosession,
+                params);
+        iosession.setAttribute(IOEventDispatch.CONNECTION_KEY, conn);
+
+        if (proxy == null) {
+            tracker.connectTarget(conn.getIOSession() instanceof SSLIOSession);
+        } else {
+            tracker.connectProxy(proxy, false);
+        }
+    }
+
+    @Override
+    public synchronized void tunnelProxy(
+            final HttpHost next, final HttpParams params) throws IOException {
+        final HttpPoolEntry entry = ensurePoolEntry();
+        final RouteTracker tracker = entry.getTracker();
+        if (!tracker.isConnected()) {
+            throw new IllegalStateException("Connection not open");
+        }
+        tracker.tunnelProxy(next, false);
+    }
+
+    @Override
+    public synchronized void tunnelTarget(
+            final HttpParams params) throws IOException {
+        final HttpPoolEntry entry = ensurePoolEntry();
+        final RouteTracker tracker = entry.getTracker();
+        if (!tracker.isConnected()) {
+            throw new IllegalStateException("Connection not open");
+        }
+        if (tracker.isTunnelled()) {
+            throw new IllegalStateException("Connection is already tunnelled");
+        }
+        tracker.tunnelTarget(false);
+    }
+
+    @Override
+    public synchronized void layerProtocol(
+            final HttpContext context, final HttpParams params) throws IOException {
+        final HttpPoolEntry entry = ensurePoolEntry();
+        final RouteTracker tracker = entry.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");
+        }
+        final HttpHost target = tracker.getTargetHost();
+        final AsyncScheme scheme = getSchemeRegistry(context).getScheme(target);
+        final LayeringStrategy layeringStrategy = scheme.getLayeringStrategy();
+        if (layeringStrategy == null) {
+            throw new IllegalStateException(scheme.getName() +
+                    " scheme does not provider support for protocol layering");
+        }
+        final IOSession iosession = entry.getConnection();
+        final ClientAsyncConnection conn = (ClientAsyncConnection) iosession.getAttribute(
+                IOEventDispatch.CONNECTION_KEY);
+        conn.upgrade((SSLIOSession) layeringStrategy.layer(iosession));
+        tracker.layerProtocol(layeringStrategy.isSecure());
+    }
+
+    @Override
+    public synchronized void releaseConnection() {
+        if (this.poolEntry == null) {
+            return;
+        }
+        this.manager.releaseConnection(this, this.duration, TimeUnit.MILLISECONDS);
+        this.poolEntry = null;
+    }
+
+    @Override
+    public synchronized void abortConnection() {
+        if (this.poolEntry == null) {
+            return;
+        }
+        this.reusable = false;
+        final IOSession iosession = this.poolEntry.getConnection();
+        final ClientAsyncConnection conn = (ClientAsyncConnection) iosession.getAttribute(
+                IOEventDispatch.CONNECTION_KEY);
+        try {
+            conn.shutdown();
+        } catch (final IOException ignore) {
+        }
+        this.manager.releaseConnection(this, this.duration, TimeUnit.MILLISECONDS);
+        this.poolEntry = null;
+    }
+
+    @Override
+    public synchronized String toString() {
+        if (this.poolEntry != null) {
+            return this.poolEntry.toString();
+        } else {
+            return "released";
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/PoolingClientAsyncConnectionManager.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/PoolingClientAsyncConnectionManager.java
new file mode 100644
index 0000000..bd67ff9
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/impl/nio/conn/PoolingClientAsyncConnectionManager.java
@@ -0,0 +1,328 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.conn.ClientAsyncConnectionFactory;
+import org.apache.http.nio.conn.ClientAsyncConnectionManager;
+import org.apache.http.nio.conn.ManagedClientAsyncConnection;
+import org.apache.http.nio.conn.scheme.AsyncSchemeRegistry;
+import org.apache.http.nio.reactor.ConnectingIOReactor;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.nio.reactor.IOReactorException;
+import org.apache.http.nio.reactor.IOReactorStatus;
+import org.apache.http.pool.ConnPoolControl;
+import org.apache.http.pool.PoolStats;
+import org.apache.http.util.Args;
+
+@Deprecated
+public class PoolingClientAsyncConnectionManager
+                              implements ClientAsyncConnectionManager, ConnPoolControl<HttpRoute> {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    private final ConnectingIOReactor ioreactor;
+    private final HttpNIOConnPool pool;
+    private final AsyncSchemeRegistry schemeRegistry;
+    private final ClientAsyncConnectionFactory connFactory;
+
+    public PoolingClientAsyncConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final AsyncSchemeRegistry schemeRegistry,
+            final long timeToLive, final TimeUnit tunit) {
+        super();
+        Args.notNull(ioreactor, "I/O reactor");
+        Args.notNull(schemeRegistry, "Scheme registory");
+        Args.notNull(tunit, "Time unit");
+        this.ioreactor = ioreactor;
+        this.pool = new HttpNIOConnPool(this.log, ioreactor, schemeRegistry, timeToLive, tunit);
+        this.schemeRegistry = schemeRegistry;
+        this.connFactory = createClientAsyncConnectionFactory();
+    }
+
+    public PoolingClientAsyncConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final AsyncSchemeRegistry schemeRegistry) throws IOReactorException {
+        this(ioreactor, schemeRegistry, -1, TimeUnit.MILLISECONDS);
+    }
+
+    public PoolingClientAsyncConnectionManager(
+            final ConnectingIOReactor ioreactor) throws IOReactorException {
+        this(ioreactor, AsyncSchemeRegistryFactory.createDefault());
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            shutdown();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    protected ClientAsyncConnectionFactory createClientAsyncConnectionFactory() {
+        return new DefaultClientAsyncConnectionFactory();
+    }
+
+    @Override
+    public AsyncSchemeRegistry getSchemeRegistry() {
+        return this.schemeRegistry;
+    }
+
+    @Override
+    public void execute(final IOEventDispatch eventDispatch) throws IOException {
+        this.ioreactor.execute(eventDispatch);
+    }
+
+    @Override
+    public IOReactorStatus getStatus() {
+        return this.ioreactor.getStatus();
+    }
+
+    @Override
+    public void shutdown(final long waitMs) throws IOException {
+        this.log.debug("Connection manager is shutting down");
+        this.pool.shutdown(waitMs);
+        this.log.debug("Connection manager shut down");
+    }
+
+    @Override
+    public void shutdown() throws IOException {
+        this.log.debug("Connection manager is shutting down");
+        this.pool.shutdown(2000);
+        this.log.debug("Connection manager shut down");
+    }
+
+    private String format(final HttpRoute route, final Object state) {
+        final 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) {
+        final StringBuilder buf = new StringBuilder();
+        final PoolStats totals = this.pool.getTotalStats();
+        final 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) {
+        final StringBuilder buf = new StringBuilder();
+        buf.append("[id: ").append(entry.getId()).append("]");
+        buf.append("[route: ").append(entry.getRoute()).append("]");
+        final Object state = entry.getState();
+        if (state != null) {
+            buf.append("[state: ").append(state).append("]");
+        }
+        return buf.toString();
+    }
+
+    @Override
+    public Future<ManagedClientAsyncConnection> leaseConnection(
+            final HttpRoute route,
+            final Object state,
+            final long connectTimeout,
+            final TimeUnit tunit,
+            final FutureCallback<ManagedClientAsyncConnection> callback) {
+        Args.notNull(route, "HTTP route");
+        Args.notNull(tunit, "Time unit");
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Connection request: " + format(route, state) + formatStats(route));
+        }
+        final BasicFuture<ManagedClientAsyncConnection> future = new BasicFuture<ManagedClientAsyncConnection>(
+                callback);
+        this.pool.lease(route, state, connectTimeout, tunit, new InternalPoolEntryCallback(future));
+        return future;
+    }
+
+    @Override
+    public void releaseConnection(
+            final ManagedClientAsyncConnection conn,
+            final long keepalive,
+            final TimeUnit tunit) {
+        Args.notNull(conn, "HTTP connection");
+        if (!(conn instanceof ManagedClientAsyncConnectionImpl)) {
+            throw new IllegalArgumentException("Connection class mismatch, " +
+                 "connection not obtained from this manager");
+        }
+        Args.notNull(tunit, "Time unit");
+        final ManagedClientAsyncConnectionImpl managedConn = (ManagedClientAsyncConnectionImpl) conn;
+        final ClientAsyncConnectionManager manager = managedConn.getManager();
+        if (manager != null && manager != this) {
+            throw new IllegalArgumentException("Connection not obtained from this manager");
+        }
+        if (this.pool.isShutdown()) {
+            return;
+        }
+
+        synchronized (managedConn) {
+            final HttpPoolEntry entry = managedConn.getPoolEntry();
+            if (entry == null) {
+                return;
+            }
+            try {
+                if (managedConn.isOpen() && !managedConn.isMarkedReusable()) {
+                    try {
+                        managedConn.shutdown();
+                    } catch (final IOException iox) {
+                        if (this.log.isDebugEnabled()) {
+                            this.log.debug("I/O exception shutting down released connection", iox);
+                        }
+                    }
+                }
+                if (managedConn.isOpen()) {
+                    entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
+                    if (this.log.isDebugEnabled()) {
+                        final String s;
+                        if (keepalive > 0) {
+                            s = "for " + keepalive + " " + tunit;
+                        } else {
+                            s = "indefinitely";
+                        }
+                        this.log.debug("Connection " + format(entry) + " can be kept alive " + s);
+                    }
+                    // Do not time out pooled connection
+                    managedConn.setSocketTimeout(0);
+                }
+            } finally {
+                this.pool.release(managedConn.detach(), managedConn.isMarkedReusable());
+            }
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute()));
+            }
+        }
+    }
+
+    @Override
+    public PoolStats getTotalStats() {
+        return this.pool.getTotalStats();
+    }
+
+    @Override
+    public PoolStats getStats(final HttpRoute route) {
+        return this.pool.getStats(route);
+    }
+
+    @Override
+    public void setMaxTotal(final int max) {
+        this.pool.setMaxTotal(max);
+    }
+
+    @Override
+    public void setDefaultMaxPerRoute(final int max) {
+        this.pool.setDefaultMaxPerRoute(max);
+    }
+
+    @Override
+    public void setMaxPerRoute(final HttpRoute route, final int max) {
+        this.pool.setMaxPerRoute(route, max);
+    }
+
+    @Override
+    public int getMaxTotal() {
+        return this.pool.getMaxTotal();
+    }
+
+    @Override
+    public int getDefaultMaxPerRoute() {
+        return this.pool.getDefaultMaxPerRoute();
+    }
+
+    @Override
+    public int getMaxPerRoute(final HttpRoute route) {
+        return this.pool.getMaxPerRoute(route);
+    }
+
+    public void closeIdleConnections(final long idleTimeout, final TimeUnit tunit) {
+        if (log.isDebugEnabled()) {
+            log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit);
+        }
+        this.pool.closeIdle(idleTimeout, tunit);
+    }
+
+    public void closeExpiredConnections() {
+        log.debug("Closing expired connections");
+        this.pool.closeExpired();
+    }
+
+    class InternalPoolEntryCallback implements FutureCallback<HttpPoolEntry> {
+
+        private final BasicFuture<ManagedClientAsyncConnection> future;
+
+        public InternalPoolEntryCallback(
+                final BasicFuture<ManagedClientAsyncConnection> future) {
+            super();
+            this.future = future;
+        }
+
+        @Override
+        public void completed(final HttpPoolEntry entry) {
+            if (log.isDebugEnabled()) {
+                log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute()));
+            }
+            final ManagedClientAsyncConnection conn = new ManagedClientAsyncConnectionImpl(
+                    PoolingClientAsyncConnectionManager.this,
+                    PoolingClientAsyncConnectionManager.this.connFactory,
+                    entry);
+            if (!this.future.completed(conn)) {
+                pool.release(entry, true);
+            }
+        }
+
+        @Override
+        public void failed(final Exception ex) {
+            if (log.isDebugEnabled()) {
+                log.debug("Connection request failed", ex);
+            }
+            this.future.failed(ex);
+        }
+
+        @Override
+        public void cancelled() {
+            log.debug("Connection request cancelled");
+            this.future.cancel(true);
+        }
+
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ClientAsyncConnection.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ClientAsyncConnection.java
new file mode 100644
index 0000000..b47cbda
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ClientAsyncConnection.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.nio.conn;
+
+import org.apache.http.HttpInetConnection;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.reactor.IOSession;
+
+@Deprecated
+public interface ClientAsyncConnection extends NHttpClientConnection, HttpInetConnection {
+
+    void upgrade(IOSession iosession);
+
+    IOSession getIOSession();
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ClientAsyncConnectionFactory.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ClientAsyncConnectionFactory.java
new file mode 100644
index 0000000..a0b4422
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ClientAsyncConnectionFactory.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.nio.conn;
+
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.params.HttpParams;
+
+@Deprecated
+public interface ClientAsyncConnectionFactory {
+
+    ClientAsyncConnection create(String id, IOSession iosession, HttpParams params);
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ClientAsyncConnectionManager.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ClientAsyncConnectionManager.java
new file mode 100644
index 0000000..ccde65f
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ClientAsyncConnectionManager.java
@@ -0,0 +1,50 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.conn.scheme.AsyncSchemeRegistry;
+import org.apache.http.nio.reactor.IOReactor;
+
+@Deprecated
+public interface ClientAsyncConnectionManager extends IOReactor {
+
+    AsyncSchemeRegistry getSchemeRegistry();
+
+    Future<ManagedClientAsyncConnection> leaseConnection(
+            HttpRoute route, Object state,
+            long connectTimeout, TimeUnit timeUnit,
+            FutureCallback<ManagedClientAsyncConnection> callback);
+
+    void releaseConnection(ManagedClientAsyncConnection session,
+            long validDuration, TimeUnit timeUnit);
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ManagedClientAsyncConnection.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ManagedClientAsyncConnection.java
new file mode 100644
index 0000000..b40042d
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ManagedClientAsyncConnection.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.nio.conn;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.HttpHost;
+import org.apache.http.conn.ConnectionReleaseTrigger;
+import org.apache.http.conn.HttpRoutedConnection;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+
+@Deprecated
+public interface ManagedClientAsyncConnection
+    extends HttpRoutedConnection, NHttpClientConnection, ConnectionReleaseTrigger {
+
+    Object getState();
+
+    void setState(Object state);
+
+    void markReusable();
+
+    void unmarkReusable();
+
+    boolean isMarkedReusable();
+
+    void open(HttpRoute route, HttpContext context, HttpParams params) throws IOException;
+
+    void tunnelTarget(HttpParams params) throws IOException;
+
+    void tunnelProxy(HttpHost next, HttpParams params) throws IOException;
+
+    void layerProtocol(HttpContext context, HttpParams params) throws IOException;
+
+    void setIdleDuration(long duration, TimeUnit tunit);
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/AsyncScheme.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/AsyncScheme.java
new file mode 100644
index 0000000..9e01ab5
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/AsyncScheme.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.nio.conn.scheme;
+
+import java.util.Locale;
+
+import org.apache.http.util.Args;
+import org.apache.http.util.LangUtils;
+
+@Deprecated
+public final class AsyncScheme {
+
+    /** The name of this scheme, in lowercase. (e.g. http, https) */
+    private final String name;
+
+    /** The layering strategy for this scheme, if applicable */
+    private final LayeringStrategy strategy;
+
+    /** The default port for this scheme */
+    private final int defaultPort;
+
+    /** A string representation, for {@link #toString toString}. */
+    private String stringRep;
+    /*
+     *  This is used to cache the result of the toString() method
+     *  Since the method always generates the same value, there's no
+     *  need to synchronize, and it does not affect immutability.
+    */
+
+    public AsyncScheme(final String name, final int port, final LayeringStrategy strategy) {
+        Args.notNull(name, "Scheme name");
+        if ((port <= 0) || (port > 0xffff)) {
+            throw new IllegalArgumentException("Port is invalid: " + port);
+        }
+        this.name = name.toLowerCase(Locale.ROOT);
+        this.strategy = strategy;
+        this.defaultPort = port;
+    }
+
+    public final int getDefaultPort() {
+        return defaultPort;
+    }
+
+    public final LayeringStrategy getLayeringStrategy() {
+        return this.strategy;
+    }
+
+    public final String getName() {
+        return name;
+    }
+
+    public final int resolvePort(final int port) {
+        return port <= 0 ? defaultPort : port;
+    }
+
+    @Override
+    public final String toString() {
+        if (stringRep == null) {
+            final StringBuilder buffer = new StringBuilder();
+            buffer.append(this.name);
+            buffer.append(':');
+            buffer.append(Integer.toString(this.defaultPort));
+            stringRep = buffer.toString();
+        }
+        return stringRep;
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof AsyncScheme) {
+            final AsyncScheme that = (AsyncScheme) obj;
+            return this.name.equals(that.name)
+                && this.defaultPort == that.defaultPort
+                && this.strategy.equals(that.strategy);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = LangUtils.HASH_SEED;
+        hash = LangUtils.hashCode(hash, this.defaultPort);
+        hash = LangUtils.hashCode(hash, this.name);
+        hash = LangUtils.hashCode(hash, this.strategy);
+        return hash;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/AsyncSchemeRegistry.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/AsyncSchemeRegistry.java
new file mode 100644
index 0000000..9f2062e
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/AsyncSchemeRegistry.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.nio.conn.scheme;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.http.HttpHost;
+
+/**
+ * A set of supported protocol {@link AsyncScheme}s.
+ * Schemes are identified by lowercase names.
+ *
+ */
+@Deprecated
+public final class AsyncSchemeRegistry {
+
+    /** The available schemes in this registry. */
+    private final Map<String, AsyncScheme> registeredSchemes;
+
+    /**
+     * Creates a new, empty scheme registry.
+     */
+    public AsyncSchemeRegistry() {
+        super();
+        registeredSchemes = new ConcurrentHashMap<String, AsyncScheme>();
+    }
+
+    /**
+     * Obtains a scheme by name.
+     *
+     * @param name      the name of the scheme to look up (in lowercase)
+     *
+     * @return  the scheme, never {@code null}
+     *
+     * @throws IllegalStateException
+     *          if the scheme with the given name is not registered
+     */
+    public final AsyncScheme getScheme(final String name) {
+        final AsyncScheme found = get(name);
+        if (found == null) {
+            throw new IllegalStateException
+                ("Scheme '"+name+"' not registered.");
+        }
+        return found;
+    }
+
+    /**
+     * Obtains the scheme for a host.
+     * Convenience method for {@code getScheme(host.getSchemeName())}
+     *
+     * @param host      the host for which to obtain the scheme
+     *
+     * @return  the scheme for the given host, never {@code null}
+     *
+     * @throws IllegalStateException
+     *          if a scheme with the respective name is not registered
+     */
+    public final AsyncScheme getScheme(final HttpHost host) {
+        if (host == null) {
+            throw new IllegalArgumentException("Host must not be null.");
+        }
+        return getScheme(host.getSchemeName());
+    }
+
+    /**
+     * Obtains a scheme by name, if registered.
+     *
+     * @param name      the name of the scheme to look up (in lowercase)
+     *
+     * @return  the scheme, or
+     *          {@code null} if there is none by this name
+     */
+    public final AsyncScheme get(final String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Name must not be null.");
+        }
+
+        // leave it to the caller to use the correct name - all lowercase
+        //name = name.toLowerCase(Locale.ENGLISH);
+        final AsyncScheme found = registeredSchemes.get(name);
+        return found;
+    }
+
+    /**
+     * Registers a scheme.
+     * The scheme can later be retrieved by its name
+     * using {@link #getScheme(String) getScheme} or {@link #get get}.
+     *
+     * @param sch       the scheme to register
+     *
+     * @return  the scheme previously registered with that name, or
+     *          {@code null} if none was registered
+     */
+    public final AsyncScheme register(final AsyncScheme sch) {
+        if (sch == null) {
+            throw new IllegalArgumentException("Scheme must not be null.");
+        }
+
+        final AsyncScheme old = registeredSchemes.put(sch.getName(), sch);
+        return old;
+    }
+
+    /**
+     * Unregisters a scheme.
+     *
+     * @param name      the name of the scheme to unregister (in lowercase)
+     *
+     * @return  the unregistered scheme, or
+     *          {@code null} if there was none
+     */
+    public final AsyncScheme unregister(final String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("Name must not be null.");
+        }
+
+        // leave it to the caller to use the correct name - all lowercase
+        //name = name.toLowerCase(Locale.ENGLISH);
+        final AsyncScheme gone = registeredSchemes.remove(name);
+        return gone;
+    }
+
+    /**
+     * Obtains the names of the registered schemes.
+     *
+     * @return  List containing registered scheme names.
+     */
+    public final List<String> getSchemeNames() {
+        return new ArrayList<String>(registeredSchemes.keySet());
+    }
+
+    /**
+     * Populates the internal collection of registered {@link AsyncScheme protocol schemes}
+     * with the content of the map passed as a parameter.
+     *
+     * @param map protocol schemes
+     */
+    public void setItems(final Map<String, AsyncScheme> map) {
+        if (map == null) {
+            return;
+        }
+        registeredSchemes.clear();
+        registeredSchemes.putAll(map);
+    }
+
+}
+
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/LayeringStrategy.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/LayeringStrategy.java
new file mode 100644
index 0000000..c794acf
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/LayeringStrategy.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.nio.conn.scheme;
+
+import org.apache.http.nio.reactor.IOSession;
+
+@Deprecated
+public interface LayeringStrategy {
+
+    boolean isSecure();
+
+    IOSession layer(IOSession iosession);
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/package-info.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/package-info.java
new file mode 100644
index 0000000..7284593
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/scheme/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * ====================================================================
+ * 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/>.
+ *
+ */
+
+/**
+ * Deprecated
+ */
+package org.apache.http.nio.conn.scheme;
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ssl/SSLLayeringStrategy.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ssl/SSLLayeringStrategy.java
new file mode 100644
index 0000000..e1c852d
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ssl/SSLLayeringStrategy.java
@@ -0,0 +1,225 @@
+/*
+ * ====================================================================
+ * 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.nio.conn.ssl;
+
+import java.net.InetSocketAddress;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
+import org.apache.http.conn.ssl.SSLContexts;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.nio.conn.scheme.LayeringStrategy;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.nio.reactor.ssl.SSLIOSession;
+import org.apache.http.nio.reactor.ssl.SSLMode;
+import org.apache.http.nio.reactor.ssl.SSLSetupHandler;
+
+@Deprecated
+public class SSLLayeringStrategy implements LayeringStrategy {
+
+    public static final String TLS   = "TLS";
+    public static final String SSL   = "SSL";
+    public static final String SSLV2 = "SSLv2";
+
+    public static SSLLayeringStrategy getDefaultStrategy() {
+        return new SSLLayeringStrategy(SSLContexts.createDefault());
+    }
+
+    public static SSLLayeringStrategy getSystemDefaultStrategy() {
+        return new SSLLayeringStrategy(SSLContexts.createSystemDefault());
+    }
+
+    private final SSLContext sslContext;
+    private final X509HostnameVerifier hostnameVerifier;
+
+    private static SSLContext createSSLContext(
+            final String algorithm,
+            final KeyStore keystore,
+            final String keystorePassword,
+            final KeyStore truststore,
+            final SecureRandom random,
+            final TrustStrategy trustStrategy)
+                throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, KeyManagementException {
+        final String algo = algorithm != null ? algorithm : TLS;
+        final KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
+                KeyManagerFactory.getDefaultAlgorithm());
+        kmfactory.init(keystore, keystorePassword != null ? keystorePassword.toCharArray(): null);
+        final KeyManager[] keymanagers =  kmfactory.getKeyManagers();
+        final TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
+                TrustManagerFactory.getDefaultAlgorithm());
+        tmfactory.init(truststore);
+        final TrustManager[] trustmanagers = tmfactory.getTrustManagers();
+        if (trustmanagers != null && trustStrategy != null) {
+            for (int i = 0; i < trustmanagers.length; i++) {
+                final TrustManager tm = trustmanagers[i];
+                if (tm instanceof X509TrustManager) {
+                    trustmanagers[i] = new TrustManagerDecorator(
+                            (X509TrustManager) tm, trustStrategy);
+                }
+            }
+        }
+        final SSLContext sslcontext = SSLContext.getInstance(algo);
+        sslcontext.init(keymanagers, trustmanagers, random);
+        return sslcontext;
+    }
+
+    public SSLLayeringStrategy(
+            final String algorithm,
+            final KeyStore keystore,
+            final String keystorePassword,
+            final KeyStore truststore,
+            final SecureRandom random,
+            final X509HostnameVerifier hostnameVerifier)
+                throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        this(createSSLContext(
+                algorithm, keystore, keystorePassword, truststore, random, null),
+                hostnameVerifier);
+    }
+
+    public SSLLayeringStrategy(
+            final String algorithm,
+            final KeyStore keystore,
+            final String keystorePassword,
+            final KeyStore truststore,
+            final SecureRandom random,
+            final TrustStrategy trustStrategy,
+            final X509HostnameVerifier hostnameVerifier)
+                throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        this(createSSLContext(
+                algorithm, keystore, keystorePassword, truststore, random, trustStrategy),
+                hostnameVerifier);
+    }
+
+    public SSLLayeringStrategy(
+            final KeyStore keystore,
+            final String keystorePassword,
+            final KeyStore truststore)
+                throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        this(TLS, keystore, keystorePassword, truststore, null, null, new BrowserCompatHostnameVerifier());
+    }
+
+    public SSLLayeringStrategy(
+            final KeyStore keystore,
+            final String keystorePassword)
+                throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException{
+        this(TLS, keystore, keystorePassword, null, null, null, new BrowserCompatHostnameVerifier());
+    }
+
+    public SSLLayeringStrategy(
+            final KeyStore truststore)
+                throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        this(TLS, null, null, truststore, null, null, new BrowserCompatHostnameVerifier());
+    }
+
+    public SSLLayeringStrategy(
+            final TrustStrategy trustStrategy,
+            final X509HostnameVerifier hostnameVerifier)
+                throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        this(TLS, null, null, null, null, trustStrategy, hostnameVerifier);
+    }
+
+    public SSLLayeringStrategy(
+            final TrustStrategy trustStrategy)
+                throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        this(TLS, null, null, null, null, trustStrategy, new BrowserCompatHostnameVerifier());
+    }
+
+    public SSLLayeringStrategy(
+            final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) {
+        super();
+        this.sslContext = sslContext;
+        this.hostnameVerifier = hostnameVerifier;
+    }
+
+    public SSLLayeringStrategy(final SSLContext sslContext) {
+        this(sslContext, new BrowserCompatHostnameVerifier());
+    }
+
+    @Override
+    public boolean isSecure() {
+        return true;
+    }
+
+    @Override
+    public SSLIOSession layer(final IOSession iosession) {
+        final SSLIOSession ssliosession = new SSLIOSession(
+            iosession,
+            SSLMode.CLIENT,
+            this.sslContext,
+            new SSLSetupHandler() {
+
+                @Override
+                public void initalize(
+                        final SSLEngine sslengine) throws SSLException {
+                    initializeEngine(sslengine);
+                }
+
+                @Override
+                public void verify(
+                        final IOSession iosession,
+                        final SSLSession sslsession) throws SSLException {
+                    verifySession(iosession, sslsession);
+                }
+
+        });
+        iosession.setAttribute(SSLIOSession.SESSION_KEY, ssliosession);
+        return ssliosession;
+    }
+
+    protected void initializeEngine(final SSLEngine engine) {
+    }
+
+    protected void verifySession(
+            final IOSession iosession,
+            final SSLSession sslsession) throws SSLException {
+        final InetSocketAddress address = (InetSocketAddress) iosession.getRemoteAddress();
+
+        final Certificate[] certs = sslsession.getPeerCertificates();
+        final X509Certificate x509 = (X509Certificate) certs[0];
+        this.hostnameVerifier.verify(address.getHostName(), x509);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ssl/TrustManagerDecorator.java b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ssl/TrustManagerDecorator.java
new file mode 100644
index 0000000..af50d99
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java-deprecated/org/apache/http/nio/conn/ssl/TrustManagerDecorator.java
@@ -0,0 +1,67 @@
+/*
+ * ====================================================================
+ * 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.nio.conn.ssl;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.http.conn.ssl.TrustStrategy;
+
+@Deprecated
+class TrustManagerDecorator implements X509TrustManager {
+
+    private final X509TrustManager trustManager;
+    private final TrustStrategy trustStrategy;
+
+    TrustManagerDecorator(final X509TrustManager trustManager, final TrustStrategy trustStrategy) {
+        super();
+        this.trustManager = trustManager;
+        this.trustStrategy = trustStrategy;
+    }
+
+    @Override
+    public void checkClientTrusted(
+            final X509Certificate[] chain, final String authType) throws CertificateException {
+        this.trustManager.checkClientTrusted(chain, authType);
+    }
+
+    @Override
+    public void checkServerTrusted(
+            final X509Certificate[] chain, final String authType) throws CertificateException {
+        if (!this.trustStrategy.isTrusted(chain, authType)) {
+            this.trustManager.checkServerTrusted(chain, authType);
+        }
+    }
+
+    @Override
+    public X509Certificate[] getAcceptedIssuers() {
+        return this.trustManager.getAcceptedIssuers();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/AbstractClientExchangeHandler.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/AbstractClientExchangeHandler.java
new file mode 100644
index 0000000..6e822d5
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/AbstractClientExchangeHandler.java
@@ -0,0 +1,454 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.ConnectionClosedException;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.RouteTracker;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.protocol.HttpAsyncClientExchangeHandler;
+import org.apache.http.nio.protocol.HttpAsyncRequestExecutor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.Asserts;
+
+/**
+ * Abstract {@link org.apache.http.nio.protocol.HttpAsyncClientExchangeHandler} class
+ * that implements connection management aspects shared by all HTTP exchange handlers.
+ * <p>
+ * Instances of this class are expected to be accessed by one thread at a time only.
+ * The {@link #cancel()} method can be called concurrently by multiple threads.
+ */
+abstract class AbstractClientExchangeHandler implements HttpAsyncClientExchangeHandler {
+
+    private static final AtomicLong COUNTER = new AtomicLong(1);
+
+    protected final Log log;
+
+    private final long id;
+    private final HttpClientContext localContext;
+    private final NHttpClientConnectionManager connmgr;
+    private final ConnectionReuseStrategy connReuseStrategy;
+    private final ConnectionKeepAliveStrategy keepaliveStrategy;
+    private final AtomicReference<Future<NHttpClientConnection>> connectionFutureRef;
+    private final AtomicReference<NHttpClientConnection> managedConnRef;
+    private final AtomicReference<HttpRoute> routeRef;
+    private final AtomicReference<RouteTracker> routeTrackerRef;
+    private final AtomicBoolean routeEstablished;
+    private final AtomicReference<Long> validDurationRef;
+    private final AtomicReference<HttpRequestWrapper> requestRef;
+    private final AtomicReference<HttpResponse> responseRef;
+    private final AtomicBoolean completed;
+    private final AtomicBoolean closed;
+
+    AbstractClientExchangeHandler(
+            final Log log,
+            final HttpClientContext localContext,
+            final NHttpClientConnectionManager connmgr,
+            final ConnectionReuseStrategy connReuseStrategy,
+            final ConnectionKeepAliveStrategy keepaliveStrategy) {
+        super();
+        this.log = log;
+        this.id = COUNTER.getAndIncrement();
+        this.localContext = localContext;
+        this.connmgr = connmgr;
+        this.connReuseStrategy = connReuseStrategy;
+        this.keepaliveStrategy = keepaliveStrategy;
+        this.connectionFutureRef = new AtomicReference<Future<NHttpClientConnection>>(null);
+        this.managedConnRef = new AtomicReference<NHttpClientConnection>(null);
+        this.routeRef = new AtomicReference<HttpRoute>(null);
+        this.routeTrackerRef = new AtomicReference<RouteTracker>(null);
+        this.routeEstablished = new AtomicBoolean(false);
+        this.validDurationRef = new AtomicReference<Long>(null);
+        this.requestRef = new AtomicReference<HttpRequestWrapper>(null);
+        this.responseRef = new AtomicReference<HttpResponse>(null);
+        this.completed = new AtomicBoolean(false);
+        this.closed = new AtomicBoolean(false);
+    }
+
+    final long getId() {
+        return this.id;
+    }
+
+    final boolean isCompleted() {
+        return this.completed.get();
+    }
+
+    final void markCompleted() {
+        this.completed.set(true);
+    }
+
+    final void markConnectionNonReusable() {
+        this.validDurationRef.set(null);
+    }
+
+    final boolean isRouteEstablished() {
+        return this.routeEstablished.get();
+    }
+
+    final HttpRoute getRoute() {
+        return this.routeRef.get();
+    }
+
+    final void setRoute(final HttpRoute route) {
+        this.routeRef.set(route);
+    }
+
+    final HttpRequestWrapper getCurrentRequest() {
+        return this.requestRef.get();
+    }
+
+    final void setCurrentRequest(final HttpRequestWrapper request) {
+        this.requestRef.set(request);
+    }
+
+    final HttpResponse getCurrentResponse() {
+        return this.responseRef.get();
+    }
+
+    final void setCurrentResponse(final HttpResponse response) {
+        this.responseRef.set(response);
+    }
+
+    final HttpRoute getActualRoute() {
+        final RouteTracker routeTracker = this.routeTrackerRef.get();
+        return routeTracker != null ? routeTracker.toRoute() : null;
+    }
+
+    final void verifytRoute() {
+        if (!this.routeEstablished.get() && this.routeTrackerRef.get() == null) {
+            final NHttpClientConnection managedConn = this.managedConnRef.get();
+            Asserts.check(managedConn != null, "Inconsistent state: managed connection is null");
+            final boolean routeComplete = this.connmgr.isRouteComplete(managedConn);
+            this.routeEstablished.set(routeComplete);
+            if (!routeComplete) {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + this.id + "] Start connection routing");
+                }
+                final HttpRoute route = this.routeRef.get();
+                this.routeTrackerRef.set(new RouteTracker(route));
+            } else {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + this.id + "] Connection route already established");
+                }
+            }
+        }
+    }
+
+    final void onRouteToTarget() throws IOException {
+        final NHttpClientConnection managedConn = this.managedConnRef.get();
+        Asserts.check(managedConn != null, "Inconsistent state: managed connection is null");
+        final HttpRoute route = this.routeRef.get();
+        Asserts.check(route != null, "Inconsistent state: HTTP route is null");
+        final RouteTracker routeTracker = this.routeTrackerRef.get();
+        Asserts.check(routeTracker != null, "Inconsistent state: HTTP route tracker");
+        this.connmgr.startRoute(managedConn, route, this.localContext);
+        routeTracker.connectTarget(route.isSecure());
+    }
+
+    final void onRouteToProxy() throws IOException {
+        final NHttpClientConnection managedConn = this.managedConnRef.get();
+        Asserts.check(managedConn != null, "Inconsistent state: managed connection is null");
+        final HttpRoute route = this.routeRef.get();
+        Asserts.check(route != null, "Inconsistent state: HTTP route is null");
+        final RouteTracker routeTracker = this.routeTrackerRef.get();
+        Asserts.check(routeTracker != null, "Inconsistent state: HTTP route tracker");
+        this.connmgr.startRoute(managedConn, route, this.localContext);
+        final HttpHost proxy  = route.getProxyHost();
+        routeTracker.connectProxy(proxy, false);
+    }
+
+    final void onRouteUpgrade() throws IOException {
+        final NHttpClientConnection managedConn = this.managedConnRef.get();
+        Asserts.check(managedConn != null, "Inconsistent state: managed connection is null");
+        final HttpRoute route = this.routeRef.get();
+        Asserts.check(route != null, "Inconsistent state: HTTP route is null");
+        final RouteTracker routeTracker = this.routeTrackerRef.get();
+        Asserts.check(routeTracker != null, "Inconsistent state: HTTP route tracker");
+        this.connmgr.upgrade(managedConn, route, this.localContext);
+        routeTracker.layerProtocol(route.isSecure());
+    }
+
+    final void onRouteTunnelToTarget() {
+        final RouteTracker routeTracker = this.routeTrackerRef.get();
+        Asserts.check(routeTracker != null, "Inconsistent state: HTTP route tracker");
+        routeTracker.tunnelTarget(false);
+    }
+
+    final void onRouteComplete() {
+        final NHttpClientConnection managedConn = this.managedConnRef.get();
+        Asserts.check(managedConn != null, "Inconsistent state: managed connection is null");
+        final HttpRoute route = this.routeRef.get();
+        Asserts.check(route != null, "Inconsistent state: HTTP route is null");
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] route completed");
+        }
+        this.connmgr.routeComplete(managedConn, route, this.localContext);
+        this.routeEstablished.set(true);
+        this.routeTrackerRef.set(null);
+    }
+
+    final NHttpClientConnection getConnection() {
+        return this.managedConnRef.get();
+    }
+
+    final void releaseConnection() {
+        final NHttpClientConnection localConn = this.managedConnRef.getAndSet(null);
+        if (localConn != null) {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("[exchange: " + this.id + "] releasing connection");
+            }
+            localConn.getContext().removeAttribute(HttpAsyncRequestExecutor.HTTP_HANDLER);
+            final Long validDuration = this.validDurationRef.get();
+            if (validDuration != null) {
+                final Object userToken = this.localContext.getUserToken();
+                this.connmgr.releaseConnection(localConn, userToken, validDuration, TimeUnit.MILLISECONDS);
+            } else {
+                try {
+                    localConn.close();
+                    if (this.log.isDebugEnabled()) {
+                        this.log.debug("[exchange: " + this.id + "] connection discarded");
+                    }
+                } catch (final IOException ex) {
+                    if (this.log.isDebugEnabled()) {
+                        this.log.debug("[exchange: " + this.id + "] " + ex.getMessage(), ex);
+                    }
+                } finally {
+                    this.connmgr.releaseConnection(localConn, null, 0, TimeUnit.MILLISECONDS);
+                }
+            }
+        }
+    }
+
+    final void discardConnection() {
+        final NHttpClientConnection localConn = this.managedConnRef.getAndSet(null);
+        if (localConn != null) {
+            try {
+                localConn.shutdown();
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + this.id + "] connection aborted");
+                }
+            } catch (final IOException ex) {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + this.id + "] " + ex.getMessage(), ex);
+                }
+            } finally {
+                this.connmgr.releaseConnection(localConn, null, 0, TimeUnit.MILLISECONDS);
+            }
+        }
+    }
+
+    final boolean manageConnectionPersistence() {
+        final HttpResponse response = this.responseRef.get();
+        Asserts.check(response != null, "Inconsistent state: HTTP response");
+        final NHttpClientConnection managedConn = this.managedConnRef.get();
+        Asserts.check(managedConn != null, "Inconsistent state: managed connection is null");
+        final boolean keepAlive = managedConn.isOpen() &&
+                this.connReuseStrategy.keepAlive(response, this.localContext);
+        if (keepAlive) {
+            final long validDuration = this.keepaliveStrategy.getKeepAliveDuration(
+                    response, this.localContext);
+            if (this.log.isDebugEnabled()) {
+                final String s;
+                if (validDuration > 0) {
+                    s = "for " + validDuration + " " + TimeUnit.MILLISECONDS;
+                } else {
+                    s = "indefinitely";
+                }
+                this.log.debug("[exchange: " + this.id + "] Connection can be kept alive " + s);
+            }
+            this.validDurationRef.set(validDuration);
+        } else {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("[exchange: " + this.id + "] Connection cannot be kept alive");
+            }
+            this.validDurationRef.set(null);
+        }
+        return keepAlive;
+    }
+
+    private void connectionAllocated(final NHttpClientConnection managedConn) {
+        try {
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("[exchange: " + this.id + "] Connection allocated: " + managedConn);
+            }
+            this.connectionFutureRef.set(null);
+            this.managedConnRef.set(managedConn);
+
+            if (this.closed.get()) {
+                discardConnection();
+                return;
+            }
+
+            if (this.connmgr.isRouteComplete(managedConn)) {
+                this.routeEstablished.set(true);
+                this.routeTrackerRef.set(null);
+            }
+
+            final HttpContext context = managedConn.getContext();
+            synchronized (context) {
+                context.setAttribute(HttpAsyncRequestExecutor.HTTP_HANDLER, this);
+                if (managedConn.isStale()) {
+                    failed(new ConnectionClosedException("Connection closed"));
+                } else {
+                    managedConn.requestOutput();
+                }
+            }
+        } catch (final RuntimeException runex) {
+            failed(runex);
+            throw runex;
+        }
+    }
+
+    private void connectionRequestFailed(final Exception ex) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] connection request failed");
+        }
+        this.connectionFutureRef.set(null);
+        failed(ex);
+    }
+
+    private void connectionRequestCancelled() {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Connection request cancelled");
+        }
+        this.connectionFutureRef.set(null);
+        try {
+            executionCancelled();
+        } finally {
+            close();
+        }
+    }
+
+    final void requestConnection() {
+        final HttpRoute route = this.routeRef.get();
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Request connection for " + route);
+        }
+
+        discardConnection();
+
+        this.validDurationRef.set(null);
+        this.routeTrackerRef.set(null);
+        this.routeEstablished.set(false);
+
+        final Object userToken = this.localContext.getUserToken();
+        final RequestConfig config = this.localContext.getRequestConfig();
+        this.connectionFutureRef.set(this.connmgr.requestConnection(
+                route,
+                userToken,
+                config.getConnectTimeout(),
+                config.getConnectionRequestTimeout(),
+                TimeUnit.MILLISECONDS,
+                new FutureCallback<NHttpClientConnection>() {
+
+                    @Override
+                    public void completed(final NHttpClientConnection managedConn) {
+                        connectionAllocated(managedConn);
+                    }
+
+                    @Override
+                    public void failed(final Exception ex) {
+                        connectionRequestFailed(ex);
+                    }
+
+                    @Override
+                    public void cancelled() {
+                        connectionRequestCancelled();
+                    }
+
+                }));
+    }
+
+    abstract void releaseResources();
+
+    abstract void executionFailed(final Exception ex);
+
+    abstract boolean executionCancelled();
+
+    @Override
+    public final void close() {
+        if (this.closed.compareAndSet(false, true)) {
+            discardConnection();
+            releaseResources();
+        }
+    }
+
+    @Override
+    public final boolean isDone() {
+        return this.completed.get();
+    }
+
+    @Override
+    public final void failed(final Exception ex) {
+        if (this.closed.compareAndSet(false, true)) {
+            try {
+                executionFailed(ex);
+            } finally {
+                discardConnection();
+                releaseResources();
+            }
+        }
+    }
+
+    @Override
+    public final boolean cancel() {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + this.id + "] Cancelled");
+        }
+        if (this.closed.compareAndSet(false, true)) {
+            try {
+                final Future<NHttpClientConnection> connectionFuture = this.connectionFutureRef.getAndSet(null);
+                if (connectionFuture != null) {
+                    connectionFuture.cancel(true);
+                }
+                return executionCancelled();
+            } finally {
+                discardConnection();
+                releaseResources();
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/CloseableHttpAsyncClient.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/CloseableHttpAsyncClient.java
new file mode 100644
index 0000000..7f6cfe9
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/CloseableHttpAsyncClient.java
@@ -0,0 +1,128 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.Closeable;
+import java.net.URI;
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.nio.client.HttpAsyncClient;
+import org.apache.http.nio.client.methods.HttpAsyncMethods;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.Args;
+
+/**
+ * Base implementation of {@link HttpAsyncClient} that also implements {@link Closeable}.
+ *
+ * @since 4.0
+ */
+@Contract(threading = ThreadingBehavior.SAFE)
+public abstract class CloseableHttpAsyncClient implements HttpAsyncClient, Closeable {
+
+    public abstract boolean isRunning();
+
+    public abstract void start();
+
+    @Override
+    public <T> Future<T> execute(
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final FutureCallback<T> callback) {
+        return execute(requestProducer, responseConsumer, HttpClientContext.create(), callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpHost target, final HttpRequest request, final HttpContext context,
+            final FutureCallback<HttpResponse> callback) {
+        return execute(
+                HttpAsyncMethods.create(target, request),
+                HttpAsyncMethods.createConsumer(),
+                context, callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpHost target, final HttpRequest request,
+            final FutureCallback<HttpResponse> callback) {
+        return execute(target, request, HttpClientContext.create(), callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpUriRequest request,
+            final FutureCallback<HttpResponse> callback) {
+        return execute(request, HttpClientContext.create(), callback);
+    }
+
+    @Override
+    public Future<HttpResponse> execute(
+            final HttpUriRequest request,
+            final HttpContext context,
+            final FutureCallback<HttpResponse> callback) {
+        final HttpHost target;
+        try {
+            target = determineTarget(request);
+        } catch (final ClientProtocolException ex) {
+            final BasicFuture<HttpResponse> future = new BasicFuture<HttpResponse>(callback);
+            future.failed(ex);
+            return future;
+        }
+        return execute(target, request, context, callback);
+    }
+
+    private HttpHost determineTarget(final HttpUriRequest request) throws ClientProtocolException {
+        Args.notNull(request, "HTTP request");
+        // A null target may be acceptable if there is a default target.
+        // Otherwise, the null target is detected in the director.
+        HttpHost target = null;
+
+        final URI requestURI = request.getURI();
+        if (requestURI.isAbsolute()) {
+            target = URIUtils.extractHost(requestURI);
+            if (target == null) {
+                throw new ClientProtocolException(
+                        "URI does not specify a valid host name: " + requestURI);
+            }
+        }
+        return target;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/CloseableHttpAsyncClientBase.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/CloseableHttpAsyncClientBase.java
new file mode 100644
index 0000000..5c12ce3
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/CloseableHttpAsyncClientBase.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.nio.client;
+
+import java.io.IOException;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.nio.NHttpClientEventHandler;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.util.Asserts;
+
+abstract class CloseableHttpAsyncClientBase extends CloseableHttpPipeliningClient {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    static enum Status {INACTIVE, ACTIVE, STOPPED}
+
+    private final NHttpClientConnectionManager connmgr;
+    private final Thread reactorThread;
+
+    private final AtomicReference<Status> status;
+
+    public CloseableHttpAsyncClientBase(
+            final NHttpClientConnectionManager connmgr,
+            final ThreadFactory threadFactory,
+            final NHttpClientEventHandler handler) {
+        super();
+        this.connmgr = connmgr;
+        if (threadFactory != null && handler != null) {
+            this.reactorThread = threadFactory.newThread(new Runnable() {
+
+                @Override
+                public void run() {
+                    try {
+                        final IOEventDispatch ioEventDispatch = new InternalIODispatch(handler);
+                        connmgr.execute(ioEventDispatch);
+                    } catch (final Exception ex) {
+                        log.error("I/O reactor terminated abnormally", ex);
+                    } finally {
+                        status.set(Status.STOPPED);
+                    }
+                }
+
+            });
+        } else {
+            this.reactorThread = null;
+        }
+        this.status = new AtomicReference<Status>(Status.INACTIVE);
+    }
+
+    @Override
+    public void start() {
+        if (this.status.compareAndSet(Status.INACTIVE, Status.ACTIVE)) {
+            if (this.reactorThread != null) {
+                this.reactorThread.start();
+            }
+        }
+    }
+
+    protected void ensureRunning() {
+        final Status currentStatus = this.status.get();
+        Asserts.check(currentStatus == Status.ACTIVE, "Request cannot be executed; " +
+                "I/O reactor status: %s", currentStatus);
+    }
+
+    @Override
+    public void close() {
+        if (this.status.compareAndSet(Status.ACTIVE, Status.STOPPED)) {
+            if (this.reactorThread != null) {
+                try {
+                    this.connmgr.shutdown();
+                } catch (final IOException ex) {
+                    this.log.error("I/O error shutting down connection manager", ex);
+                }
+                try {
+                    this.reactorThread.join();
+                } catch (final InterruptedException ex) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean isRunning() {
+        return this.status.get() == Status.ACTIVE;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/CloseableHttpPipeliningClient.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/CloseableHttpPipeliningClient.java
new file mode 100644
index 0000000..9f0d648
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/CloseableHttpPipeliningClient.java
@@ -0,0 +1,93 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.nio.client.HttpPipeliningClient;
+import org.apache.http.nio.client.methods.HttpAsyncMethods;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.Args;
+
+/**
+ * Base implementation of {@link org.apache.http.nio.client.HttpPipeliningClient} that also
+ * implements {@link java.io.Closeable}.
+ *
+ * @since 4.1
+ */
+@Contract(threading = ThreadingBehavior.SAFE)
+public abstract class CloseableHttpPipeliningClient
+        extends CloseableHttpAsyncClient implements HttpPipeliningClient {
+
+    @Override
+    public <T> Future<List<T>> execute(
+            final HttpHost target,
+            final List<? extends HttpAsyncRequestProducer> requestProducers,
+            final List<? extends HttpAsyncResponseConsumer<T>> responseConsumers,
+            final FutureCallback<List<T>> callback) {
+        return execute(target, requestProducers, responseConsumers, HttpClientContext.create(), callback);
+    }
+
+    @Override
+    public Future<List<HttpResponse>> execute(
+            final HttpHost target,
+            final List<HttpRequest> requests,
+            final FutureCallback<List<HttpResponse>> callback) {
+        return execute(target, requests, HttpClientContext.create(), callback);
+    }
+
+    @Override
+    public Future<List<HttpResponse>> execute(
+            final HttpHost target,
+            final List<HttpRequest> requests,
+            final HttpContext context,
+            final FutureCallback<List<HttpResponse>> callback) {
+        Args.notEmpty(requests, "HTTP request list");
+        final List<HttpAsyncRequestProducer> requestProducers = new ArrayList<HttpAsyncRequestProducer>(
+                requests.size());
+        final List<HttpAsyncResponseConsumer<HttpResponse>> responseConsumers = new ArrayList<HttpAsyncResponseConsumer<HttpResponse>>(
+                requests.size());
+        for (int i = 0; i < requests.size(); i++) {
+            final HttpRequest request = requests.get(i);
+            requestProducers.add(HttpAsyncMethods.create(target, request));
+            responseConsumers.add(HttpAsyncMethods.createConsumer());
+        }
+        return execute(target, requestProducers, responseConsumers, context, callback);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultAsyncUserTokenHandler.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultAsyncUserTokenHandler.java
new file mode 100644
index 0000000..d26dc71
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultAsyncUserTokenHandler.java
@@ -0,0 +1,104 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import org.apache.http.HttpConnection;
+import org.apache.http.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.Credentials;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.protocol.HttpContext;
+
+import javax.net.ssl.SSLSession;
+import java.security.Principal;
+
+/**
+ * Default implementation of {@link org.apache.http.client.UserTokenHandler}
+ * for asynchrounous HTTP client communication. This class will use
+ * an instance of {@link java.security.Principal} as a state object for
+ * non-blocking HTTP connections, if it can be obtained from the given
+ * execution context. This helps ensure persistent connections created with
+ * a particular user identity within a particular security context can be
+ * reused by the same user only.
+ * <p>
+ * This implementation will use the user principle of connection based
+ * authentication schemes such as NTLM or that of the SSL session with
+ * the client authentication turned on. If both are unavailable,
+ * {@code null} token will be returned.
+ *
+ * @since 4.0
+ */
+@Contract(threading = ThreadingBehavior.IMMUTABLE)
+public class DefaultAsyncUserTokenHandler implements UserTokenHandler {
+
+    public static final DefaultAsyncUserTokenHandler INSTANCE = new DefaultAsyncUserTokenHandler();
+
+    @Override
+    public Object getUserToken(final HttpContext context) {
+
+        final HttpClientContext clientContext = HttpClientContext.adapt(context);
+
+        Principal userPrincipal = null;
+
+        final AuthState targetAuthState = clientContext.getTargetAuthState();
+        if (targetAuthState != null) {
+            userPrincipal = getAuthPrincipal(targetAuthState);
+            if (userPrincipal == null) {
+                final AuthState proxyAuthState = clientContext.getProxyAuthState();
+                userPrincipal = getAuthPrincipal(proxyAuthState);
+            }
+        }
+
+        if (userPrincipal == null) {
+            final HttpConnection conn = clientContext.getConnection();
+            if (conn.isOpen() && conn instanceof ManagedNHttpClientConnection) {
+                final SSLSession sslsession = ((ManagedNHttpClientConnection) conn).getSSLSession();
+                if (sslsession != null) {
+                    userPrincipal = sslsession.getLocalPrincipal();
+                }
+            }
+        }
+
+        return userPrincipal;
+    }
+
+    private static Principal getAuthPrincipal(final AuthState authState) {
+        final AuthScheme scheme = authState.getAuthScheme();
+        if (scheme != null && scheme.isComplete() && scheme.isConnectionBased()) {
+            final Credentials creds = authState.getCredentials();
+            if (creds != null) {
+                return creds.getUserPrincipal();
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultClientExchangeHandlerImpl.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultClientExchangeHandlerImpl.java
new file mode 100644
index 0000000..9b7f6d9
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultClientExchangeHandlerImpl.java
@@ -0,0 +1,215 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpExecutionAware;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+
+/**
+ * Default implementation of {@link org.apache.http.nio.protocol.HttpAsyncClientExchangeHandler}.
+ * <p>
+ * Instances of this class are expected to be accessed by one thread at a time only.
+ * The {@link #cancel()} method can be called concurrently by multiple threads.
+ */
+class DefaultClientExchangeHandlerImpl<T> extends AbstractClientExchangeHandler {
+
+    private final HttpAsyncRequestProducer requestProducer;
+    private final HttpAsyncResponseConsumer<T> responseConsumer;
+    private final BasicFuture<T> resultFuture;
+    private final InternalClientExec exec;
+    private final InternalState state;
+
+    public DefaultClientExchangeHandlerImpl(
+            final Log log,
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final HttpClientContext localContext,
+            final BasicFuture<T> resultFuture,
+            final NHttpClientConnectionManager connmgr,
+            final ConnectionReuseStrategy connReuseStrategy,
+            final ConnectionKeepAliveStrategy keepaliveStrategy,
+            final InternalClientExec exec) {
+        super(log, localContext, connmgr, connReuseStrategy, keepaliveStrategy);
+        this.requestProducer = requestProducer;
+        this.responseConsumer = responseConsumer;
+        this.resultFuture = resultFuture;
+        this.exec = exec;
+        this.state = new InternalState(getId(), requestProducer, responseConsumer, localContext);
+    }
+
+    @Override
+    void releaseResources() {
+        try {
+            this.requestProducer.close();
+        } catch (final IOException ex) {
+            this.log.debug("I/O error closing request producer", ex);
+        }
+        try {
+            this.responseConsumer.close();
+        } catch (final IOException ex) {
+            this.log.debug("I/O error closing response consumer", ex);
+        }
+    }
+
+    @Override
+    void executionFailed(final Exception ex) {
+        try {
+            this.requestProducer.failed(ex);
+            this.responseConsumer.failed(ex);
+        } finally {
+            this.resultFuture.failed(ex);
+        }
+    }
+
+    @Override
+    boolean executionCancelled() {
+        final boolean cancelled = this.responseConsumer.cancel();
+
+        final T result = this.responseConsumer.getResult();
+        final Exception ex = this.responseConsumer.getException();
+        if (ex != null) {
+            this.resultFuture.failed(ex);
+        } else if (result != null) {
+            this.resultFuture.completed(result);
+        } else {
+            this.resultFuture.cancel();
+        }
+        return cancelled;
+    }
+
+    public void start() throws HttpException, IOException {
+        final HttpHost target = this.requestProducer.getTarget();
+        final HttpRequest original = this.requestProducer.generateRequest();
+
+        if (original instanceof HttpExecutionAware) {
+            ((HttpExecutionAware) original).setCancellable(this);
+        }
+        this.exec.prepare(target, original, this.state, this);
+        requestConnection();
+    }
+
+    @Override
+    public HttpRequest generateRequest() throws IOException, HttpException {
+        return this.exec.generateRequest(this.state, this);
+    }
+
+    @Override
+    public void produceContent(
+            final ContentEncoder encoder, final IOControl ioctrl) throws IOException {
+        this.exec.produceContent(this.state, encoder, ioctrl);
+    }
+
+    @Override
+    public void requestCompleted() {
+        this.exec.requestCompleted(this.state, this);
+    }
+
+    @Override
+    public void responseReceived(
+            final HttpResponse response) throws IOException, HttpException {
+        this.exec.responseReceived(response, this.state, this);
+    }
+
+    @Override
+    public void consumeContent(
+            final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
+        this.exec.consumeContent(this.state, decoder, ioctrl);
+        if (!decoder.isCompleted() && this.responseConsumer.isDone()) {
+            markConnectionNonReusable();
+            try {
+                markCompleted();
+                releaseConnection();
+                this.resultFuture.cancel();
+            } finally {
+                close();
+            }
+        }
+    }
+
+    @Override
+    public void responseCompleted() throws IOException, HttpException {
+        this.exec.responseCompleted(this.state, this);
+
+        if (this.state.getFinalResponse() != null || this.resultFuture.isDone()) {
+            try {
+                markCompleted();
+                releaseConnection();
+                final T result = this.responseConsumer.getResult();
+                final Exception ex = this.responseConsumer.getException();
+                if (ex == null) {
+                    this.resultFuture.completed(result);
+                } else {
+                    this.resultFuture.failed(ex);
+                }
+            } finally {
+                close();
+            }
+        } else {
+            NHttpClientConnection localConn = getConnection();
+            if (localConn != null && !localConn.isOpen()) {
+                releaseConnection();
+                localConn = null;
+            }
+            if (localConn != null) {
+                localConn.requestOutput();
+            } else {
+                requestConnection();
+            }
+        }
+    }
+
+    @Override
+    public void inputTerminated() {
+        if (!isCompleted()) {
+            requestConnection();
+        } else {
+            close();
+        }
+    }
+
+    public void abortConnection() {
+        discardConnection();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/FutureWrapper.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/FutureWrapper.java
new file mode 100644
index 0000000..f65e7e8
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/FutureWrapper.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.nio.client;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.http.concurrent.Cancellable;
+
+final class FutureWrapper<T> implements Future<T> {
+
+    private final Future<T> future;
+    private final Cancellable cancellable;
+
+    public FutureWrapper(final Future<T> future, final Cancellable cancellable) {
+        super();
+        this.future = future;
+        this.cancellable = cancellable;
+    }
+
+    @Override
+    public boolean cancel(final boolean mayInterruptIfRunning) {
+        try {
+            if (cancellable != null) {
+                cancellable.cancel();
+            }
+        } finally {
+            return future.cancel(mayInterruptIfRunning);
+        }
+    }
+
+    @Override
+    public boolean isCancelled() {
+        return future.isCancelled();
+    }
+
+    @Override
+    public boolean isDone() {
+        return future.isDone();
+    }
+
+    @Override
+    public T get() throws InterruptedException, ExecutionException {
+        return future.get();
+    }
+
+    @Override
+    public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+        return future.get(timeout, unit);
+    }
+
+    @Override
+    public String toString() {
+        return future.toString();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.java
new file mode 100644
index 0000000..7db471a
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/HttpAsyncClientBuilder.java
@@ -0,0 +1,885 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.net.ProxySelector;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.Header;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.HttpResponseInterceptor;
+import org.apache.http.auth.AuthSchemeProvider;
+import org.apache.http.client.AuthenticationStrategy;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.config.AuthSchemes;
+import org.apache.http.client.config.CookieSpecs;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.protocol.RequestAddCookies;
+import org.apache.http.client.protocol.RequestAuthCache;
+import org.apache.http.client.protocol.RequestClientConnControl;
+import org.apache.http.client.protocol.RequestDefaultHeaders;
+import org.apache.http.client.protocol.RequestExpectContinue;
+import org.apache.http.client.protocol.ResponseProcessCookies;
+import org.apache.http.config.ConnectionConfig;
+import org.apache.http.config.Lookup;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.SchemePortResolver;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.conn.util.PublicSuffixMatcher;
+import org.apache.http.conn.util.PublicSuffixMatcherLoader;
+import org.apache.http.cookie.CookieSpecProvider;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.NoConnectionReuseStrategy;
+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.impl.client.BasicCookieStore;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
+import org.apache.http.impl.client.DefaultRedirectStrategy;
+import org.apache.http.impl.client.NoopUserTokenHandler;
+import org.apache.http.impl.client.ProxyAuthenticationStrategy;
+import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
+import org.apache.http.impl.client.TargetAuthenticationStrategy;
+import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
+import org.apache.http.impl.conn.DefaultRoutePlanner;
+import org.apache.http.impl.conn.DefaultSchemePortResolver;
+import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
+import org.apache.http.impl.cookie.DefaultCookieSpecProvider;
+import org.apache.http.impl.cookie.IgnoreSpecProvider;
+import org.apache.http.impl.cookie.NetscapeDraftSpecProvider;
+import org.apache.http.impl.cookie.RFC6265CookieSpecProvider;
+import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
+import org.apache.http.impl.nio.reactor.IOReactorConfig;
+import org.apache.http.nio.NHttpClientEventHandler;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.conn.NoopIOSessionStrategy;
+import org.apache.http.nio.conn.SchemeIOSessionStrategy;
+import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
+import org.apache.http.nio.protocol.HttpAsyncRequestExecutor;
+import org.apache.http.nio.reactor.ConnectingIOReactor;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpProcessorBuilder;
+import org.apache.http.protocol.RequestContent;
+import org.apache.http.protocol.RequestTargetHost;
+import org.apache.http.protocol.RequestUserAgent;
+import org.apache.http.ssl.SSLContexts;
+import org.apache.http.util.TextUtils;
+import org.apache.http.util.VersionInfo;
+
+/**
+ * Builder for {@link CloseableHttpAsyncClient} instances.
+ * <p>
+ * When a particular component is not explicitly this class will
+ * use its default implementation. System properties will be taken
+ * into account when configuring the default implementations when
+ * {@link #useSystemProperties()} method is called prior to calling
+ * {@link #build()}.
+ * <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>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>https.protocols</li>
+ *  <li>https.cipherSuites</li>
+ *  <li>http.proxyHost</li>
+ *  <li>http.proxyPort</li>
+ *  <li>http.keepAlive</li>
+ *  <li>http.maxConnections</li>
+ *  <li>http.agent</li>
+ * </ul>
+ * <p>
+ * Please note that some settings used by this class can be mutually
+ * exclusive and may not apply when building {@link CloseableHttpAsyncClient}
+ * instances.
+ *
+ * @since 4.0
+ */
+public class HttpAsyncClientBuilder {
+
+    private NHttpClientConnectionManager connManager;
+    private boolean connManagerShared;
+    private SchemePortResolver schemePortResolver;
+    private SchemeIOSessionStrategy sslStrategy;
+    private HostnameVerifier hostnameVerifier;
+    private SSLContext sslcontext;
+    private ConnectionReuseStrategy reuseStrategy;
+    private ConnectionKeepAliveStrategy keepAliveStrategy;
+    private AuthenticationStrategy targetAuthStrategy;
+    private AuthenticationStrategy proxyAuthStrategy;
+    private UserTokenHandler userTokenHandler;
+    private HttpProcessor httpprocessor;
+
+    private LinkedList<HttpRequestInterceptor> requestFirst;
+    private LinkedList<HttpRequestInterceptor> requestLast;
+    private LinkedList<HttpResponseInterceptor> responseFirst;
+    private LinkedList<HttpResponseInterceptor> responseLast;
+
+    private HttpRoutePlanner routePlanner;
+    private RedirectStrategy redirectStrategy;
+    private Lookup<AuthSchemeProvider> authSchemeRegistry;
+    private Lookup<CookieSpecProvider> cookieSpecRegistry;
+    private CookieStore cookieStore;
+    private CredentialsProvider credentialsProvider;
+    private String userAgent;
+    private HttpHost proxy;
+    private Collection<? extends Header> defaultHeaders;
+    private IOReactorConfig defaultIOReactorConfig;
+    private ConnectionConfig defaultConnectionConfig;
+    private RequestConfig defaultRequestConfig;
+
+    private ThreadFactory threadFactory;
+    private NHttpClientEventHandler eventHandler;
+
+    private PublicSuffixMatcher publicSuffixMatcher;
+
+    private boolean systemProperties;
+    private boolean cookieManagementDisabled;
+    private boolean authCachingDisabled;
+    private boolean connectionStateDisabled;
+
+    private int maxConnTotal = 0;
+    private int maxConnPerRoute = 0;
+
+    public static HttpAsyncClientBuilder create() {
+        return new HttpAsyncClientBuilder();
+    }
+
+    protected HttpAsyncClientBuilder() {
+        super();
+    }
+
+    /**
+     * Assigns file containing public suffix matcher. Instances of this class can be created
+     * with {@link org.apache.http.conn.util.PublicSuffixMatcherLoader}.
+     *
+     * @see org.apache.http.conn.util.PublicSuffixMatcher
+     * @see org.apache.http.conn.util.PublicSuffixMatcherLoader
+     *
+     *   @since 4.1
+     */
+    public final HttpAsyncClientBuilder setPublicSuffixMatcher(final PublicSuffixMatcher publicSuffixMatcher) {
+        this.publicSuffixMatcher = publicSuffixMatcher;
+        return this;
+    }
+
+    /**
+     * Assigns {@link NHttpClientConnectionManager} instance.
+     */
+    public final HttpAsyncClientBuilder setConnectionManager(
+            final NHttpClientConnectionManager connManager) {
+        this.connManager = connManager;
+        return this;
+    }
+
+    /**
+     * Defines the connection manager is to be shared by multiple
+     * client instances.
+     * <p>
+     * If the connection manager is shared its life-cycle is expected
+     * to be managed by the caller and it will not be shut down
+     * if the client is closed.
+     *
+     * @param shared defines whether or not the connection manager can be shared
+     *  by multiple clients.
+     *
+     * @since 4.1
+     */
+    public final HttpAsyncClientBuilder setConnectionManagerShared(
+            final boolean shared) {
+        this.connManagerShared = shared;
+        return this;
+    }
+
+    /**
+     * Assigns {@link SchemePortResolver} instance.
+     */
+    public final HttpAsyncClientBuilder setSchemePortResolver(
+            final SchemePortResolver schemePortResolver) {
+        this.schemePortResolver = schemePortResolver;
+        return this;
+    }
+
+    /**
+     * Assigns maximum total connection value.
+     * <p>
+     * Please note this value can be overridden by the {@link #setConnectionManager(
+     *   org.apache.http.nio.conn.NHttpClientConnectionManager)} method.
+     */
+    public final HttpAsyncClientBuilder setMaxConnTotal(final int maxConnTotal) {
+        this.maxConnTotal = maxConnTotal;
+        return this;
+    }
+
+    /**
+     * Assigns maximum connection per route value.
+     * <p>
+     * Please note this value can be overridden by the {@link #setConnectionManager(
+     *   org.apache.http.nio.conn.NHttpClientConnectionManager)} method.
+     */
+    public final HttpAsyncClientBuilder setMaxConnPerRoute(final int maxConnPerRoute) {
+        this.maxConnPerRoute = maxConnPerRoute;
+        return this;
+    }
+
+    /**
+     * Assigns {@link ConnectionReuseStrategy} instance.
+     */
+    public final HttpAsyncClientBuilder setConnectionReuseStrategy(
+            final ConnectionReuseStrategy reuseStrategy) {
+        this.reuseStrategy = reuseStrategy;
+        return this;
+    }
+
+    /**
+     * Assigns {@link ConnectionKeepAliveStrategy} instance.
+     */
+    public final HttpAsyncClientBuilder setKeepAliveStrategy(
+            final ConnectionKeepAliveStrategy keepAliveStrategy) {
+        this.keepAliveStrategy = keepAliveStrategy;
+        return this;
+    }
+
+    /**
+     * Assigns {@link UserTokenHandler} instance.
+     * <p>
+     * Please note this value can be overridden by the {@link #disableConnectionState()}
+     * method.
+     */
+    public final HttpAsyncClientBuilder setUserTokenHandler(final UserTokenHandler userTokenHandler) {
+        this.userTokenHandler = userTokenHandler;
+        return this;
+    }
+
+    /**
+     * Assigns {@link AuthenticationStrategy} instance for proxy
+     * authentication.
+     */
+    public final HttpAsyncClientBuilder setTargetAuthenticationStrategy(
+            final AuthenticationStrategy targetAuthStrategy) {
+        this.targetAuthStrategy = targetAuthStrategy;
+        return this;
+    }
+
+    /**
+     * Assigns {@link AuthenticationStrategy} instance for target
+     * host authentication.
+     */
+    public final HttpAsyncClientBuilder setProxyAuthenticationStrategy(
+            final AuthenticationStrategy proxyAuthStrategy) {
+        this.proxyAuthStrategy = proxyAuthStrategy;
+        return this;
+    }
+
+    /**
+     * Assigns {@link HttpProcessor} instance.
+     */
+    public final HttpAsyncClientBuilder setHttpProcessor(final HttpProcessor httpprocessor) {
+        this.httpprocessor = httpprocessor;
+        return this;
+    }
+
+    /**
+     * Adds this protocol interceptor to the head of the protocol processing list.
+     * <p>
+     * Please note this value can be overridden by the {@link #setHttpProcessor(
+     * org.apache.http.protocol.HttpProcessor)} method.
+     */
+    public final HttpAsyncClientBuilder addInterceptorFirst(final HttpResponseInterceptor itcp) {
+        if (itcp == null) {
+            return this;
+        }
+        if (responseFirst == null) {
+            responseFirst = new LinkedList<HttpResponseInterceptor>();
+        }
+        responseFirst.addFirst(itcp);
+        return this;
+    }
+
+    /**
+     * Adds this protocol interceptor to the tail of the protocol processing list.
+     * <p>
+     * Please note this value can be overridden by the {@link #setHttpProcessor(
+     * org.apache.http.protocol.HttpProcessor)} method.
+     */
+    public final HttpAsyncClientBuilder addInterceptorLast(final HttpResponseInterceptor itcp) {
+        if (itcp == null) {
+            return this;
+        }
+        if (responseLast == null) {
+            responseLast = new LinkedList<HttpResponseInterceptor>();
+        }
+        responseLast.addLast(itcp);
+        return this;
+    }
+
+    /**
+     * Adds this protocol interceptor to the head of the protocol processing list.
+     * <p>
+     * Please note this value can be overridden by the {@link #setHttpProcessor(
+     * org.apache.http.protocol.HttpProcessor)} method.
+     */
+    public final HttpAsyncClientBuilder addInterceptorFirst(final HttpRequestInterceptor itcp) {
+        if (itcp == null) {
+            return this;
+        }
+        if (requestFirst == null) {
+            requestFirst = new LinkedList<HttpRequestInterceptor>();
+        }
+        requestFirst.addFirst(itcp);
+        return this;
+    }
+
+    /**
+     * Adds this protocol interceptor to the tail of the protocol processing list.
+     * <p>
+     * Please note this value can be overridden by the {@link #setHttpProcessor(
+     * org.apache.http.protocol.HttpProcessor)} method.
+     */
+    public final HttpAsyncClientBuilder addInterceptorLast(final HttpRequestInterceptor itcp) {
+        if (itcp == null) {
+            return this;
+        }
+        if (requestLast == null) {
+            requestLast = new LinkedList<HttpRequestInterceptor>();
+        }
+        requestLast.addLast(itcp);
+        return this;
+    }
+
+    /**
+     * Assigns {@link HttpRoutePlanner} instance.
+     */
+    public final HttpAsyncClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) {
+        this.routePlanner = routePlanner;
+        return this;
+    }
+
+    /**
+     * Assigns {@link RedirectStrategy} instance.
+     */
+    public final HttpAsyncClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) {
+        this.redirectStrategy = redirectStrategy;
+        return this;
+    }
+
+    /**
+     * Assigns default {@link CookieStore} instance which will be used for
+     * request execution if not explicitly set in the client execution context.
+     */
+    public final HttpAsyncClientBuilder setDefaultCookieStore(final CookieStore cookieStore) {
+        this.cookieStore = cookieStore;
+        return this;
+    }
+
+    /**
+     * Assigns default {@link CredentialsProvider} instance which will be used
+     * for request execution if not explicitly set in the client execution
+     * context.
+     */
+    public final HttpAsyncClientBuilder setDefaultCredentialsProvider(
+            final CredentialsProvider credentialsProvider) {
+        this.credentialsProvider = credentialsProvider;
+        return this;
+    }
+
+
+    /**
+     * Assigns default {@link org.apache.http.auth.AuthScheme} registry which will
+     * be used for request execution if not explicitly set in the client execution
+     * context.
+     */
+    public final HttpAsyncClientBuilder setDefaultAuthSchemeRegistry(
+            final Lookup<AuthSchemeProvider> authSchemeRegistry) {
+        this.authSchemeRegistry = authSchemeRegistry;
+        return this;
+    }
+
+    /**
+     * Assigns default {@link org.apache.http.cookie.CookieSpec} registry which will
+     * be used for request execution if not explicitly set in the client execution
+     * context.
+     */
+    public final HttpAsyncClientBuilder setDefaultCookieSpecRegistry(
+            final Lookup<CookieSpecProvider> cookieSpecRegistry) {
+        this.cookieSpecRegistry = cookieSpecRegistry;
+        return this;
+    }
+
+    /**
+     * Assigns {@code User-Agent} value.
+     * <p>
+     * Please note this value can be overridden by the {@link #setHttpProcessor(
+     * org.apache.http.protocol.HttpProcessor)} method.
+     */
+    public final HttpAsyncClientBuilder setUserAgent(final String userAgent) {
+        this.userAgent = userAgent;
+        return this;
+    }
+
+    /**
+     * Assigns default proxy value.
+     * <p>
+     * Please note this value can be overridden by the {@link #setRoutePlanner(
+     *   org.apache.http.conn.routing.HttpRoutePlanner)} method.
+     */
+    public final HttpAsyncClientBuilder setProxy(final HttpHost proxy) {
+        this.proxy = proxy;
+        return this;
+    }
+
+    /**
+     * Assigns {@link SchemeIOSessionStrategy} instance.
+     * <p>
+     * Please note this value can be overridden by the {@link #setConnectionManager(
+     *   org.apache.http.nio.conn.NHttpClientConnectionManager)} method.
+     */
+    public final HttpAsyncClientBuilder setSSLStrategy(final SchemeIOSessionStrategy strategy) {
+        this.sslStrategy = strategy;
+        return this;
+    }
+
+    /**
+     * Assigns {@link SSLContext} instance.
+     * <p>
+     * Please note this value can be overridden by the {@link #setConnectionManager(
+     *   org.apache.http.nio.conn.NHttpClientConnectionManager)} and the {@link #setSSLStrategy(
+     *   org.apache.http.nio.conn.SchemeIOSessionStrategy)} methods.
+     */
+    public final HttpAsyncClientBuilder setSSLContext(final SSLContext sslcontext) {
+        this.sslcontext = sslcontext;
+        return this;
+    }
+
+    /**
+     * Assigns {@link X509HostnameVerifier} instance.
+     * <p>
+     * Please note this value can be overridden by the {@link #setConnectionManager(
+     *   org.apache.http.nio.conn.NHttpClientConnectionManager)} and the {@link #setSSLStrategy(
+     *   org.apache.http.nio.conn.SchemeIOSessionStrategy)} methods.
+     *
+     * @deprecated (4.1) use {@link #setSSLHostnameVerifier(javax.net.ssl.HostnameVerifier)}
+     */
+    @Deprecated
+    public final HttpAsyncClientBuilder setHostnameVerifier(final X509HostnameVerifier hostnameVerifier) {
+        this.hostnameVerifier = hostnameVerifier;
+        return this;
+    }
+
+    /**
+     * Assigns {@link javax.net.ssl.HostnameVerifier} instance.
+     * <p>
+     * Please note this value can be overridden by the {@link #setConnectionManager(
+     *   org.apache.http.nio.conn.NHttpClientConnectionManager)} and the {@link #setSSLStrategy(
+     *   org.apache.http.nio.conn.SchemeIOSessionStrategy)} methods.
+     *
+     * @since 4.1
+     */
+    public final HttpAsyncClientBuilder setSSLHostnameVerifier(final HostnameVerifier hostnameVerifier) {
+        this.hostnameVerifier = hostnameVerifier;
+        return this;
+    }
+
+    /**
+     * Assigns default request header values.
+     * <p>
+     * Please note this value can be overridden by the {@link #setHttpProcessor(
+     * org.apache.http.protocol.HttpProcessor)} method.
+     */
+    public final HttpAsyncClientBuilder setDefaultHeaders(final Collection<? extends Header> defaultHeaders) {
+        this.defaultHeaders = defaultHeaders;
+        return this;
+    }
+
+    /**
+     * Assigns default {@link IOReactorConfig}.
+     * <p>
+     * Please note this value can be overridden by the {@link #setConnectionManager(
+     *   org.apache.http.nio.conn.NHttpClientConnectionManager)} method.
+     */
+    public final HttpAsyncClientBuilder setDefaultIOReactorConfig(final IOReactorConfig config) {
+        this.defaultIOReactorConfig = config;
+        return this;
+    }
+
+    /**
+     * Assigns default {@link ConnectionConfig}.
+     * <p>
+     * Please note this value can be overridden by the {@link #setConnectionManager(
+     *   org.apache.http.nio.conn.NHttpClientConnectionManager)} method.
+     */
+    public final HttpAsyncClientBuilder setDefaultConnectionConfig(final ConnectionConfig config) {
+        this.defaultConnectionConfig = config;
+        return this;
+    }
+
+    /**
+     * Assigns default {@link RequestConfig} instance which will be used
+     * for request execution if not explicitly set in the client execution
+     * context.
+     */
+    public final HttpAsyncClientBuilder setDefaultRequestConfig(final RequestConfig config) {
+        this.defaultRequestConfig = config;
+        return this;
+    }
+
+    /**
+     * Assigns {@link ThreadFactory} instance.
+     */
+    public final HttpAsyncClientBuilder setThreadFactory(final ThreadFactory threadFactory) {
+        this.threadFactory = threadFactory;
+        return this;
+    }
+
+    /**
+     * Assigns {@link NHttpClientEventHandler} instance.
+     *
+     * @since 4.1
+     */
+    public final HttpAsyncClientBuilder setEventHandler(final NHttpClientEventHandler eventHandler) {
+        this.eventHandler = eventHandler;
+        return this;
+    }
+
+    /**
+     * Disables connection state tracking.
+     */
+    public final HttpAsyncClientBuilder disableConnectionState() {
+        connectionStateDisabled = true;
+        return this;
+    }
+
+    /**
+     * Disables state (cookie) management.
+     * <p>
+     * Please note this value can be overridden by the {@link #setHttpProcessor(
+     * org.apache.http.protocol.HttpProcessor)} method.
+     */
+    public final HttpAsyncClientBuilder disableCookieManagement() {
+        cookieManagementDisabled = true;
+        return this;
+    }
+
+    /**
+     * Disables authentication scheme caching.
+     * <p>
+     * Please note this value can be overridden by the {@link #setHttpProcessor(
+     * org.apache.http.protocol.HttpProcessor)} method.
+     */
+    public final HttpAsyncClientBuilder disableAuthCaching() {
+        authCachingDisabled = true;
+        return this;
+    }
+
+    /**
+     * Use system properties when creating and configuring default
+     * implementations.
+     */
+    public final HttpAsyncClientBuilder useSystemProperties() {
+        systemProperties = true;
+        return this;
+    }
+
+    private static String[] split(final String s) {
+        if (TextUtils.isBlank(s)) {
+            return null;
+        }
+        return s.split(" *, *");
+    }
+
+    public CloseableHttpAsyncClient build() {
+
+        PublicSuffixMatcher publicSuffixMatcher = this.publicSuffixMatcher;
+        if (publicSuffixMatcher == null) {
+            publicSuffixMatcher = PublicSuffixMatcherLoader.getDefault();
+        }
+
+        NHttpClientConnectionManager connManager = this.connManager;
+        if (connManager == null) {
+            SchemeIOSessionStrategy sslStrategy = this.sslStrategy;
+            if (sslStrategy == null) {
+                SSLContext sslcontext = this.sslcontext;
+                if (sslcontext == null) {
+                    if (systemProperties) {
+                        sslcontext = SSLContexts.createSystemDefault();
+                    } else {
+                        sslcontext = SSLContexts.createDefault();
+                    }
+                }
+                final String[] supportedProtocols = systemProperties ? split(
+                        System.getProperty("https.protocols")) : null;
+                final String[] supportedCipherSuites = systemProperties ? split(
+                        System.getProperty("https.cipherSuites")) : null;
+                HostnameVerifier hostnameVerifier = this.hostnameVerifier;
+                if (hostnameVerifier == null) {
+                    hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
+                }
+                sslStrategy = new SSLIOSessionStrategy(
+                        sslcontext, supportedProtocols, supportedCipherSuites, hostnameVerifier);
+            }
+            final ConnectingIOReactor ioreactor = IOReactorUtils.create(
+                defaultIOReactorConfig != null ? defaultIOReactorConfig : IOReactorConfig.DEFAULT, threadFactory);
+            final PoolingNHttpClientConnectionManager poolingmgr = new PoolingNHttpClientConnectionManager(
+                    ioreactor,
+                    RegistryBuilder.<SchemeIOSessionStrategy>create()
+                        .register("http", NoopIOSessionStrategy.INSTANCE)
+                        .register("https", sslStrategy)
+                        .build());
+            if (defaultConnectionConfig != null) {
+                poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig);
+            }
+            if (systemProperties) {
+                String s = System.getProperty("http.keepAlive", "true");
+                if ("true".equalsIgnoreCase(s)) {
+                    s = System.getProperty("http.maxConnections", "5");
+                    final int max = Integer.parseInt(s);
+                    poolingmgr.setDefaultMaxPerRoute(max);
+                    poolingmgr.setMaxTotal(2 * max);
+                }
+            } else {
+                if (maxConnTotal > 0) {
+                    poolingmgr.setMaxTotal(maxConnTotal);
+                }
+                if (maxConnPerRoute > 0) {
+                    poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute);
+                }
+            }
+            connManager = poolingmgr;
+        }
+        ConnectionReuseStrategy reuseStrategy = this.reuseStrategy;
+        if (reuseStrategy == null) {
+            if (systemProperties) {
+                final String s = System.getProperty("http.keepAlive", "true");
+                if ("true".equalsIgnoreCase(s)) {
+                    reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE;
+                } else {
+                    reuseStrategy = NoConnectionReuseStrategy.INSTANCE;
+                }
+            } else {
+                reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE;
+            }
+        }
+        ConnectionKeepAliveStrategy keepAliveStrategy = this.keepAliveStrategy;
+        if (keepAliveStrategy == null) {
+            keepAliveStrategy = DefaultConnectionKeepAliveStrategy.INSTANCE;
+        }
+        AuthenticationStrategy targetAuthStrategy = this.targetAuthStrategy;
+        if (targetAuthStrategy == null) {
+            targetAuthStrategy = TargetAuthenticationStrategy.INSTANCE;
+        }
+        AuthenticationStrategy proxyAuthStrategy = this.proxyAuthStrategy;
+        if (proxyAuthStrategy == null) {
+            proxyAuthStrategy = ProxyAuthenticationStrategy.INSTANCE;
+        }
+        UserTokenHandler userTokenHandler = this.userTokenHandler;
+        if (userTokenHandler == null) {
+            if (!connectionStateDisabled) {
+                userTokenHandler = DefaultAsyncUserTokenHandler.INSTANCE;
+            } else {
+                userTokenHandler = NoopUserTokenHandler.INSTANCE;
+            }
+        }
+        SchemePortResolver schemePortResolver = this.schemePortResolver;
+        if (schemePortResolver == null) {
+            schemePortResolver = DefaultSchemePortResolver.INSTANCE;
+        }
+
+        HttpProcessor httpprocessor = this.httpprocessor;
+        if (httpprocessor == null) {
+
+            String userAgent = this.userAgent;
+            if (userAgent == null) {
+                if (systemProperties) {
+                    userAgent = System.getProperty("http.agent");
+                }
+                if (userAgent == null) {
+                    userAgent = VersionInfo.getUserAgent(
+                            "Apache-HttpAsyncClient",
+                            "org.apache.http.nio.client", getClass());
+                }
+            }
+
+            final HttpProcessorBuilder b = HttpProcessorBuilder.create();
+            if (requestFirst != null) {
+                for (final HttpRequestInterceptor i: requestFirst) {
+                    b.addFirst(i);
+                }
+            }
+            if (responseFirst != null) {
+                for (final HttpResponseInterceptor i: responseFirst) {
+                    b.addFirst(i);
+                }
+            }
+            b.addAll(
+                    new RequestDefaultHeaders(defaultHeaders),
+                    new RequestContent(),
+                    new RequestTargetHost(),
+                    new RequestClientConnControl(),
+                    new RequestUserAgent(userAgent),
+                    new RequestExpectContinue());
+            if (!cookieManagementDisabled) {
+                b.add(new RequestAddCookies());
+            }
+            if (!authCachingDisabled) {
+                b.add(new RequestAuthCache());
+            }
+            if (!cookieManagementDisabled) {
+                b.add(new ResponseProcessCookies());
+            }
+            if (requestLast != null) {
+                for (final HttpRequestInterceptor i: requestLast) {
+                    b.addLast(i);
+                }
+            }
+            if (responseLast != null) {
+                for (final HttpResponseInterceptor i: responseLast) {
+                    b.addLast(i);
+                }
+            }
+            httpprocessor = b.build();
+        }
+        // Add redirect executor, if not disabled
+        HttpRoutePlanner routePlanner = this.routePlanner;
+        if (routePlanner == null) {
+            if (proxy != null) {
+                routePlanner = new DefaultProxyRoutePlanner(proxy, schemePortResolver);
+            } else if (systemProperties) {
+                routePlanner = new SystemDefaultRoutePlanner(
+                        schemePortResolver, ProxySelector.getDefault());
+            } else {
+                routePlanner = new DefaultRoutePlanner(schemePortResolver);
+            }
+        }
+        Lookup<AuthSchemeProvider> authSchemeRegistry = this.authSchemeRegistry;
+        if (authSchemeRegistry == null) {
+            authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
+                .register(AuthSchemes.BASIC, new BasicSchemeFactory())
+                .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
+                .register(AuthSchemes.NTLM, new NTLMSchemeFactory())
+                .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
+                .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
+                .build();
+        }
+        Lookup<CookieSpecProvider> cookieSpecRegistry = this.cookieSpecRegistry;
+        if (cookieSpecRegistry == null) {
+            final CookieSpecProvider defaultProvider = new DefaultCookieSpecProvider(publicSuffixMatcher);
+            final CookieSpecProvider laxStandardProvider = new RFC6265CookieSpecProvider(
+                    RFC6265CookieSpecProvider.CompatibilityLevel.RELAXED, publicSuffixMatcher);
+            final CookieSpecProvider strictStandardProvider = new RFC6265CookieSpecProvider(
+                    RFC6265CookieSpecProvider.CompatibilityLevel.STRICT, publicSuffixMatcher);
+            cookieSpecRegistry = RegistryBuilder.<CookieSpecProvider>create()
+                    .register(CookieSpecs.DEFAULT, defaultProvider)
+                    .register("best-match", defaultProvider)
+                    .register("compatibility", defaultProvider)
+                    .register(CookieSpecs.STANDARD, laxStandardProvider)
+                    .register(CookieSpecs.STANDARD_STRICT, strictStandardProvider)
+                    .register(CookieSpecs.NETSCAPE, new NetscapeDraftSpecProvider())
+                    .register(CookieSpecs.IGNORE_COOKIES, new IgnoreSpecProvider())
+                    .build();
+        }
+
+        CookieStore defaultCookieStore = this.cookieStore;
+        if (defaultCookieStore == null) {
+            defaultCookieStore = new BasicCookieStore();
+        }
+
+        CredentialsProvider defaultCredentialsProvider = this.credentialsProvider;
+        if (defaultCredentialsProvider == null) {
+            if (systemProperties) {
+                defaultCredentialsProvider = new SystemDefaultCredentialsProvider();
+            } else {
+                defaultCredentialsProvider = new BasicCredentialsProvider();
+            }
+        }
+        RedirectStrategy redirectStrategy = this.redirectStrategy;
+        if (redirectStrategy == null) {
+            redirectStrategy = DefaultRedirectStrategy.INSTANCE;
+        }
+
+        RequestConfig defaultRequestConfig = this.defaultRequestConfig;
+        if (defaultRequestConfig == null) {
+            defaultRequestConfig = RequestConfig.DEFAULT;
+        }
+
+        final MainClientExec exec = new MainClientExec(
+            httpprocessor,
+            routePlanner,
+            redirectStrategy,
+            targetAuthStrategy,
+            proxyAuthStrategy,
+            userTokenHandler);
+
+        ThreadFactory threadFactory = null;
+        NHttpClientEventHandler eventHandler = null;
+        if (!this.connManagerShared) {
+            threadFactory = this.threadFactory;
+            if (threadFactory == null) {
+                threadFactory = Executors.defaultThreadFactory();
+            }
+            eventHandler = this.eventHandler;
+            if (eventHandler == null) {
+                eventHandler = new HttpAsyncRequestExecutor();
+            }
+        }
+        return new InternalHttpAsyncClient(
+            connManager,
+            reuseStrategy,
+            keepAliveStrategy,
+            threadFactory,
+            eventHandler,
+            exec,
+            cookieSpecRegistry,
+            authSchemeRegistry,
+            defaultCookieStore,
+            defaultCredentialsProvider,
+            defaultRequestConfig);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/HttpAsyncClients.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/HttpAsyncClients.java
new file mode 100644
index 0000000..788e96b
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/HttpAsyncClients.java
@@ -0,0 +1,175 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.reactor.ConnectingIOReactor;
+import org.apache.http.util.Args;
+
+/**
+ * Factory methods for {@link org.apache.http.impl.nio.client.CloseableHttpAsyncClient} and
+ * {@link org.apache.http.impl.nio.client.CloseableHttpPipeliningClient} instances.
+ *
+ * @since 4.0
+ */
+public class HttpAsyncClients {
+
+    private HttpAsyncClients() {
+        super();
+    }
+
+    /**
+     * Creates builder object for construction of custom
+     * {@link CloseableHttpAsyncClient} instances.
+     */
+    public static HttpAsyncClientBuilder custom() {
+        return HttpAsyncClientBuilder.create();
+    }
+
+    /**
+     * Creates {@link CloseableHttpAsyncClient} instance with default
+     * configuration.
+     */
+    public static CloseableHttpAsyncClient createDefault() {
+        return HttpAsyncClientBuilder.create().build();
+    }
+
+    /**
+     * Creates {@link CloseableHttpAsyncClient} instance with default
+     * configuration based on ssytem properties.
+     */
+    public static CloseableHttpAsyncClient createSystem() {
+        return HttpAsyncClientBuilder.create()
+                .useSystemProperties()
+                .build();
+    }
+
+    /**
+     * Creates {@link CloseableHttpAsyncClient} instance that supports esential HTTP protocol
+     * aspects only. This client does not support HTTP state management, authentication
+     * and automatic redirects.
+     */
+    public static CloseableHttpAsyncClient createMinimal() {
+        return MinimalHttpAsyncClientBuilder.create()
+                .disableCookieManagement()
+                .build();
+    }
+
+    /**
+     * Creates {@link CloseableHttpAsyncClient} instance that supports esential HTTP protocol
+     * aspects only. This client does not support HTTP state management, authentication
+     * and automatic redirects.
+     */
+    public static CloseableHttpAsyncClient createMinimal(final ConnectingIOReactor ioreactor) {
+        Args.notNull(ioreactor, "I/O reactor");
+        return createMinimal(new PoolingNHttpClientConnectionManager(ioreactor), false);
+    }
+
+    /**
+     * Creates {@link CloseableHttpAsyncClient} instance that supports esential HTTP protocol
+     * aspects only. This client does not support HTTP state management, authentication
+     * and automatic redirects.
+     */
+    public static CloseableHttpAsyncClient createMinimal(final NHttpClientConnectionManager connManager) {
+        return createMinimal(connManager, false);
+    }
+
+    /**
+     * Creates {@link CloseableHttpAsyncClient} instance that supports esential HTTP protocol
+     * aspects only. This client does not support HTTP state management, authentication
+     * and automatic redirects.
+     * <p>
+     * Please note that clients with a shared connection manager make no attempts to control
+     * its life cycle and dealocation of resources. It is a responibility of the caller to
+     * ensure that the shared connection manager is properly started and shut down when no
+     * longer needed.
+     *
+     * @since 4.1
+     */
+    public static CloseableHttpAsyncClient createMinimal(
+            final NHttpClientConnectionManager connManager, final boolean shared) {
+        Args.notNull(connManager, "Connection manager");
+        return MinimalHttpAsyncClientBuilder.create()
+                .setConnectionManager(connManager)
+                .setConnectionManagerShared(shared)
+                .disableCookieManagement()
+                .build();
+    }
+
+    /**
+     * Creates {@link CloseableHttpPipeliningClient} instance that supports pipelined request
+     * execution. This client does not support authentication and automatic redirects.
+     *
+     * @since 4.1
+     */
+    public static CloseableHttpPipeliningClient createPipelining() {
+        return MinimalHttpAsyncClientBuilder.create().build();
+    }
+
+    /**
+     * Creates {@link CloseableHttpPipeliningClient} instance that supports pipelined request
+     * execution. This client does not support authentication and automatic redirects.
+     *
+     * @since 4.1
+     */
+    public static CloseableHttpPipeliningClient createPipelining(final ConnectingIOReactor ioreactor) {
+        return createPipelining(new PoolingNHttpClientConnectionManager(ioreactor), false);
+    }
+
+    /**
+     * Creates {@link CloseableHttpPipeliningClient} instance that supports pipelined request
+     * execution. This client does not support authentication and automatic redirects.
+     *
+     * @since 4.1
+     */
+    public static CloseableHttpPipeliningClient createPipelining(final NHttpClientConnectionManager connManager) {
+        return createPipelining(connManager, false);
+    }
+
+    /**
+     * Creates {@link CloseableHttpPipeliningClient} instance that supports pipelined request
+     * execution. This client does not support authentication and automatic redirects.
+     * <p>
+     * Please note that clients with a shared connection manager make no attempts to control
+     * its life cycle and dealocation of resources. It is a responibility of the caller to
+     * ensure that the shared connection manager is properly started and shut down when no
+     * longer needed.
+     *
+     * @since 4.1
+     */
+    public static CloseableHttpPipeliningClient createPipelining(
+            final NHttpClientConnectionManager connManager, final boolean shared) {
+        Args.notNull(connManager, "Connection manager");
+        return MinimalHttpAsyncClientBuilder.create()
+                .setConnectionManager(connManager)
+                .setConnectionManagerShared(shared)
+                .build();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/IOReactorUtils.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/IOReactorUtils.java
new file mode 100644
index 0000000..c25265c
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/IOReactorUtils.java
@@ -0,0 +1,49 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.concurrent.ThreadFactory;
+
+import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
+import org.apache.http.impl.nio.reactor.IOReactorConfig;
+import org.apache.http.nio.reactor.ConnectingIOReactor;
+import org.apache.http.nio.reactor.IOReactorException;
+
+final class IOReactorUtils {
+
+    private IOReactorUtils() {
+    }
+
+    public static ConnectingIOReactor create(final IOReactorConfig config, final ThreadFactory threadFactory) {
+        try {
+            return new DefaultConnectingIOReactor(config, threadFactory);
+        } catch (final IOReactorException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalClientExec.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalClientExec.java
new file mode 100644
index 0000000..b26bb31
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalClientExec.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.impl.nio.client;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.IOControl;
+
+interface InternalClientExec {
+
+    void prepare(
+            HttpHost target,
+            HttpRequest original,
+            InternalState state,
+            AbstractClientExchangeHandler handler) throws IOException, HttpException;
+
+    HttpRequest generateRequest(
+            InternalState state,
+            AbstractClientExchangeHandler handler) throws IOException, HttpException;
+
+    void produceContent(
+            InternalState state,
+            ContentEncoder encoder,
+            IOControl ioctrl) throws IOException;
+
+    void requestCompleted(
+            InternalState state,
+            AbstractClientExchangeHandler handler);
+
+    void responseReceived(
+            HttpResponse response,
+            InternalState state,
+            AbstractClientExchangeHandler handler) throws IOException, HttpException;
+
+    void consumeContent(
+            InternalState state,
+            ContentDecoder decoder,
+            IOControl ioctrl) throws IOException;
+
+    void responseCompleted(
+            InternalState state,
+            AbstractClientExchangeHandler handler) throws IOException, HttpException;
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalHttpAsyncClient.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalHttpAsyncClient.java
new file mode 100644
index 0000000..930b7c2
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalHttpAsyncClient.java
@@ -0,0 +1,158 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthSchemeProvider;
+import org.apache.http.auth.AuthState;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.config.Lookup;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.cookie.CookieSpecProvider;
+import org.apache.http.nio.NHttpClientEventHandler;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+
+class InternalHttpAsyncClient extends CloseableHttpAsyncClientBase {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    private final NHttpClientConnectionManager connmgr;
+    private final ConnectionReuseStrategy connReuseStrategy;
+    private final ConnectionKeepAliveStrategy keepaliveStrategy;
+    private final InternalClientExec exec;
+    private final Lookup<CookieSpecProvider> cookieSpecRegistry;
+    private final Lookup<AuthSchemeProvider> authSchemeRegistry;
+    private final CookieStore cookieStore;
+    private final CredentialsProvider credentialsProvider;
+    private final RequestConfig defaultConfig;
+
+    public InternalHttpAsyncClient(
+            final NHttpClientConnectionManager connmgr,
+            final ConnectionReuseStrategy connReuseStrategy,
+            final ConnectionKeepAliveStrategy keepaliveStrategy,
+            final ThreadFactory threadFactory,
+            final NHttpClientEventHandler handler,
+            final InternalClientExec exec,
+            final Lookup<CookieSpecProvider> cookieSpecRegistry,
+            final Lookup<AuthSchemeProvider> authSchemeRegistry,
+            final CookieStore cookieStore,
+            final CredentialsProvider credentialsProvider,
+            final RequestConfig defaultConfig) {
+        super(connmgr, threadFactory, handler);
+        this.connmgr = connmgr;
+        this.connReuseStrategy = connReuseStrategy;
+        this.keepaliveStrategy = keepaliveStrategy;
+        this.exec = exec;
+        this.cookieSpecRegistry = cookieSpecRegistry;
+        this.authSchemeRegistry = authSchemeRegistry;
+        this.cookieStore = cookieStore;
+        this.credentialsProvider = credentialsProvider;
+        this.defaultConfig = defaultConfig;
+    }
+
+    private void setupContext(final HttpClientContext context) {
+        if (context.getAttribute(HttpClientContext.TARGET_AUTH_STATE) == null) {
+            context.setAttribute(HttpClientContext.TARGET_AUTH_STATE, new AuthState());
+        }
+        if (context.getAttribute(HttpClientContext.PROXY_AUTH_STATE) == null) {
+            context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, new AuthState());
+        }
+        if (context.getAttribute(HttpClientContext.AUTHSCHEME_REGISTRY) == null) {
+            context.setAttribute(HttpClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry);
+        }
+        if (context.getAttribute(HttpClientContext.COOKIESPEC_REGISTRY) == null) {
+            context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, this.cookieSpecRegistry);
+        }
+        if (context.getAttribute(HttpClientContext.COOKIE_STORE) == null) {
+            context.setAttribute(HttpClientContext.COOKIE_STORE, this.cookieStore);
+        }
+        if (context.getAttribute(HttpClientContext.CREDS_PROVIDER) == null) {
+            context.setAttribute(HttpClientContext.CREDS_PROVIDER, this.credentialsProvider);
+        }
+        if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
+            context.setAttribute(HttpClientContext.REQUEST_CONFIG, this.defaultConfig);
+        }
+    }
+
+    @Override
+    public <T> Future<T> execute(
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final HttpContext context,
+            final FutureCallback<T> callback) {
+        ensureRunning();
+        final BasicFuture<T> future = new BasicFuture<T>(callback);
+        final HttpClientContext localcontext = HttpClientContext.adapt(
+            context != null ? context : new BasicHttpContext());
+        setupContext(localcontext);
+
+        @SuppressWarnings("resource")
+        final DefaultClientExchangeHandlerImpl<T> handler = new DefaultClientExchangeHandlerImpl<T>(
+            this.log,
+            requestProducer,
+            responseConsumer,
+            localcontext,
+            future,
+            this.connmgr,
+            this.connReuseStrategy,
+            this.keepaliveStrategy,
+            this.exec);
+        try {
+            handler.start();
+        } catch (final Exception ex) {
+            handler.failed(ex);
+        }
+        return new FutureWrapper<T>(future, handler);
+    }
+
+    @Override
+    public <T> Future<List<T>> execute(
+            final HttpHost target,
+            final List<? extends HttpAsyncRequestProducer> requestProducers,
+            final List<? extends HttpAsyncResponseConsumer<T>> responseConsumers,
+            final HttpContext context,
+            final FutureCallback<List<T>> callback) {
+        throw new UnsupportedOperationException("Pipelining not supported");
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalIODispatch.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalIODispatch.java
new file mode 100644
index 0000000..fa04c6b
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalIODispatch.java
@@ -0,0 +1,98 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.impl.nio.DefaultNHttpClientConnection;
+import org.apache.http.impl.nio.reactor.AbstractIODispatch;
+import org.apache.http.nio.NHttpClientEventHandler;
+import org.apache.http.nio.reactor.IOSession;
+
+class InternalIODispatch extends AbstractIODispatch<DefaultNHttpClientConnection> {
+
+    private final Log log = LogFactory.getLog(InternalIODispatch.class);
+
+    private final NHttpClientEventHandler handler;
+
+    public InternalIODispatch(final NHttpClientEventHandler handler) {
+        super();
+        if (this.log.isDebugEnabled()) {
+            this.handler = new InternalRequestExecutor(this.log, handler);
+        } else {
+            this.handler = handler;
+        }
+    }
+
+    @Override
+    protected DefaultNHttpClientConnection createConnection(final IOSession session) {
+        throw new IllegalStateException("Connection must be created by connection manager");
+    }
+
+    @Override
+    protected void onConnected(final DefaultNHttpClientConnection conn) {
+        final Object attachment = conn.getContext().getAttribute(IOSession.ATTACHMENT_KEY);
+        try {
+            this.handler.connected(conn, attachment);
+        } catch (final Exception ex) {
+            this.handler.exception(conn, ex);
+        }
+    }
+
+    @Override
+    protected void onClosed(final DefaultNHttpClientConnection conn) {
+        this.handler.closed(conn);
+    }
+
+    @Override
+    protected void onException(final DefaultNHttpClientConnection conn, final IOException ex) {
+        this.handler.exception(conn, ex);
+    }
+
+    @Override
+    protected void onInputReady(final DefaultNHttpClientConnection conn) {
+        conn.consumeInput(this.handler);
+    }
+
+    @Override
+    protected void onOutputReady(final DefaultNHttpClientConnection conn) {
+        conn.produceOutput(this.handler);
+    }
+
+    @Override
+    protected void onTimeout(final DefaultNHttpClientConnection conn) {
+        try {
+            this.handler.timeout(conn);
+        } catch (final Exception ex) {
+            this.handler.exception(conn, ex);
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalIOReactorExceptionHandler.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalIOReactorExceptionHandler.java
new file mode 100644
index 0000000..1fc0634
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalIOReactorExceptionHandler.java
@@ -0,0 +1,55 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.nio.reactor.IOReactorExceptionHandler;
+
+class InternalIOReactorExceptionHandler implements IOReactorExceptionHandler {
+
+    private final Log log;
+
+    InternalIOReactorExceptionHandler(final Log log) {
+        super();
+        this.log = log;
+    }
+
+    @Override
+    public boolean handle(final IOException ex) {
+        this.log.error("Fatal I/O error", ex);
+        return false;
+    }
+
+    @Override
+    public boolean handle(final RuntimeException ex) {
+        this.log.error("Fatal runtime error", ex);
+        return false;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalRequestExecutor.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalRequestExecutor.java
new file mode 100644
index 0000000..fae3ab9
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalRequestExecutor.java
@@ -0,0 +1,135 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.HttpException;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.NHttpClientEventHandler;
+
+class InternalRequestExecutor implements NHttpClientEventHandler {
+
+    private final Log log;
+    private final NHttpClientEventHandler handler;
+
+    public InternalRequestExecutor(final Log log, final NHttpClientEventHandler handler) {
+        this.log = log;
+        this.handler = handler;
+    }
+
+    @Override
+    public void connected(
+            final NHttpClientConnection conn,
+            final Object attachment) throws IOException, HttpException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + ": Connected");
+        }
+        this.handler.connected(conn, attachment);
+    }
+
+    @Override
+    public void closed(final NHttpClientConnection conn) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + ": Disconnected");
+        }
+        this.handler.closed(conn);
+    }
+
+    @Override
+    public void requestReady(
+            final NHttpClientConnection conn) throws IOException, HttpException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + " Request ready");
+        }
+        this.handler.requestReady(conn);
+    }
+
+    @Override
+    public void inputReady(
+            final NHttpClientConnection conn,
+            final ContentDecoder decoder) throws IOException, HttpException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + " Input ready");
+        }
+        this.handler.inputReady(conn, decoder);
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + " " + decoder);
+        }
+    }
+
+    @Override
+    public void outputReady(
+            final NHttpClientConnection conn,
+            final ContentEncoder encoder) throws IOException, HttpException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + " Output ready");
+        }
+        this.handler.outputReady(conn, encoder);
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + " " + encoder);
+        }
+    }
+
+    @Override
+    public void responseReceived(
+            final NHttpClientConnection conn) throws HttpException, IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + " Response received");
+        }
+        this.handler.responseReceived(conn);
+    }
+
+    @Override
+    public void timeout(final NHttpClientConnection conn) throws HttpException, IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + " Timeout");
+        }
+        this.handler.timeout(conn);
+    }
+
+    @Override
+    public void exception(final NHttpClientConnection conn, final Exception ex) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + " Exception", ex);
+        }
+        this.handler.exception(conn, ex);
+    }
+
+    @Override
+    public void endOfInput(final NHttpClientConnection conn) throws IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(conn + " End of input");
+        }
+        this.handler.endOfInput(conn);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalState.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalState.java
new file mode 100644
index 0000000..1c7c992
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/InternalState.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.nio.client;
+
+import java.nio.ByteBuffer;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+
+class InternalState {
+
+    private final long id;
+    private final HttpAsyncRequestProducer requestProducer;
+    private final HttpAsyncResponseConsumer<?> responseConsumer;
+    private final HttpClientContext localContext;
+
+    private HttpRequestWrapper mainRequest;
+    private HttpResponse finalResponse;
+    private ByteBuffer tmpbuf;
+    private boolean requestContentProduced;
+    private int execCount;
+
+    private int redirectCount;
+    private HttpUriRequest redirect;
+
+    public InternalState(
+            final long id,
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<?> responseConsumer,
+            final HttpClientContext localContext) {
+        super();
+        this.id = id;
+        this.requestProducer = requestProducer;
+        this.responseConsumer = responseConsumer;
+        this.localContext = localContext;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public HttpAsyncRequestProducer getRequestProducer() {
+        return requestProducer;
+    }
+
+    public HttpAsyncResponseConsumer<?> getResponseConsumer() {
+        return responseConsumer;
+    }
+
+    public HttpClientContext getLocalContext() {
+        return localContext;
+    }
+
+    public HttpRequestWrapper getMainRequest() {
+        return mainRequest;
+    }
+
+    public void setMainRequest(final HttpRequestWrapper mainRequest) {
+        this.mainRequest = mainRequest;
+    }
+
+    public HttpResponse getFinalResponse() {
+        return finalResponse;
+    }
+
+    public void setFinalResponse(final HttpResponse finalResponse) {
+        this.finalResponse = finalResponse;
+    }
+
+    public ByteBuffer getTmpbuf() {
+        if (tmpbuf == null) {
+            tmpbuf = ByteBuffer.allocate(4 * 1024);
+        }
+        return tmpbuf;
+    }
+
+    public boolean isRequestContentProduced() {
+        return requestContentProduced;
+    }
+
+    public void setRequestContentProduced() {
+        this.requestContentProduced = true;
+    }
+
+    public int getExecCount() {
+        return execCount;
+    }
+
+    public void incrementExecCount() {
+        this.execCount++;
+    }
+
+    public int getRedirectCount() {
+        return redirectCount;
+    }
+
+    public void incrementRedirectCount() {
+        this.redirectCount++;
+    }
+
+    public HttpUriRequest getRedirect() {
+        return redirect;
+    }
+
+    public void setRedirect(final HttpUriRequest redirect) {
+        this.redirect = redirect;
+    }
+
+    @Override
+    public String toString() {
+        return Long.toString(id);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MainClientExec.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MainClientExec.java
new file mode 100644
index 0000000..e418598
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MainClientExec.java
@@ -0,0 +1,644 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+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.HttpVersion;
+import org.apache.http.ProtocolException;
+import org.apache.http.auth.AUTH;
+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.UsernamePasswordCredentials;
+import org.apache.http.client.AuthenticationStrategy;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.NonRepeatableRequestException;
+import org.apache.http.client.RedirectException;
+import org.apache.http.client.RedirectStrategy;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.Configurable;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.client.protocol.RequestClientConnControl;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.conn.routing.BasicRouteDirector;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.HttpRouteDirector;
+import org.apache.http.conn.routing.HttpRoutePlanner;
+import org.apache.http.impl.auth.HttpAuthenticator;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.protocol.HttpCoreContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.ImmutableHttpProcessor;
+import org.apache.http.protocol.RequestTargetHost;
+
+class MainClientExec implements InternalClientExec {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    private final HttpProcessor httpProcessor;
+    private final HttpProcessor proxyHttpProcessor;
+    private final HttpRoutePlanner routePlanner;
+    private final AuthenticationStrategy targetAuthStrategy;
+    private final AuthenticationStrategy proxyAuthStrategy;
+    private final UserTokenHandler userTokenHandler;
+    private final RedirectStrategy redirectStrategy;
+    private final HttpRouteDirector routeDirector;
+    private final HttpAuthenticator authenticator;
+
+    public MainClientExec(
+            final HttpProcessor httpProcessor,
+            final HttpRoutePlanner routePlanner,
+            final RedirectStrategy redirectStrategy,
+            final AuthenticationStrategy targetAuthStrategy,
+            final AuthenticationStrategy proxyAuthStrategy,
+            final UserTokenHandler userTokenHandler) {
+        super();
+        this.httpProcessor = httpProcessor;
+        this.proxyHttpProcessor = new ImmutableHttpProcessor(
+                new RequestTargetHost(), new RequestClientConnControl());
+        this.routePlanner = routePlanner;
+        this.redirectStrategy = redirectStrategy;
+        this.targetAuthStrategy = targetAuthStrategy;
+        this.proxyAuthStrategy = proxyAuthStrategy;
+        this.userTokenHandler = userTokenHandler;
+        this.routeDirector = new BasicRouteDirector();
+        this.authenticator = new HttpAuthenticator(log);
+    }
+
+    @Override
+    public void prepare(
+            final HttpHost target,
+            final HttpRequest original,
+            final InternalState state,
+            final AbstractClientExchangeHandler handler) throws HttpException, IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + state.getId() + "] start execution");
+        }
+
+        final HttpClientContext localContext = state.getLocalContext();
+
+        if (original instanceof Configurable) {
+            final RequestConfig config = ((Configurable) original).getConfig();
+            if (config != null) {
+                localContext.setRequestConfig(config);
+            }
+        }
+
+        final List<URI> redirectLocations = localContext.getRedirectLocations();
+        if (redirectLocations != null) {
+            redirectLocations.clear();
+        }
+
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(original);
+        final HttpRoute route = this.routePlanner.determineRoute(target, request, localContext);
+
+        handler.setRoute(route);
+
+        state.setMainRequest(request);
+        handler.setCurrentRequest(request);
+
+        prepareRequest(state, handler);
+    }
+
+    @Override
+    public HttpRequest generateRequest(
+            final InternalState state,
+            final AbstractClientExchangeHandler handler) throws IOException, HttpException {
+
+        final HttpRoute route = handler.getRoute();
+
+        handler.verifytRoute();
+
+        if (!handler.isRouteEstablished()) {
+            int step;
+            loop:
+            do {
+                final HttpRoute fact = handler.getActualRoute();
+                step = this.routeDirector.nextStep(route, fact);
+                switch (step) {
+                case HttpRouteDirector.CONNECT_TARGET:
+                    handler.onRouteToTarget();
+                    break;
+                case HttpRouteDirector.CONNECT_PROXY:
+                    handler.onRouteToProxy();
+                    break;
+                case HttpRouteDirector.TUNNEL_TARGET:
+                    if (this.log.isDebugEnabled()) {
+                        this.log.debug("[exchange: " + state.getId() + "] Tunnel required");
+                    }
+                    final HttpRequest connect = createConnectRequest(route, state);
+                    handler.setCurrentRequest(HttpRequestWrapper.wrap(connect));
+                    break loop;
+                case HttpRouteDirector.TUNNEL_PROXY:
+                    throw new HttpException("Proxy chains are not supported");
+                case HttpRouteDirector.LAYER_PROTOCOL:
+                    handler.onRouteUpgrade();
+                    break;
+                case HttpRouteDirector.UNREACHABLE:
+                    throw new HttpException("Unable to establish route: " +
+                            "planned = " + route + "; current = " + fact);
+                case HttpRouteDirector.COMPLETE:
+                    handler.onRouteComplete();
+                    this.log.debug("[exchange: " + state.getId() + "] Connection route established");
+                    break;
+                default:
+                    throw new IllegalStateException("Unknown step indicator "
+                            + step + " from RouteDirector.");
+                }
+            } while (step > HttpRouteDirector.COMPLETE);
+        }
+
+        final HttpClientContext localContext = state.getLocalContext();
+        HttpRequestWrapper currentRequest = handler.getCurrentRequest();
+        if (currentRequest == null) {
+            currentRequest = state.getMainRequest();
+            handler.setCurrentRequest(currentRequest);
+        }
+
+        if (handler.isRouteEstablished()) {
+            state.incrementExecCount();
+            if (state.getExecCount() > 1) {
+                final HttpAsyncRequestProducer requestProducer = state.getRequestProducer();
+                if (!requestProducer.isRepeatable() && state.isRequestContentProduced()) {
+                    throw new NonRepeatableRequestException("Cannot retry request " +
+                            "with a non-repeatable request entity.");
+                }
+                requestProducer.resetRequest();
+            }
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("[exchange: " + state.getId() + "] Attempt " + state.getExecCount() +
+                    " to execute request");
+            }
+
+            if (!currentRequest.containsHeader(AUTH.WWW_AUTH_RESP)) {
+                final AuthState targetAuthState = localContext.getTargetAuthState();
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + state.getId() + "] Target auth state: " + targetAuthState.getState());
+                }
+                this.authenticator.generateAuthResponse(currentRequest, targetAuthState, localContext);
+            }
+            if (!currentRequest.containsHeader(AUTH.PROXY_AUTH_RESP) && !route.isTunnelled()) {
+                final AuthState proxyAuthState = localContext.getProxyAuthState();
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + state.getId() + "] Proxy auth state: " + proxyAuthState.getState());
+                }
+                this.authenticator.generateAuthResponse(currentRequest, proxyAuthState, localContext);
+            }
+        } else {
+            if (!currentRequest.containsHeader(AUTH.PROXY_AUTH_RESP)) {
+                final AuthState proxyAuthState = localContext.getProxyAuthState();
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + state.getId() + "] Proxy auth state: " + proxyAuthState.getState());
+                }
+                this.authenticator.generateAuthResponse(currentRequest, proxyAuthState, localContext);
+            }
+        }
+
+        final NHttpClientConnection managedConn = handler.getConnection();
+        localContext.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn);
+        final RequestConfig config = localContext.getRequestConfig();
+        if (config.getSocketTimeout() > 0) {
+            managedConn.setSocketTimeout(config.getSocketTimeout());
+        }
+        return currentRequest;
+    }
+
+    @Override
+    public void produceContent(
+            final InternalState state,
+            final ContentEncoder encoder,
+            final IOControl ioctrl) throws IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + state.getId() + "] produce content");
+        }
+        final HttpAsyncRequestProducer requestProducer = state.getRequestProducer();
+        state.setRequestContentProduced();
+        requestProducer.produceContent(encoder, ioctrl);
+        if (encoder.isCompleted()) {
+            requestProducer.resetRequest();
+        }
+    }
+
+    @Override
+    public void requestCompleted(
+            final InternalState state,
+            final AbstractClientExchangeHandler handler) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + state.getId() + "] Request completed");
+        }
+        final HttpClientContext localContext = state.getLocalContext();
+        final HttpAsyncRequestProducer requestProducer = state.getRequestProducer();
+        requestProducer.requestCompleted(localContext);
+    }
+
+    @Override
+    public void responseReceived(
+            final HttpResponse response,
+            final InternalState state,
+            final AbstractClientExchangeHandler handler) throws IOException, HttpException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + state.getId() + "] Response received " + response.getStatusLine());
+        }
+        final HttpClientContext context = state.getLocalContext();
+        context.setAttribute(HttpClientContext.HTTP_RESPONSE, response);
+        this.httpProcessor.process(response, context);
+
+        handler.setCurrentResponse(response);
+
+        if (!handler.isRouteEstablished()) {
+            final int status = response.getStatusLine().getStatusCode();
+            if (status < 200) {
+                throw new HttpException("Unexpected response to CONNECT request: " +
+                        response.getStatusLine());
+            }
+            if (status == HttpStatus.SC_OK) {
+                handler.onRouteTunnelToTarget();
+                handler.setCurrentRequest(null);
+            } else {
+                if (!handleConnectResponse(state, handler)) {
+                    state.setFinalResponse(response);
+                }
+            }
+        } else {
+            if (!handleResponse(state, handler)) {
+                state.setFinalResponse(response);
+            }
+        }
+        if (state.getFinalResponse() != null) {
+            final HttpAsyncResponseConsumer<?> responseConsumer = state.getResponseConsumer();
+            responseConsumer.responseReceived(response);
+        }
+    }
+
+    @Override
+    public void consumeContent(
+            final InternalState state,
+            final ContentDecoder decoder,
+            final IOControl ioctrl) throws IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + state.getId() + "] Consume content");
+        }
+        if (state.getFinalResponse() != null) {
+            final HttpAsyncResponseConsumer<?> responseConsumer = state.getResponseConsumer();
+            responseConsumer.consumeContent(decoder, ioctrl);
+        } else {
+            final ByteBuffer tmpbuf = state.getTmpbuf();
+            tmpbuf.clear();
+            decoder.read(tmpbuf);
+        }
+    }
+
+    @Override
+    public void responseCompleted(
+            final InternalState state,
+            final AbstractClientExchangeHandler handler) throws IOException, HttpException {
+        final HttpClientContext localContext = state.getLocalContext();
+        final HttpResponse currentResponse = handler.getCurrentResponse();
+
+        if (!handler.isRouteEstablished()) {
+            final int status = currentResponse.getStatusLine().getStatusCode();
+            if (status == HttpStatus.SC_OK) {
+                handler.setCurrentResponse(null);
+                return;
+            }
+        }
+
+        final boolean keepAlive = handler.manageConnectionPersistence();
+        if (!keepAlive) {
+            handler.releaseConnection();
+            final AuthState proxyAuthState = localContext.getProxyAuthState();
+            if (proxyAuthState.getState() == AuthProtocolState.SUCCESS
+                    && proxyAuthState.getAuthScheme() != null
+                    && proxyAuthState.getAuthScheme().isConnectionBased()) {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + state.getId() + "] Resetting proxy auth state");
+                }
+                proxyAuthState.reset();
+            }
+            final AuthState targetAuthState = localContext.getTargetAuthState();
+            if (targetAuthState.getState() == AuthProtocolState.SUCCESS
+                    && targetAuthState.getAuthScheme() != null
+                    && targetAuthState.getAuthScheme().isConnectionBased()) {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + state.getId() + "] Resetting target auth state");
+                }
+                targetAuthState.reset();
+            }
+        }
+
+        Object userToken = localContext.getUserToken();
+        if (userToken == null) {
+            userToken = this.userTokenHandler.getUserToken(localContext);
+            localContext.setAttribute(HttpClientContext.USER_TOKEN, userToken);
+        }
+
+        if (state.getFinalResponse() != null) {
+            final HttpAsyncResponseConsumer<?> responseConsumer = state.getResponseConsumer();
+            responseConsumer.responseCompleted(localContext);
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("[exchange: " + state.getId() + "] Response processed");
+            }
+            handler.releaseConnection();
+        } else {
+            if (state.getRedirect() != null) {
+                final HttpUriRequest redirect = state.getRedirect();
+                final URI uri = redirect.getURI();
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + state.getId() + "] Redirecting to '" + uri + "'");
+                }
+                state.setRedirect(null);
+
+                final HttpHost newTarget = URIUtils.extractHost(uri);
+                if (newTarget == null) {
+                    throw new ProtocolException("Redirect URI does not specify a valid host name: " + uri);
+                }
+
+                // Reset auth states if redirecting to another host
+                final HttpRoute route = handler.getRoute();
+                if (!route.getTargetHost().equals(newTarget)) {
+                    final AuthState targetAuthState = localContext.getTargetAuthState();
+                    if (this.log.isDebugEnabled()) {
+                        this.log.debug("[exchange: " + state.getId() + "] Resetting target auth state");
+                    }
+                    targetAuthState.reset();
+                    final AuthState proxyAuthState = localContext.getProxyAuthState();
+                    final AuthScheme authScheme = proxyAuthState.getAuthScheme();
+                    if (authScheme != null && authScheme.isConnectionBased()) {
+                        if (this.log.isDebugEnabled()) {
+                            this.log.debug("[exchange: " + state.getId() + "] Resetting proxy auth state");
+                        }
+                        proxyAuthState.reset();
+                    }
+                }
+
+                if (!redirect.headerIterator().hasNext()) {
+                    final HttpRequest original = state.getMainRequest().getOriginal();
+                    redirect.setHeaders(original.getAllHeaders());
+                }
+
+                final HttpRequestWrapper newRequest = HttpRequestWrapper.wrap(redirect);
+                final HttpRoute newRoute = this.routePlanner.determineRoute(
+                    newTarget, newRequest, localContext);
+                if (!route.equals(newRoute)) {
+                    handler.releaseConnection();
+                }
+                handler.setRoute(newRoute);
+                handler.setCurrentRequest(newRequest);
+                state.setMainRequest(newRequest);
+                prepareRequest(state, handler);
+            }
+        }
+        handler.setCurrentResponse(null);
+    }
+
+    private void rewriteRequestURI(
+            final HttpRequestWrapper request,
+            final HttpRoute route) throws ProtocolException {
+        try {
+            URI uri = request.getURI();
+            if (uri != null) {
+                if (route.getProxyHost() != null && !route.isTunnelled()) {
+                    // Make sure the request URI is absolute
+                    if (!uri.isAbsolute()) {
+                        final HttpHost target = route.getTargetHost();
+                        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, true);
+                    } else {
+                        uri = URIUtils.rewriteURI(uri);
+                    }
+                }
+                request.setURI(uri);
+            }
+        } catch (final URISyntaxException ex) {
+            throw new ProtocolException("Invalid URI: " +
+                    request.getRequestLine().getUri(), ex);
+        }
+    }
+
+    private void prepareRequest(
+            final InternalState state,
+            final AbstractClientExchangeHandler handler) throws IOException, HttpException {
+        final HttpClientContext localContext = state.getLocalContext();
+        final HttpRequestWrapper currentRequest = handler.getCurrentRequest();
+        final HttpRoute route = handler.getRoute();
+
+        final HttpRequest original = currentRequest.getOriginal();
+        URI uri = null;
+        if (original instanceof HttpUriRequest) {
+            uri = ((HttpUriRequest) original).getURI();
+        } else {
+            final String uriString = original.getRequestLine().getUri();
+            try {
+                uri = URI.create(uriString);
+            } catch (final IllegalArgumentException ex) {
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("[exchange: " + state.getId() + "] Unable to parse '" + uriString +
+                            "' as a valid URI; request URI and Host header may be inconsistent", ex);
+                }
+            }
+
+        }
+        currentRequest.setURI(uri);
+
+        // Re-write request URI if needed
+        rewriteRequestURI(currentRequest, route);
+
+        HttpHost target = null;
+        if (uri != null && uri.isAbsolute() && uri.getHost() != null) {
+            target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
+        }
+        if (target == null) {
+            target = route.getTargetHost();
+        }
+
+        // Get user info from the URI
+        if (uri != null) {
+            final String userinfo = uri.getUserInfo();
+            if (userinfo != null) {
+                final CredentialsProvider credsProvider = localContext.getCredentialsProvider();
+                credsProvider.setCredentials(
+                        new AuthScope(target),
+                        new UsernamePasswordCredentials(userinfo));
+            }
+        }
+
+        localContext.setAttribute(HttpClientContext.HTTP_REQUEST, currentRequest);
+        localContext.setAttribute(HttpClientContext.HTTP_TARGET_HOST, target);
+        localContext.setAttribute(HttpClientContext.HTTP_ROUTE, route);
+        this.httpProcessor.process(currentRequest, localContext);
+    }
+
+    private HttpRequest createConnectRequest(
+            final HttpRoute route, final InternalState state) throws IOException, HttpException {
+        // see RFC 2817, section 5.2 and
+        // INTERNET-DRAFT: Tunneling TCP based protocols through
+        // Web proxy servers
+        final HttpHost target = route.getTargetHost();
+        final String host = target.getHostName();
+        final int port = target.getPort();
+        final StringBuilder buffer = new StringBuilder(host.length() + 6);
+        buffer.append(host);
+        buffer.append(':');
+        buffer.append(Integer.toString(port));
+        final HttpRequest request = new BasicHttpRequest("CONNECT", buffer.toString(), HttpVersion.HTTP_1_1);
+        final HttpClientContext localContext = state.getLocalContext();
+        this.proxyHttpProcessor.process(request, localContext);
+        return request;
+    }
+
+    private boolean handleConnectResponse(
+            final InternalState state,
+            final AbstractClientExchangeHandler handler) throws HttpException {
+        final HttpClientContext localContext = state.getLocalContext();
+        final RequestConfig config = localContext.getRequestConfig();
+        if (config.isAuthenticationEnabled()) {
+            final CredentialsProvider credsProvider = localContext.getCredentialsProvider();
+            if (credsProvider != null) {
+                final HttpRoute route = handler.getRoute();
+                final HttpHost proxy = route.getProxyHost();
+                final HttpResponse currentResponse = handler.getCurrentResponse();
+                final AuthState proxyAuthState = localContext.getProxyAuthState();
+                if (this.authenticator.isAuthenticationRequested(proxy, currentResponse,
+                        this.proxyAuthStrategy, proxyAuthState, localContext)) {
+                    return this.authenticator.handleAuthChallenge(proxy, currentResponse,
+                            this.proxyAuthStrategy, proxyAuthState, localContext);
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean handleResponse(
+            final InternalState state,
+            final AbstractClientExchangeHandler handler) throws HttpException {
+        final HttpClientContext localContext = state.getLocalContext();
+        final RequestConfig config = localContext.getRequestConfig();
+        if (config.isAuthenticationEnabled()) {
+            if (needAuthentication(state, handler)) {
+                // discard previous auth headers
+                final HttpRequestWrapper currentRequest = handler.getCurrentRequest();
+                final HttpRequest original = currentRequest.getOriginal();
+                if (!original.containsHeader(AUTH.WWW_AUTH_RESP)) {
+                    currentRequest.removeHeaders(AUTH.WWW_AUTH_RESP);
+                }
+                if (!original.containsHeader(AUTH.PROXY_AUTH_RESP)) {
+                    currentRequest.removeHeaders(AUTH.PROXY_AUTH_RESP);
+                }
+                return true;
+            }
+        }
+        if (config.isRedirectsEnabled()) {
+            final HttpRequestWrapper currentRequest = handler.getCurrentRequest();
+            final HttpResponse currentResponse = handler.getCurrentResponse();
+            if (this.redirectStrategy.isRedirected(currentRequest, currentResponse, localContext)) {
+                final int maxRedirects = config.getMaxRedirects() >= 0 ? config.getMaxRedirects() : 100;
+                if (state.getRedirectCount() >= maxRedirects) {
+                    throw new RedirectException("Maximum redirects (" + maxRedirects + ") exceeded");
+                }
+                state.incrementRedirectCount();
+                final HttpUriRequest redirect = this.redirectStrategy.getRedirect(currentRequest.getOriginal(), currentResponse,
+                    localContext);
+                state.setRedirect(redirect);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean needAuthentication(
+            final InternalState state,
+            final AbstractClientExchangeHandler handler) throws HttpException {
+        final HttpClientContext localContext = state.getLocalContext();
+        final CredentialsProvider credsProvider = localContext.getCredentialsProvider();
+        if (credsProvider != null) {
+            final HttpRoute route = handler.getRoute();
+            final HttpResponse currentResponse = handler.getCurrentResponse();
+            HttpHost target = localContext.getTargetHost();
+            if (target == null) {
+                target = route.getTargetHost();
+            }
+            if (target.getPort() < 0) {
+                target = new HttpHost(
+                        target.getHostName(),
+                        route.getTargetHost().getPort(),
+                        target.getSchemeName());
+            }
+            final AuthState targetAuthState = localContext.getTargetAuthState();
+            final AuthState proxyAuthState = localContext.getProxyAuthState();
+
+            final boolean targetAuthRequested = this.authenticator.isAuthenticationRequested(
+                    target, currentResponse, this.targetAuthStrategy, targetAuthState, localContext);
+
+            HttpHost proxy = route.getProxyHost();
+            // if proxy is not set use target host instead
+            if (proxy == null) {
+                proxy = route.getTargetHost();
+            }
+            final boolean proxyAuthRequested = this.authenticator.isAuthenticationRequested(
+                    proxy, currentResponse, this.proxyAuthStrategy, proxyAuthState, localContext);
+
+            if (targetAuthRequested) {
+                return this.authenticator.handleAuthChallenge(target, currentResponse,
+                        this.targetAuthStrategy, targetAuthState, localContext);
+            }
+            if (proxyAuthRequested) {
+                return this.authenticator.handleAuthChallenge(proxy, currentResponse,
+                        this.proxyAuthStrategy, proxyAuthState, localContext);
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MinimalClientExchangeHandlerImpl.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MinimalClientExchangeHandlerImpl.java
new file mode 100644
index 0000000..64c423b
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MinimalClientExchangeHandlerImpl.java
@@ -0,0 +1,256 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.Configurable;
+import org.apache.http.client.methods.HttpExecutionAware;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.protocol.HttpCoreContext;
+import org.apache.http.protocol.HttpProcessor;
+
+/**
+ * Default implementation of {@link org.apache.http.nio.protocol.HttpAsyncClientExchangeHandler}.
+ * <p>
+ * Instances of this class are expected to be accessed by one thread at a time only.
+ * The {@link #cancel()} method can be called concurrently by multiple threads.
+ */
+class MinimalClientExchangeHandlerImpl<T> extends AbstractClientExchangeHandler {
+
+    private final HttpAsyncRequestProducer requestProducer;
+    private final HttpAsyncResponseConsumer<T> responseConsumer;
+    private final HttpClientContext localContext;
+    private final BasicFuture<T> resultFuture;
+    private final HttpProcessor httpProcessor;
+
+    public MinimalClientExchangeHandlerImpl(
+            final Log log,
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final HttpClientContext localContext,
+            final BasicFuture<T> resultFuture,
+            final NHttpClientConnectionManager connmgr,
+            final HttpProcessor httpProcessor,
+            final ConnectionReuseStrategy connReuseStrategy,
+            final ConnectionKeepAliveStrategy keepaliveStrategy) {
+        super(log, localContext, connmgr, connReuseStrategy, keepaliveStrategy);
+        this.requestProducer = requestProducer;
+        this.responseConsumer = responseConsumer;
+        this.localContext = localContext;
+        this.resultFuture = resultFuture;
+        this.httpProcessor = httpProcessor;
+    }
+
+    @Override
+    void releaseResources() {
+        try {
+            this.requestProducer.close();
+        } catch (final IOException ex) {
+            this.log.debug("I/O error closing request producer", ex);
+        }
+        try {
+            this.responseConsumer.close();
+        } catch (final IOException ex) {
+            this.log.debug("I/O error closing response consumer", ex);
+        }
+    }
+
+    @Override
+    void executionFailed(final Exception ex) {
+        this.requestProducer.failed(ex);
+        this.responseConsumer.failed(ex);
+    }
+
+    @Override
+    boolean executionCancelled() {
+        final boolean cancelled = this.responseConsumer.cancel();
+
+        final T result = this.responseConsumer.getResult();
+        final Exception ex = this.responseConsumer.getException();
+        if (ex != null) {
+            this.resultFuture.failed(ex);
+        } else if (result != null) {
+            this.resultFuture.completed(result);
+        } else {
+            this.resultFuture.cancel();
+        }
+        return cancelled;
+    }
+
+    public void start() throws HttpException, IOException {
+        final HttpHost target = this.requestProducer.getTarget();
+        final HttpRequest original = this.requestProducer.generateRequest();
+
+        if (original instanceof HttpExecutionAware) {
+            ((HttpExecutionAware) original).setCancellable(this);
+        }
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] start execution");
+        }
+
+        if (original instanceof Configurable) {
+            final RequestConfig config = ((Configurable) original).getConfig();
+            if (config != null) {
+                this.localContext.setRequestConfig(config);
+            }
+        }
+
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(original);
+        final HttpRoute route = new HttpRoute(target);
+        setCurrentRequest(request);
+        setRoute(route);
+
+        this.localContext.setAttribute(HttpClientContext.HTTP_REQUEST, request);
+        this.localContext.setAttribute(HttpClientContext.HTTP_TARGET_HOST, target);
+        this.localContext.setAttribute(HttpClientContext.HTTP_ROUTE, route);
+
+        this.httpProcessor.process(request, this.localContext);
+
+        requestConnection();
+    }
+
+    @Override
+    public HttpRequest generateRequest() throws IOException, HttpException {
+        verifytRoute();
+        if (!isRouteEstablished()) {
+            onRouteToTarget();
+            onRouteComplete();
+        }
+
+        final NHttpClientConnection localConn = getConnection();
+        this.localContext.setAttribute(HttpCoreContext.HTTP_CONNECTION, localConn);
+        final RequestConfig config = this.localContext.getRequestConfig();
+        if (config.getSocketTimeout() > 0) {
+            localConn.setSocketTimeout(config.getSocketTimeout());
+        }
+        return getCurrentRequest();
+    }
+
+    @Override
+    public void produceContent(
+            final ContentEncoder encoder, final IOControl ioctrl) throws IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] produce content");
+        }
+        this.requestProducer.produceContent(encoder, ioctrl);
+        if (encoder.isCompleted()) {
+            this.requestProducer.resetRequest();
+        }
+    }
+
+    @Override
+    public void requestCompleted() {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] Request completed");
+        }
+        this.requestProducer.requestCompleted(this.localContext);
+    }
+
+    @Override
+    public void responseReceived(
+            final HttpResponse response) throws IOException, HttpException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] Response received " + response.getStatusLine());
+        }
+        this.localContext.setAttribute(HttpClientContext.HTTP_RESPONSE, response);
+        this.httpProcessor.process(response, this.localContext);
+
+        setCurrentResponse(response);
+
+        this.responseConsumer.responseReceived(response);
+    }
+
+    @Override
+    public void consumeContent(
+            final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] Consume content");
+        }
+        this.responseConsumer.consumeContent(decoder, ioctrl);
+        if (!decoder.isCompleted() && this.responseConsumer.isDone()) {
+            markConnectionNonReusable();
+            try {
+                markCompleted();
+                releaseConnection();
+                this.resultFuture.cancel();
+            } finally {
+                close();
+            }
+        }
+    }
+
+    @Override
+    public void responseCompleted() throws IOException, HttpException {
+        manageConnectionPersistence();
+        this.responseConsumer.responseCompleted(this.localContext);
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] Response processed");
+        }
+        try {
+            markCompleted();
+            releaseConnection();
+            final T result = this.responseConsumer.getResult();
+            final Exception ex = this.responseConsumer.getException();
+            if (ex == null) {
+                this.resultFuture.completed(result);
+            } else {
+                this.resultFuture.failed(ex);
+            }
+        } finally {
+            close();
+        }
+    }
+
+    @Override
+    public void inputTerminated() {
+        close();
+    }
+
+    public void abortConnection() {
+        discardConnection();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MinimalHttpAsyncClient.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MinimalHttpAsyncClient.java
new file mode 100644
index 0000000..12a6a20
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MinimalHttpAsyncClient.java
@@ -0,0 +1,148 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpHost;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
+import org.apache.http.nio.NHttpClientEventHandler;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.protocol.HttpAsyncRequestExecutor;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpProcessor;
+
+class MinimalHttpAsyncClient extends CloseableHttpAsyncClientBase {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    private final NHttpClientConnectionManager connmgr;
+    private final HttpProcessor httpProcessor;
+    private final ConnectionReuseStrategy connReuseStrategy;
+    private final ConnectionKeepAliveStrategy keepaliveStrategy;
+
+    public MinimalHttpAsyncClient(
+            final NHttpClientConnectionManager connmgr,
+            final ThreadFactory threadFactory,
+            final NHttpClientEventHandler eventHandler,
+            final HttpProcessor httpProcessor,
+            final ConnectionReuseStrategy connReuseStrategy,
+            final ConnectionKeepAliveStrategy keepaliveStrategy) {
+        super(connmgr, threadFactory, eventHandler);
+        this.connmgr = connmgr;
+        this.httpProcessor = httpProcessor;
+        this.connReuseStrategy = connReuseStrategy;
+        this.keepaliveStrategy = keepaliveStrategy;
+    }
+
+    public MinimalHttpAsyncClient(
+            final NHttpClientConnectionManager connmgr,
+            final HttpProcessor httpProcessor) {
+        this(connmgr,
+                Executors.defaultThreadFactory(),
+                new HttpAsyncRequestExecutor(),
+                httpProcessor,
+                DefaultConnectionReuseStrategy.INSTANCE,
+                DefaultConnectionKeepAliveStrategy.INSTANCE);
+    }
+
+    @Override
+    public <T> Future<T> execute(
+            final HttpAsyncRequestProducer requestProducer,
+            final HttpAsyncResponseConsumer<T> responseConsumer,
+            final HttpContext context,
+            final FutureCallback<T> callback) {
+        ensureRunning();
+        final BasicFuture<T> future = new BasicFuture<T>(callback);
+        final HttpClientContext localcontext = HttpClientContext.adapt(
+            context != null ? context : new BasicHttpContext());
+
+        @SuppressWarnings("resource")
+        final MinimalClientExchangeHandlerImpl<T> handler = new MinimalClientExchangeHandlerImpl<T>(
+            this.log,
+            requestProducer,
+            responseConsumer,
+            localcontext,
+            future,
+            this.connmgr,
+            this.httpProcessor,
+            this.connReuseStrategy,
+            this.keepaliveStrategy);
+        try {
+            handler.start();
+        } catch (final Exception ex) {
+            handler.failed(ex);
+        }
+        return new FutureWrapper<T>(future, handler);
+    }
+
+    @Override
+    public <T> Future<List<T>> execute(
+            final HttpHost target,
+            final List<? extends HttpAsyncRequestProducer> requestProducers,
+            final List<? extends HttpAsyncResponseConsumer<T>> responseConsumers,
+            final HttpContext context,
+            final FutureCallback<List<T>> callback) {
+        ensureRunning();
+        final BasicFuture<List<T>> future = new BasicFuture<List<T>>(callback);
+        final HttpClientContext localcontext = HttpClientContext.adapt(
+                context != null ? context : new BasicHttpContext());
+        @SuppressWarnings("resource")
+        final PipeliningClientExchangeHandlerImpl<T> handler = new PipeliningClientExchangeHandlerImpl<T>(
+                this.log,
+                target,
+                requestProducers,
+                responseConsumers,
+                localcontext,
+                future,
+                this.connmgr,
+                this.httpProcessor,
+                this.connReuseStrategy,
+                this.keepaliveStrategy);
+        try {
+            handler.start();
+        } catch (final Exception ex) {
+            handler.failed(ex);
+        }
+        return new FutureWrapper<List<T>>(future, handler);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MinimalHttpAsyncClientBuilder.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MinimalHttpAsyncClientBuilder.java
new file mode 100644
index 0000000..dc76318
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/MinimalHttpAsyncClientBuilder.java
@@ -0,0 +1,164 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.client.protocol.RequestAddCookies;
+import org.apache.http.client.protocol.RequestClientConnControl;
+import org.apache.http.client.protocol.ResponseProcessCookies;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
+import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
+import org.apache.http.impl.nio.reactor.IOReactorConfig;
+import org.apache.http.nio.NHttpClientEventHandler;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.protocol.HttpAsyncRequestExecutor;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.protocol.HttpProcessorBuilder;
+import org.apache.http.protocol.RequestContent;
+import org.apache.http.protocol.RequestTargetHost;
+import org.apache.http.protocol.RequestUserAgent;
+import org.apache.http.util.VersionInfo;
+
+/**
+ * Builder for {@link org.apache.http.impl.nio.client.MinimalHttpAsyncClient} instances.
+ *
+ * @since 4.1
+ */
+class MinimalHttpAsyncClientBuilder {
+
+    private NHttpClientConnectionManager connManager;
+    private boolean connManagerShared;
+    private ConnectionReuseStrategy reuseStrategy;
+    private ConnectionKeepAliveStrategy keepAliveStrategy;
+    private String userAgent;
+    private ThreadFactory threadFactory;
+    private boolean cookieManagementDisabled;
+
+    public static MinimalHttpAsyncClientBuilder create() {
+        return new MinimalHttpAsyncClientBuilder();
+    }
+
+    protected MinimalHttpAsyncClientBuilder() {
+        super();
+    }
+
+    public final MinimalHttpAsyncClientBuilder setConnectionManager(
+            final NHttpClientConnectionManager connManager) {
+        this.connManager = connManager;
+        return this;
+    }
+
+    public final MinimalHttpAsyncClientBuilder setConnectionManagerShared(
+            final boolean shared) {
+        this.connManagerShared = shared;
+        return this;
+    }
+
+    public final MinimalHttpAsyncClientBuilder setConnectionReuseStrategy(
+            final ConnectionReuseStrategy reuseStrategy) {
+        this.reuseStrategy = reuseStrategy;
+        return this;
+    }
+
+    public final MinimalHttpAsyncClientBuilder setKeepAliveStrategy(
+            final ConnectionKeepAliveStrategy keepAliveStrategy) {
+        this.keepAliveStrategy = keepAliveStrategy;
+        return this;
+    }
+
+    public final MinimalHttpAsyncClientBuilder setUserAgent(final String userAgent) {
+        this.userAgent = userAgent;
+        return this;
+    }
+
+    public final MinimalHttpAsyncClientBuilder setThreadFactory(final ThreadFactory threadFactory) {
+        this.threadFactory = threadFactory;
+        return this;
+    }
+
+    public final MinimalHttpAsyncClientBuilder disableCookieManagement() {
+        cookieManagementDisabled = true;
+        return this;
+    }
+
+    public MinimalHttpAsyncClient build() {
+
+        NHttpClientConnectionManager connManager = this.connManager;
+        if (connManager == null) {
+            connManager = new PoolingNHttpClientConnectionManager(IOReactorUtils.create(IOReactorConfig.DEFAULT,
+                    threadFactory));
+        }
+        ConnectionReuseStrategy reuseStrategy = this.reuseStrategy;
+        if (reuseStrategy == null) {
+            reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE;
+        }
+        ConnectionKeepAliveStrategy keepAliveStrategy = this.keepAliveStrategy;
+        if (keepAliveStrategy == null) {
+            keepAliveStrategy = DefaultConnectionKeepAliveStrategy.INSTANCE;
+        }
+        String userAgent = this.userAgent;
+        if (userAgent == null) {
+            userAgent = VersionInfo.getUserAgent(
+                    "Apache-HttpAsyncClient", "org.apache.http.nio.client", getClass());
+        }
+        final HttpProcessorBuilder b = HttpProcessorBuilder.create();
+        b.addAll(
+                new RequestContent(),
+                new RequestTargetHost(),
+                new RequestClientConnControl(),
+                new RequestUserAgent(userAgent));
+        if (!cookieManagementDisabled) {
+            b.add(new RequestAddCookies());
+            b.add(new ResponseProcessCookies());
+        }
+        final HttpProcessor httpprocessor = b.build();
+
+        ThreadFactory threadFactory = null;
+        NHttpClientEventHandler eventHandler = null;
+        if (!this.connManagerShared) {
+            threadFactory = this.threadFactory;
+            if (threadFactory == null) {
+                threadFactory = Executors.defaultThreadFactory();
+            }
+            eventHandler = new HttpAsyncRequestExecutor();
+        }
+        return new MinimalHttpAsyncClient(
+            connManager,
+            threadFactory,
+            eventHandler,
+            httpprocessor,
+            reuseStrategy,
+            keepAliveStrategy);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/PipeliningClientExchangeHandlerImpl.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/PipeliningClientExchangeHandlerImpl.java
new file mode 100644
index 0000000..7758d9a
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/PipeliningClientExchangeHandlerImpl.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.nio.client;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.ConnectionClosedException;
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpRequestWrapper;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.nio.protocol.Pipelined;
+import org.apache.http.protocol.HttpCoreContext;
+import org.apache.http.protocol.HttpProcessor;
+import org.apache.http.util.Args;
+import org.apache.http.util.Asserts;
+
+/**
+ * {@link org.apache.http.nio.protocol.HttpAsyncClientExchangeHandler} implementation
+ * that supports HTTP message pipelining.
+ * <p>
+ * Instances of this class are expected to be accessed by one thread at a time only.
+ * The {@link #cancel()} method can be called concurrently by multiple threads.
+ */
+@Pipelined
+class PipeliningClientExchangeHandlerImpl<T> extends AbstractClientExchangeHandler {
+
+    private final HttpHost target;
+    private final Queue<HttpAsyncRequestProducer> requestProducerQueue;
+    private final Queue<HttpAsyncResponseConsumer<T>> responseConsumerQueue;
+    private final Queue<HttpRequest> requestQueue;
+    private final Queue<T> resultQueue;
+    private final HttpClientContext localContext;
+    private final BasicFuture<List<T>> resultFuture;
+    private final HttpProcessor httpProcessor;
+    private final AtomicReference<HttpAsyncRequestProducer> requestProducerRef;
+    private final AtomicReference<HttpAsyncResponseConsumer<T>> responseConsumerRef;
+
+    public PipeliningClientExchangeHandlerImpl(
+            final Log log,
+            final HttpHost target,
+            final List<? extends HttpAsyncRequestProducer> requestProducers,
+            final List<? extends HttpAsyncResponseConsumer<T>> responseConsumers,
+            final HttpClientContext localContext,
+            final BasicFuture<List<T>> resultFuture,
+            final NHttpClientConnectionManager connmgr,
+            final HttpProcessor httpProcessor,
+            final ConnectionReuseStrategy connReuseStrategy,
+            final ConnectionKeepAliveStrategy keepaliveStrategy) {
+        super(log, localContext, connmgr, connReuseStrategy, keepaliveStrategy);
+        Args.notNull(target, "HTTP target");
+        Args.notEmpty(requestProducers, "Request producer list");
+        Args.notEmpty(responseConsumers, "Response consumer list");
+        Args.check(requestProducers.size() == responseConsumers.size(),
+                "Number of request producers does not match that of response consumers");
+        this.target = target;
+        this.requestProducerQueue = new ConcurrentLinkedQueue<HttpAsyncRequestProducer>(requestProducers);
+        this.responseConsumerQueue = new ConcurrentLinkedQueue<HttpAsyncResponseConsumer<T>>(responseConsumers);
+        this.requestQueue = new ConcurrentLinkedQueue<HttpRequest>();
+        this.resultQueue = new ConcurrentLinkedQueue<T>();
+        this.localContext = localContext;
+        this.resultFuture = resultFuture;
+        this.httpProcessor = httpProcessor;
+        this.requestProducerRef = new AtomicReference<HttpAsyncRequestProducer>(null);
+        this.responseConsumerRef = new AtomicReference<HttpAsyncResponseConsumer<T>>(null);
+    }
+
+    private void closeProducer(final HttpAsyncRequestProducer requestProducer) {
+        if (requestProducer != null) {
+            try {
+                requestProducer.close();
+            } catch (final IOException ex) {
+                this.log.debug("I/O error closing request producer", ex);
+            }
+        }
+    }
+
+    private void closeConsumer(final HttpAsyncResponseConsumer<?> responseConsumer) {
+        if (responseConsumer != null) {
+            try {
+                responseConsumer.close();
+            } catch (final IOException ex) {
+                this.log.debug("I/O error closing response consumer", ex);
+            }
+        }
+    }
+
+    @Override
+    void releaseResources() {
+        closeProducer(this.requestProducerRef.getAndSet(null));
+        closeConsumer(this.responseConsumerRef.getAndSet(null));
+        while (!this.requestProducerQueue.isEmpty()) {
+            closeProducer(this.requestProducerQueue.remove());
+        }
+        while (!this.responseConsumerQueue.isEmpty()) {
+            closeConsumer(this.responseConsumerQueue.remove());
+        }
+        this.requestQueue.clear();
+        this.resultQueue.clear();
+    }
+
+    @Override
+    void executionFailed(final Exception ex) {
+        final HttpAsyncRequestProducer requestProducer = this.requestProducerRef.get();
+        if (requestProducer != null) {
+            requestProducer.failed(ex);
+        }
+        final HttpAsyncResponseConsumer<T> responseConsumer = this.responseConsumerRef.get();
+        if (responseConsumer != null) {
+            responseConsumer.failed(ex);
+        }
+        for (final HttpAsyncResponseConsumer<T> cancellable: this.responseConsumerQueue) {
+            cancellable.cancel();
+        }
+    }
+
+    @Override
+    boolean executionCancelled() {
+        final HttpAsyncResponseConsumer<T> responseConsumer = this.responseConsumerRef.get();
+        final boolean cancelled = responseConsumer != null && responseConsumer.cancel();
+        this.resultFuture.cancel();
+        return cancelled;
+    }
+
+    public void start() throws HttpException, IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] start execution");
+        }
+
+        final HttpRoute route = new HttpRoute(this.target);
+        setRoute(route);
+
+        this.localContext.setAttribute(HttpClientContext.HTTP_TARGET_HOST, this.target);
+        this.localContext.setAttribute(HttpClientContext.HTTP_ROUTE, route);
+
+        requestConnection();
+    }
+
+    @Override
+    public HttpRequest generateRequest() throws IOException, HttpException {
+        verifytRoute();
+        if (!isRouteEstablished()) {
+            onRouteToTarget();
+            onRouteComplete();
+        }
+        final NHttpClientConnection localConn = getConnection();
+        this.localContext.setAttribute(HttpCoreContext.HTTP_CONNECTION, localConn);
+
+        Asserts.check(this.requestProducerRef.get() == null, "Inconsistent state: currentRequest producer is not null");
+        final HttpAsyncRequestProducer requestProducer = this.requestProducerQueue.poll();
+        if (requestProducer == null) {
+            return null;
+        }
+        this.requestProducerRef.set(requestProducer);
+
+        final HttpRequest original = requestProducer.generateRequest();
+        final HttpRequestWrapper currentRequest = HttpRequestWrapper.wrap(original);
+        final RequestConfig config = this.localContext.getRequestConfig();
+        if (config.getSocketTimeout() > 0) {
+            localConn.setSocketTimeout(config.getSocketTimeout());
+        }
+
+        this.httpProcessor.process(currentRequest, this.localContext);
+
+        this.requestQueue.add(currentRequest);
+        setCurrentRequest(currentRequest);
+
+        return currentRequest;
+    }
+
+    @Override
+    public void produceContent(
+            final ContentEncoder encoder, final IOControl ioctrl) throws IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] produce content");
+        }
+        final HttpAsyncRequestProducer requestProducer = this.requestProducerRef.get();
+        Asserts.check(requestProducer != null, "Inconsistent state: request producer is null");
+        requestProducer.produceContent(encoder, ioctrl);
+        if (encoder.isCompleted()) {
+            requestProducer.resetRequest();
+        }
+    }
+
+    @Override
+    public void requestCompleted() {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] Request completed");
+        }
+        final HttpAsyncRequestProducer requestProducer = this.requestProducerRef.getAndSet(null);
+        Asserts.check(requestProducer != null, "Inconsistent state: request producer is null");
+        requestProducer.requestCompleted(this.localContext);
+        try {
+            requestProducer.close();
+        } catch (final IOException ioex) {
+            this.log.debug(ioex.getMessage(), ioex);
+        }
+    }
+
+    @Override
+    public void responseReceived(
+            final HttpResponse response) throws IOException, HttpException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] Response received " + response.getStatusLine());
+        }
+
+        Asserts.check(this.responseConsumerRef.get() == null, "Inconsistent state: response consumer is not null");
+
+        final HttpAsyncResponseConsumer<T> responseConsumer = this.responseConsumerQueue.poll();
+        Asserts.check(responseConsumer != null, "Inconsistent state: response consumer queue is empty");
+        this.responseConsumerRef.set(responseConsumer);
+
+        final HttpRequest request = this.requestQueue.poll();
+        Asserts.check(request != null, "Inconsistent state: request queue is empty");
+
+        this.localContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
+        this.localContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
+        this.httpProcessor.process(response, this.localContext);
+
+        responseConsumer.responseReceived(response);
+
+        setCurrentResponse(response);
+    }
+
+    @Override
+    public void consumeContent(
+            final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] Consume content");
+        }
+        final HttpAsyncResponseConsumer<T> responseConsumer = this.responseConsumerRef.get();
+        Asserts.check(responseConsumer != null, "Inconsistent state: response consumer is null");
+        responseConsumer.consumeContent(decoder, ioctrl);
+    }
+
+    @Override
+    public void responseCompleted() throws IOException, HttpException {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("[exchange: " + getId() + "] Response processed");
+        }
+
+        final boolean keepAlive = manageConnectionPersistence();
+
+        final HttpAsyncResponseConsumer<T> responseConsumer = this.responseConsumerRef.getAndSet(null);
+        Asserts.check(responseConsumer != null, "Inconsistent state: response consumer is null");
+        try {
+            responseConsumer.responseCompleted(this.localContext);
+            final T result = responseConsumer.getResult();
+            final Exception ex = responseConsumer.getException();
+            try {
+                responseConsumer.close();
+            } catch (final IOException ioex) {
+                this.log.debug(ioex.getMessage(), ioex);
+            }
+            if (result != null) {
+                this.resultQueue.add(result);
+            } else {
+                failed(ex);
+            }
+            if (!this.resultFuture.isDone() && this.responseConsumerQueue.isEmpty()) {
+                this.resultFuture.completed(new ArrayList<T>(this.resultQueue));
+                this.resultQueue.clear();
+            }
+
+            if (this.resultFuture.isDone()) {
+                close();
+            } else {
+                if (!keepAlive) {
+                    failed(new ConnectionClosedException("Connection closed"));
+                } else {
+                    final NHttpClientConnection localConn = getConnection();
+                    if (localConn != null) {
+                        localConn.requestOutput();
+                    } else {
+                        requestConnection();
+                    }
+                }
+            }
+        } catch (final RuntimeException ex) {
+            failed(ex);
+            throw ex;
+        }
+    }
+
+    @Override
+    public void inputTerminated() {
+        failed(new ConnectionClosedException("Connection closed"));
+    }
+
+    public void abortConnection() {
+        discardConnection();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/package-info.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/package-info.java
new file mode 100644
index 0000000..10110e7
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/package-info.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/>.
+ *
+ */
+
+/**
+ * Default asynchronous HTTP client implementation.
+ * <p>
+ * The usual execution flow can be demonstrated by the code snippet below:
+ * <pre>
+ * CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
+ * try {
+ *     httpclient.start();
+ *     HttpGet request = new HttpGet("http://www.apache.org/");
+ *     Future<HttpResponse> future = httpclient.execute(request, null);
+ *     HttpResponse response = future.get();
+ *     System.out.println(response.getStatusLine());
+ *     // Do something useful with the response body
+ * } finally {
+ *     httpclient.close();
+ * }
+ * </pre>
+ */
+package org.apache.http.impl.nio.client;
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/CPool.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/CPool.java
new file mode 100644
index 0000000..de3a7e4
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/CPool.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.nio.conn;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.nio.pool.AbstractNIOConnPool;
+import org.apache.http.nio.pool.NIOConnFactory;
+import org.apache.http.nio.pool.SocketAddressResolver;
+import org.apache.http.nio.reactor.ConnectingIOReactor;
+
+@Contract(threading = ThreadingBehavior.SAFE)
+class CPool extends AbstractNIOConnPool<HttpRoute, ManagedNHttpClientConnection, CPoolEntry> {
+
+    private final Log log = LogFactory.getLog(CPool.class);
+
+    private final long timeToLive;
+    private final TimeUnit tunit;
+
+    public CPool(
+            final ConnectingIOReactor ioreactor,
+            final NIOConnFactory<HttpRoute, ManagedNHttpClientConnection> connFactory,
+            final SocketAddressResolver<HttpRoute> addressResolver,
+            final int defaultMaxPerRoute, final int maxTotal,
+            final long timeToLive, final TimeUnit tunit) {
+        super(ioreactor, connFactory, addressResolver, defaultMaxPerRoute, maxTotal);
+        this.timeToLive = timeToLive;
+        this.tunit = tunit;
+    }
+
+    @Override
+    protected CPoolEntry createEntry(final HttpRoute route, final ManagedNHttpClientConnection conn) {
+        final CPoolEntry entry =  new CPoolEntry(this.log, conn.getId(), route, conn, this.timeToLive, this.tunit);
+        entry.setSocketTimeout(conn.getSocketTimeout());
+        return entry;
+    }
+
+    @Override
+    protected void onLease(final CPoolEntry entry) {
+        final NHttpClientConnection conn = entry.getConnection();
+        conn.setSocketTimeout(entry.getSocketTimeout());
+    }
+
+    @Override
+    protected void onRelease(final CPoolEntry entry) {
+        final NHttpClientConnection conn = entry.getConnection();
+        conn.setSocketTimeout(0);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/CPoolEntry.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/CPoolEntry.java
new file mode 100644
index 0000000..d8a43cd
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/CPoolEntry.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.impl.nio.conn;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.pool.PoolEntry;
+
+@Contract(threading = ThreadingBehavior.SAFE)
+class CPoolEntry extends PoolEntry<HttpRoute, ManagedNHttpClientConnection> {
+
+    private final Log log;
+    private volatile int socketTimeout;
+    private volatile boolean routeComplete;
+
+    public CPoolEntry(
+            final Log log,
+            final String id,
+            final HttpRoute route,
+            final ManagedNHttpClientConnection conn,
+            final long timeToLive, final TimeUnit tunit) {
+        super(id, route, conn, timeToLive, tunit);
+        this.log = log;
+    }
+
+    public boolean isRouteComplete() {
+        return this.routeComplete;
+    }
+
+    public void markRouteComplete() {
+        this.routeComplete = true;
+    }
+
+    public int getSocketTimeout() {
+        return this.socketTimeout;
+    }
+
+    public void setSocketTimeout(final int socketTimeout) {
+        this.socketTimeout = socketTimeout;
+    }
+
+    public void closeConnection() throws IOException {
+        final ManagedNHttpClientConnection conn = getConnection();
+        conn.close();
+    }
+
+    public void shutdownConnection() throws IOException {
+        final ManagedNHttpClientConnection conn = getConnection();
+        conn.shutdown();
+    }
+
+    @Override
+    public boolean isExpired(final long now) {
+        final boolean expired = super.isExpired(now);
+        if (expired && this.log.isDebugEnabled()) {
+            this.log.debug("Connection " + this + " expired @ " + new Date(getExpiry()));
+        }
+        return expired;
+    }
+
+    @Override
+    public boolean isClosed() {
+        final ManagedNHttpClientConnection conn = getConnection();
+        return !conn.isOpen();
+    }
+
+    @Override
+    public void close() {
+        try {
+            closeConnection();
+        } catch (final IOException ex) {
+            this.log.debug("I/O error closing connection", ex);
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/CPoolProxy.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/CPoolProxy.java
new file mode 100644
index 0000000..e38246c
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/CPoolProxy.java
@@ -0,0 +1,278 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.http.HttpConnectionMetrics;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.impl.conn.ConnectionShutdownException;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.protocol.HttpContext;
+
+class CPoolProxy implements ManagedNHttpClientConnection {
+
+    private volatile CPoolEntry poolEntry;
+
+    CPoolProxy(final CPoolEntry entry) {
+        super();
+        this.poolEntry = entry;
+    }
+
+    CPoolEntry getPoolEntry() {
+        return this.poolEntry;
+    }
+
+    CPoolEntry detach() {
+        final CPoolEntry local = this.poolEntry;
+        this.poolEntry = null;
+        return local;
+    }
+
+    ManagedNHttpClientConnection getConnection() {
+        final CPoolEntry local = this.poolEntry;
+        if (local == null) {
+            return null;
+        }
+        return local.getConnection();
+    }
+
+    ManagedNHttpClientConnection getValidConnection() {
+        final ManagedNHttpClientConnection conn = getConnection();
+        if (conn == null) {
+            throw new ConnectionShutdownException();
+        }
+        return conn;
+    }
+
+    @Override
+    public void close() throws IOException {
+        final CPoolEntry local = this.poolEntry;
+        if (local != null) {
+            local.closeConnection();
+        }
+    }
+
+    @Override
+    public void shutdown() throws IOException {
+        final CPoolEntry local = this.poolEntry;
+        if (local != null) {
+            local.shutdownConnection();
+        }
+    }
+
+    @Override
+    public HttpConnectionMetrics getMetrics() {
+        return getValidConnection().getMetrics();
+    }
+
+    @Override
+    public void requestInput() {
+        final NHttpClientConnection conn = getConnection();
+        if (conn != null) {
+            conn.requestInput();
+        }
+    }
+
+    @Override
+    public void suspendInput() {
+        final NHttpClientConnection conn = getConnection();
+        if (conn != null) {
+            conn.suspendInput();
+        }
+    }
+
+    @Override
+    public void requestOutput() {
+        final NHttpClientConnection conn = getConnection();
+        if (conn != null) {
+            conn.requestOutput();
+        }
+    }
+
+    @Override
+    public void suspendOutput() {
+        final NHttpClientConnection conn = getConnection();
+        if (conn != null) {
+            conn.suspendOutput();
+        }
+    }
+
+    @Override
+    public InetAddress getLocalAddress() {
+        return getValidConnection().getLocalAddress();
+    }
+
+    @Override
+    public int getLocalPort() {
+        return getValidConnection().getLocalPort();
+    }
+
+    @Override
+    public InetAddress getRemoteAddress() {
+        return getValidConnection().getRemoteAddress();
+    }
+
+    @Override
+    public int getRemotePort() {
+        return getValidConnection().getRemotePort();
+    }
+
+    @Override
+    public boolean isOpen() {
+        final CPoolEntry local = this.poolEntry;
+        if (local != null) {
+            return !local.isClosed();
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isStale() {
+        final NHttpClientConnection conn = getConnection();
+        if (conn != null) {
+            return !conn.isOpen();
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public void setSocketTimeout(final int i) {
+        getValidConnection().setSocketTimeout(i);
+    }
+
+    @Override
+    public int getSocketTimeout() {
+        return getValidConnection().getSocketTimeout();
+    }
+
+    @Override
+    public void submitRequest(final HttpRequest request) throws IOException, HttpException {
+        getValidConnection().submitRequest(request);
+    }
+
+    @Override
+    public boolean isRequestSubmitted() {
+        return getValidConnection().isRequestSubmitted();
+    }
+
+    @Override
+    public void resetOutput() {
+        getValidConnection().resetOutput();
+    }
+
+    @Override
+    public void resetInput() {
+        getValidConnection().resetInput();
+    }
+
+    @Override
+    public int getStatus() {
+        return getValidConnection().getStatus();
+    }
+
+    @Override
+    public HttpRequest getHttpRequest() {
+        return getValidConnection().getHttpRequest();
+    }
+
+    @Override
+    public HttpResponse getHttpResponse() {
+        return getValidConnection().getHttpResponse();
+    }
+
+    @Override
+    public HttpContext getContext() {
+        return getValidConnection().getContext();
+    }
+
+    public static NHttpClientConnection newProxy(final CPoolEntry poolEntry) {
+        return new CPoolProxy(poolEntry);
+    }
+
+    private static CPoolProxy getProxy(final NHttpClientConnection conn) {
+        if (!CPoolProxy.class.isInstance(conn)) {
+            throw new IllegalStateException("Unexpected connection proxy class: " + conn.getClass());
+        }
+        return CPoolProxy.class.cast(conn);
+    }
+
+    public static CPoolEntry getPoolEntry(final NHttpClientConnection proxy) {
+        final CPoolEntry entry = getProxy(proxy).getPoolEntry();
+        if (entry == null) {
+            throw new ConnectionShutdownException();
+        }
+        return entry;
+    }
+
+    public static CPoolEntry detach(final NHttpClientConnection proxy) {
+        return getProxy(proxy).detach();
+    }
+
+    @Override
+    public String getId() {
+        return getValidConnection().getId();
+    }
+
+    @Override
+    public void bind(final IOSession iosession) {
+        getValidConnection().bind(iosession);
+    }
+
+    @Override
+    public IOSession getIOSession() {
+        return getValidConnection().getIOSession();
+    }
+
+    @Override
+    public SSLSession getSSLSession() {
+        return getValidConnection().getSSLSession();
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("CPoolProxy{");
+        final ManagedNHttpClientConnection conn = getConnection();
+        if (conn != null) {
+            sb.append(conn);
+        } else {
+            sb.append("detached");
+        }
+        sb.append('}');
+        return sb.toString();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/LoggingIOSession.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/LoggingIOSession.java
new file mode 100644
index 0000000..57db166
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/LoggingIOSession.java
@@ -0,0 +1,249 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.ByteChannel;
+import java.nio.channels.SelectionKey;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.nio.reactor.SessionBufferStatus;
+
+class LoggingIOSession implements IOSession {
+
+    private final IOSession session;
+    private final ByteChannel channel;
+    private final String id;
+    private final Log log;
+    private final Wire wirelog;
+
+    public LoggingIOSession(final IOSession session, final String id, final Log log, final Log wirelog) {
+        super();
+        this.session = session;
+        this.channel = new LoggingByteChannel();
+        this.id = id;
+        this.log = log;
+        this.wirelog = new Wire(wirelog, this.id);
+    }
+
+    @Override
+    public ByteChannel channel() {
+        return this.channel;
+    }
+
+    @Override
+    public SocketAddress getLocalAddress() {
+        return this.session.getLocalAddress();
+    }
+
+    @Override
+    public SocketAddress getRemoteAddress() {
+        return this.session.getRemoteAddress();
+    }
+
+    @Override
+    public int getEventMask() {
+        return this.session.getEventMask();
+    }
+
+    private static String formatOps(final int ops) {
+        final StringBuilder buffer = new StringBuilder(6);
+        buffer.append('[');
+        if ((ops & SelectionKey.OP_READ) > 0) {
+            buffer.append('r');
+        }
+        if ((ops & SelectionKey.OP_WRITE) > 0) {
+            buffer.append('w');
+        }
+        if ((ops & SelectionKey.OP_ACCEPT) > 0) {
+            buffer.append('a');
+        }
+        if ((ops & SelectionKey.OP_CONNECT) > 0) {
+            buffer.append('c');
+        }
+        buffer.append(']');
+        return buffer.toString();
+    }
+
+    @Override
+    public void setEventMask(final int ops) {
+        this.session.setEventMask(ops);
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(this.id + " " + this.session + ": Event mask set " + formatOps(ops));
+        }
+    }
+
+    @Override
+    public void setEvent(final int op) {
+        this.session.setEvent(op);
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(this.id + " " + this.session + ": Event set " + formatOps(op));
+        }
+    }
+
+    @Override
+    public void clearEvent(final int op) {
+        this.session.clearEvent(op);
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(this.id + " " + this.session + ": Event cleared " + formatOps(op));
+        }
+    }
+
+    @Override
+    public void close() {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(this.id + " " + this.session + ": Close");
+        }
+        this.session.close();
+    }
+
+    @Override
+    public int getStatus() {
+        return this.session.getStatus();
+    }
+
+    @Override
+    public boolean isClosed() {
+        return this.session.isClosed();
+    }
+
+    @Override
+    public void shutdown() {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(this.id + " " + this.session + ": Shutdown");
+        }
+        this.session.shutdown();
+    }
+
+    @Override
+    public int getSocketTimeout() {
+        return this.session.getSocketTimeout();
+    }
+
+    @Override
+    public void setSocketTimeout(final int timeout) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(this.id + " " + this.session + ": Set timeout " + timeout);
+        }
+        this.session.setSocketTimeout(timeout);
+    }
+
+    @Override
+    public void setBufferStatus(final SessionBufferStatus status) {
+        this.session.setBufferStatus(status);
+    }
+
+    @Override
+    public boolean hasBufferedInput() {
+        return this.session.hasBufferedInput();
+    }
+
+    @Override
+    public boolean hasBufferedOutput() {
+        return this.session.hasBufferedOutput();
+    }
+
+    @Override
+    public Object getAttribute(final String name) {
+        return this.session.getAttribute(name);
+    }
+
+    @Override
+    public void setAttribute(final String name, final Object obj) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(this.id + " " + this.session + ": Set attribute " + name);
+        }
+        this.session.setAttribute(name, obj);
+    }
+
+    @Override
+    public Object removeAttribute(final String name) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug(this.id + " " + this.session + ": Remove attribute " + name);
+        }
+        return this.session.removeAttribute(name);
+    }
+
+    @Override
+    public String toString() {
+        return this.id + " " + this.session.toString();
+    }
+
+    class LoggingByteChannel implements ByteChannel {
+
+        @Override
+        public int read(final ByteBuffer dst) throws IOException {
+            final int bytesRead = session.channel().read(dst);
+            if (log.isDebugEnabled()) {
+                log.debug(id + " " + session + ": " + bytesRead + " bytes read");
+            }
+            if (bytesRead > 0 && wirelog.isEnabled()) {
+                final ByteBuffer b = dst.duplicate();
+                final int p = b.position();
+                b.limit(p);
+                b.position(p - bytesRead);
+                wirelog.input(b);
+            }
+            return bytesRead;
+        }
+
+        @Override
+        public int write(final ByteBuffer src) throws IOException {
+            final int byteWritten = session.channel().write(src);
+            if (log.isDebugEnabled()) {
+                log.debug(id + " " + session + ": " + byteWritten + " bytes written");
+            }
+            if (byteWritten > 0 && wirelog.isEnabled()) {
+                final ByteBuffer b = src.duplicate();
+                final int p = b.position();
+                b.limit(p);
+                b.position(p - byteWritten);
+                wirelog.output(b);
+            }
+            return byteWritten;
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (log.isDebugEnabled()) {
+                log.debug(id + " " + session + ": Channel close");
+            }
+            session.channel().close();
+        }
+
+        @Override
+        public boolean isOpen() {
+            return session.channel().isOpen();
+        }
+
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/ManagedNHttpClientConnectionFactory.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/ManagedNHttpClientConnectionFactory.java
new file mode 100644
index 0000000..83d0738
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/ManagedNHttpClientConnectionFactory.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.impl.nio.conn;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CodingErrorAction;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.config.ConnectionConfig;
+import org.apache.http.impl.nio.codecs.DefaultHttpRequestWriterFactory;
+import org.apache.http.impl.nio.codecs.DefaultHttpResponseParserFactory;
+import org.apache.http.nio.NHttpMessageParserFactory;
+import org.apache.http.nio.NHttpMessageWriterFactory;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.nio.conn.NHttpConnectionFactory;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.nio.util.ByteBufferAllocator;
+import org.apache.http.nio.util.HeapByteBufferAllocator;
+
+/**
+ * Default factory for {@link ManagedNHttpClientConnection} instances.
+ *
+ * @since 4.0
+ */
+public class ManagedNHttpClientConnectionFactory implements NHttpConnectionFactory<ManagedNHttpClientConnection> {
+
+    private final Log headerlog = LogFactory.getLog("org.apache.http.headers");
+    private final Log wirelog = LogFactory.getLog("org.apache.http.wire");
+    private final Log log = LogFactory.getLog(ManagedNHttpClientConnectionImpl.class);
+
+    private static final AtomicLong COUNTER = new AtomicLong();
+
+    public static final ManagedNHttpClientConnectionFactory INSTANCE = new ManagedNHttpClientConnectionFactory();
+
+    private final ByteBufferAllocator allocator;
+    private final NHttpMessageWriterFactory<HttpRequest> requestWriterFactory;
+    private final NHttpMessageParserFactory<HttpResponse> responseParserFactory;
+
+    public ManagedNHttpClientConnectionFactory(
+            final NHttpMessageWriterFactory<HttpRequest> requestWriterFactory,
+            final NHttpMessageParserFactory<HttpResponse> responseParserFactory,
+            final ByteBufferAllocator allocator) {
+        super();
+        this.requestWriterFactory = requestWriterFactory != null ? requestWriterFactory :
+            DefaultHttpRequestWriterFactory.INSTANCE;
+        this.responseParserFactory = responseParserFactory != null ? responseParserFactory :
+            DefaultHttpResponseParserFactory.INSTANCE;
+        this.allocator = allocator != null ? allocator : HeapByteBufferAllocator.INSTANCE;
+    }
+
+    public ManagedNHttpClientConnectionFactory() {
+        this(null, null, null);
+    }
+
+    @Override
+    public ManagedNHttpClientConnection create(
+            final IOSession iosession, final ConnectionConfig config) {
+        final String id = "http-outgoing-" + Long.toString(COUNTER.getAndIncrement());
+        CharsetDecoder chardecoder = null;
+        CharsetEncoder charencoder = null;
+        final Charset charset = config.getCharset();
+        final CodingErrorAction malformedInputAction = config.getMalformedInputAction() != null ?
+                config.getMalformedInputAction() : CodingErrorAction.REPORT;
+        final CodingErrorAction unmappableInputAction = config.getUnmappableInputAction() != null ?
+                config.getUnmappableInputAction() : CodingErrorAction.REPORT;
+        if (charset != null) {
+            chardecoder = charset.newDecoder();
+            chardecoder.onMalformedInput(malformedInputAction);
+            chardecoder.onUnmappableCharacter(unmappableInputAction);
+            charencoder = charset.newEncoder();
+            charencoder.onMalformedInput(malformedInputAction);
+            charencoder.onUnmappableCharacter(unmappableInputAction);
+        }
+        final ManagedNHttpClientConnection conn = new ManagedNHttpClientConnectionImpl(
+                id,
+                this.log,
+                this.headerlog,
+                this.wirelog,
+                iosession,
+                config.getBufferSize(),
+                config.getFragmentSizeHint(),
+                this.allocator,
+                chardecoder,
+                charencoder,
+                config.getMessageConstraints(),
+                null,
+                null,
+                this.requestWriterFactory,
+                this.responseParserFactory);
+        iosession.setAttribute(IOEventDispatch.CONNECTION_KEY, conn);
+        return conn;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/ManagedNHttpClientConnectionImpl.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/ManagedNHttpClientConnectionImpl.java
new file mode 100644
index 0000000..e52d9c1
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/ManagedNHttpClientConnectionImpl.java
@@ -0,0 +1,167 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.Header;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.config.MessageConstraints;
+import org.apache.http.entity.ContentLengthStrategy;
+import org.apache.http.impl.nio.DefaultNHttpClientConnection;
+import org.apache.http.nio.NHttpMessageParserFactory;
+import org.apache.http.nio.NHttpMessageWriterFactory;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.nio.reactor.ssl.SSLIOSession;
+import org.apache.http.nio.util.ByteBufferAllocator;
+import org.apache.http.util.Args;
+import org.apache.http.util.Asserts;
+
+class ManagedNHttpClientConnectionImpl
+                    extends DefaultNHttpClientConnection implements ManagedNHttpClientConnection {
+
+    private final Log headerlog;
+    private final Log wirelog;
+    private final Log log;
+
+    private final String id;
+    private IOSession original;
+
+    public ManagedNHttpClientConnectionImpl(
+            final String id,
+            final Log log,
+            final Log headerlog,
+            final Log wirelog,
+            final IOSession iosession,
+            final int buffersize,
+            final int fragmentSizeHint,
+            final ByteBufferAllocator allocator,
+            final CharsetDecoder chardecoder,
+            final CharsetEncoder charencoder,
+            final MessageConstraints constraints,
+            final ContentLengthStrategy incomingContentStrategy,
+            final ContentLengthStrategy outgoingContentStrategy,
+            final NHttpMessageWriterFactory<HttpRequest> requestWriterFactory,
+            final NHttpMessageParserFactory<HttpResponse> responseParserFactory) {
+        super(iosession, buffersize, fragmentSizeHint, allocator, chardecoder, charencoder, constraints,
+                incomingContentStrategy, outgoingContentStrategy,
+                requestWriterFactory, responseParserFactory);
+        this.id = id;
+        this.log = log;
+        this.headerlog = headerlog;
+        this.wirelog = wirelog;
+        this.original = iosession;
+        if (this.log.isDebugEnabled() || this.wirelog.isDebugEnabled()) {
+            super.bind(new LoggingIOSession(iosession, this.id, this.log, this.wirelog));
+        }
+    }
+
+    @Override
+    public void bind(final IOSession iosession) {
+        Args.notNull(iosession, "I/O session");
+        Asserts.check(!iosession.isClosed(), "I/O session is closed");
+        this.status = ACTIVE;
+        this.original = iosession;
+        if (this.log.isDebugEnabled() || this.wirelog.isDebugEnabled()) {
+            this.log.debug(this.id + " Upgrade session " + iosession);
+            super.bind(new LoggingIOSession(iosession, this.id, this.log, this.wirelog));
+        } else {
+            super.bind(iosession);
+        }
+    }
+
+    @Override
+    public IOSession getIOSession() {
+        return this.original;
+    }
+
+    @Override
+    public SSLSession getSSLSession() {
+        if (this.original instanceof SSLIOSession) {
+            return ((SSLIOSession) this.original).getSSLSession();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public String getId() {
+        return this.id;
+    }
+
+    @Override
+    protected void onResponseReceived(final HttpResponse response) {
+        if (response != null && this.headerlog.isDebugEnabled()) {
+            this.headerlog.debug(this.id + " << " + response.getStatusLine().toString());
+            final Header[] headers = response.getAllHeaders();
+            for (final Header header : headers) {
+                this.headerlog.debug(this.id + " << " + header.toString());
+            }
+        }
+    }
+
+    @Override
+    protected void onRequestSubmitted(final HttpRequest request) {
+        if (request != null && this.headerlog.isDebugEnabled()) {
+            this.headerlog.debug(this.id + " >> " + request.getRequestLine().toString());
+            final Header[] headers = request.getAllHeaders();
+            for (final Header header : headers) {
+                this.headerlog.debug(this.id + " >> " + header.toString());
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder buf = new StringBuilder();
+        buf.append(this.id);
+        buf.append(" [");
+        switch (this.status) {
+        case ACTIVE:
+            buf.append("ACTIVE");
+            if (this.inbuf.hasData()) {
+                buf.append("(").append(this.inbuf.length()).append(")");
+            }
+            break;
+        case CLOSING:
+            buf.append("CLOSING");
+            break;
+        case CLOSED:
+            buf.append("CLOSED");
+            break;
+        }
+        buf.append("]");
+        return buf.toString();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/PoolingNHttpClientConnectionManager.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/PoolingNHttpClientConnectionManager.java
new file mode 100644
index 0000000..bff892b
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/PoolingNHttpClientConnectionManager.java
@@ -0,0 +1,669 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+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.annotation.Contract;
+import org.apache.http.annotation.ThreadingBehavior;
+import org.apache.http.concurrent.BasicFuture;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.config.ConnectionConfig;
+import org.apache.http.config.Lookup;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.DnsResolver;
+import org.apache.http.conn.SchemePortResolver;
+import org.apache.http.conn.UnsupportedSchemeException;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.conn.DefaultSchemePortResolver;
+import org.apache.http.impl.conn.SystemDefaultDnsResolver;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.nio.conn.NHttpClientConnectionManager;
+import org.apache.http.nio.conn.NHttpConnectionFactory;
+import org.apache.http.nio.conn.NoopIOSessionStrategy;
+import org.apache.http.nio.conn.SchemeIOSessionStrategy;
+import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
+import org.apache.http.nio.pool.NIOConnFactory;
+import org.apache.http.nio.pool.SocketAddressResolver;
+import org.apache.http.nio.reactor.ConnectingIOReactor;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.pool.ConnPoolControl;
+import org.apache.http.pool.PoolStats;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.Args;
+import org.apache.http.util.Asserts;
+
+/**
+ * {@code PoolingNHttpClientConnectionManager} maintains a pool of
+ * {@link NHttpClientConnection}s 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>
+ * {@code PoolingNHttpClientConnectionManager} 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 {@link ConnPoolControl} methods.
+ *
+ * @since 4.0
+ */
+@Contract(threading = ThreadingBehavior.SAFE)
+public class PoolingNHttpClientConnectionManager
+       implements NHttpClientConnectionManager, ConnPoolControl<HttpRoute> {
+
+    private final Log log = LogFactory.getLog(getClass());
+
+    static final String IOSESSION_FACTORY_REGISTRY = "http.iosession-factory-registry";
+
+    private final ConnectingIOReactor ioreactor;
+    private final ConfigData configData;
+    private final CPool pool;
+    private final Registry<SchemeIOSessionStrategy> iosessionFactoryRegistry;
+
+    private static Registry<SchemeIOSessionStrategy> getDefaultRegistry() {
+        return RegistryBuilder.<SchemeIOSessionStrategy>create()
+                .register("http", NoopIOSessionStrategy.INSTANCE)
+                .register("https", SSLIOSessionStrategy.getDefaultStrategy())
+                .build();
+    }
+
+    public PoolingNHttpClientConnectionManager(final ConnectingIOReactor ioreactor) {
+        this(ioreactor, getDefaultRegistry());
+    }
+
+    public PoolingNHttpClientConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final Registry<SchemeIOSessionStrategy> iosessionFactoryRegistry) {
+        this(ioreactor, null, iosessionFactoryRegistry, (DnsResolver) null);
+    }
+
+    public PoolingNHttpClientConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory,
+            final DnsResolver dnsResolver) {
+        this(ioreactor, connFactory, getDefaultRegistry(), dnsResolver);
+    }
+
+    public PoolingNHttpClientConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory,
+            final SocketAddressResolver<HttpRoute> socketAddressResolver) {
+        this(ioreactor, connFactory, getDefaultRegistry(), socketAddressResolver);
+    }
+
+    public PoolingNHttpClientConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory) {
+        this(ioreactor, connFactory, getDefaultRegistry(), (DnsResolver) null);
+    }
+
+    public PoolingNHttpClientConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory,
+            final Registry<SchemeIOSessionStrategy> iosessionFactoryRegistry) {
+        this(ioreactor, connFactory, iosessionFactoryRegistry, (DnsResolver) null);
+    }
+
+    public PoolingNHttpClientConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory,
+            final Registry<SchemeIOSessionStrategy> iosessionFactoryRegistry,
+            final DnsResolver dnsResolver) {
+        this(ioreactor, connFactory, iosessionFactoryRegistry, null, dnsResolver,
+            -1, TimeUnit.MILLISECONDS);
+    }
+
+    public PoolingNHttpClientConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory,
+            final Registry<SchemeIOSessionStrategy> iosessionFactoryRegistry,
+            final SocketAddressResolver<HttpRoute> socketAddressResolver) {
+        this(ioreactor, connFactory, iosessionFactoryRegistry, socketAddressResolver,
+                -1, TimeUnit.MILLISECONDS);
+    }
+
+    public PoolingNHttpClientConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory,
+            final Registry<SchemeIOSessionStrategy> iosessionFactoryRegistry,
+            final SchemePortResolver schemePortResolver,
+            final DnsResolver dnsResolver,
+            final long timeToLive, final TimeUnit tunit) {
+        this(ioreactor, connFactory, iosessionFactoryRegistry,
+                new InternalAddressResolver(schemePortResolver, dnsResolver), timeToLive, tunit);
+    }
+
+    public PoolingNHttpClientConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory,
+            final Registry<SchemeIOSessionStrategy> iosessionFactoryRegistry,
+            final SocketAddressResolver<HttpRoute> socketAddressResolver,
+            final long timeToLive, final TimeUnit tunit) {
+        super();
+        Args.notNull(ioreactor, "I/O reactor");
+        Args.notNull(iosessionFactoryRegistry, "I/O session factory registry");
+        Args.notNull(socketAddressResolver, "Socket address resolver");
+        this.ioreactor = ioreactor;
+        this.configData = new ConfigData();
+        this.pool = new CPool(ioreactor,
+                new InternalConnectionFactory(this.configData, connFactory),
+                socketAddressResolver,
+                2, 20, timeToLive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
+        this.iosessionFactoryRegistry = iosessionFactoryRegistry;
+    }
+
+    PoolingNHttpClientConnectionManager(
+            final ConnectingIOReactor ioreactor,
+            final CPool pool,
+            final Registry<SchemeIOSessionStrategy> iosessionFactoryRegistry) {
+        super();
+        this.ioreactor = ioreactor;
+        this.configData = new ConfigData();
+        this.pool = pool;
+        this.iosessionFactoryRegistry = iosessionFactoryRegistry;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            shutdown();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public void execute(final IOEventDispatch eventDispatch) throws IOException {
+        this.ioreactor.execute(eventDispatch);
+    }
+
+    public void shutdown(final long waitMs) throws IOException {
+        this.log.debug("Connection manager is shutting down");
+        this.pool.shutdown(waitMs);
+        this.log.debug("Connection manager shut down");
+    }
+
+    @Override
+    public void shutdown() throws IOException {
+        this.log.debug("Connection manager is shutting down");
+        this.pool.shutdown(2000);
+        this.log.debug("Connection manager shut down");
+    }
+
+    private String format(final HttpRoute route, final Object state) {
+        final 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) {
+        final StringBuilder buf = new StringBuilder();
+        final PoolStats totals = this.pool.getTotalStats();
+        final 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 CPoolEntry entry) {
+        final StringBuilder buf = new StringBuilder();
+        buf.append("[id: ").append(entry.getId()).append("]");
+        buf.append("[route: ").append(entry.getRoute()).append("]");
+        final Object state = entry.getState();
+        if (state != null) {
+            buf.append("[state: ").append(state).append("]");
+        }
+        return buf.toString();
+    }
+
+    @Override
+    public Future<NHttpClientConnection> requestConnection(
+            final HttpRoute route,
+            final Object state,
+            final long connectTimeout,
+            final long leaseTimeout,
+            final TimeUnit tunit,
+            final FutureCallback<NHttpClientConnection> callback) {
+        Args.notNull(route, "HTTP route");
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Connection request: " + format(route, state) + formatStats(route));
+        }
+        final BasicFuture<NHttpClientConnection> resultFuture = new BasicFuture<NHttpClientConnection>(callback);
+        final HttpHost host;
+        if (route.getProxyHost() != null) {
+            host = route.getProxyHost();
+        } else {
+            host = route.getTargetHost();
+        }
+        final SchemeIOSessionStrategy sf = this.iosessionFactoryRegistry.lookup(
+                host.getSchemeName());
+        if (sf == null) {
+            resultFuture.failed(new UnsupportedSchemeException(host.getSchemeName() +
+                    " protocol is not supported"));
+            return resultFuture;
+        }
+        final Future<CPoolEntry> leaseFuture = this.pool.lease(route, state,
+                connectTimeout, leaseTimeout, tunit != null ? tunit : TimeUnit.MILLISECONDS,
+                new FutureCallback<CPoolEntry>() {
+
+                    @Override
+                    public void completed(final CPoolEntry entry) {
+                        Asserts.check(entry.getConnection() != null, "Pool entry with no connection");
+                        if (log.isDebugEnabled()) {
+                            log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute()));
+                        }
+                        final NHttpClientConnection managedConn = CPoolProxy.newProxy(entry);
+                        if (!resultFuture.completed(managedConn)) {
+                            pool.release(entry, true);
+                        }
+                    }
+
+                    @Override
+                    public void failed(final Exception ex) {
+                        if (log.isDebugEnabled()) {
+                            log.debug("Connection request failed", ex);
+                        }
+                        resultFuture.failed(ex);
+                    }
+
+                    @Override
+                    public void cancelled() {
+                        log.debug("Connection request cancelled");
+                        resultFuture.cancel(true);
+                    }
+
+                });
+        return new Future<NHttpClientConnection>() {
+
+            @Override
+            public boolean cancel(final boolean mayInterruptIfRunning) {
+                try {
+                    leaseFuture.cancel(mayInterruptIfRunning);
+                } finally {
+                    return resultFuture.cancel(mayInterruptIfRunning);
+                }
+            }
+
+            @Override
+            public boolean isCancelled() {
+                return resultFuture.isCancelled();
+            }
+
+            @Override
+            public boolean isDone() {
+                return resultFuture.isDone();
+            }
+
+            @Override
+            public NHttpClientConnection get() throws InterruptedException, ExecutionException {
+                return resultFuture.get();
+            }
+
+            @Override
+            public NHttpClientConnection get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+                return resultFuture.get(timeout, unit);
+            }
+
+        };
+    }
+
+    @Override
+    public void releaseConnection(
+            final NHttpClientConnection managedConn,
+            final Object state,
+            final long keepalive,
+            final TimeUnit tunit) {
+        Args.notNull(managedConn, "Managed connection");
+        synchronized (managedConn) {
+            final CPoolEntry entry = CPoolProxy.detach(managedConn);
+            if (entry == null) {
+                return;
+            }
+            if (this.log.isDebugEnabled()) {
+                this.log.debug("Releasing connection: " + format(entry) + formatStats(entry.getRoute()));
+            }
+            final NHttpClientConnection conn = entry.getConnection();
+            try {
+                if (conn.isOpen()) {
+                    entry.setState(state);
+                    entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
+                    if (this.log.isDebugEnabled()) {
+                        final String s;
+                        if (keepalive > 0) {
+                            s = "for " + (double) keepalive / 1000 + " seconds";
+                        } else {
+                            s = "indefinitely";
+                        }
+                        this.log.debug("Connection " + format(entry) + " can be kept alive " + s);
+                    }
+                }
+            } finally {
+                this.pool.release(entry, conn.isOpen() && entry.isRouteComplete());
+                if (this.log.isDebugEnabled()) {
+                    this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute()));
+                }
+            }
+        }
+    }
+
+    private Lookup<SchemeIOSessionStrategy> getIOSessionFactoryRegistry(final HttpContext context) {
+        @SuppressWarnings("unchecked")
+        Lookup<SchemeIOSessionStrategy> reg = (Lookup<SchemeIOSessionStrategy>) context.getAttribute(
+                IOSESSION_FACTORY_REGISTRY);
+        if (reg == null) {
+            reg = this.iosessionFactoryRegistry;
+        }
+        return reg;
+    }
+
+    @Override
+    public void startRoute(
+            final NHttpClientConnection managedConn,
+            final HttpRoute route,
+            final HttpContext context) throws IOException {
+        Args.notNull(managedConn, "Managed connection");
+        Args.notNull(route, "HTTP route");
+        final HttpHost host;
+        if (route.getProxyHost() != null) {
+            host = route.getProxyHost();
+        } else {
+            host = route.getTargetHost();
+        }
+        final Lookup<SchemeIOSessionStrategy> reg = getIOSessionFactoryRegistry(context);
+        final SchemeIOSessionStrategy sf = reg.lookup(host.getSchemeName());
+        if (sf == null) {
+            throw new UnsupportedSchemeException(host.getSchemeName() +
+                    " protocol is not supported");
+        }
+        if (sf.isLayeringRequired()) {
+            synchronized (managedConn) {
+                final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn);
+                final ManagedNHttpClientConnection conn = entry.getConnection();
+                final IOSession ioSession = conn.getIOSession();
+                final IOSession currentSession = sf.upgrade(host, ioSession);
+                conn.bind(currentSession);
+            }
+        }
+    }
+
+    @Override
+    public void upgrade(
+            final NHttpClientConnection managedConn,
+            final HttpRoute route,
+            final HttpContext context) throws IOException {
+        Args.notNull(managedConn, "Managed connection");
+        Args.notNull(route, "HTTP route");
+        final HttpHost host  = route.getTargetHost();
+        final Lookup<SchemeIOSessionStrategy> reg = getIOSessionFactoryRegistry(context);
+        final SchemeIOSessionStrategy sf = reg.lookup(host.getSchemeName());
+        if (sf == null) {
+            throw new UnsupportedSchemeException(host.getSchemeName() +
+                    " protocol is not supported");
+        }
+        if (!sf.isLayeringRequired()) {
+            throw new UnsupportedSchemeException(host.getSchemeName() +
+                    " protocol does not support connection upgrade");
+        }
+        synchronized (managedConn) {
+            final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn);
+            final ManagedNHttpClientConnection conn = entry.getConnection();
+            final IOSession currentSession = sf.upgrade(host, conn.getIOSession());
+            conn.bind(currentSession);
+        }
+    }
+
+    @Override
+    public void routeComplete(
+            final NHttpClientConnection managedConn,
+            final HttpRoute route,
+            final HttpContext context) {
+        Args.notNull(managedConn, "Managed connection");
+        Args.notNull(route, "HTTP route");
+        synchronized (managedConn) {
+            final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn);
+            entry.markRouteComplete();
+        }
+    }
+
+    @Override
+    public boolean isRouteComplete(
+            final NHttpClientConnection managedConn) {
+        Args.notNull(managedConn, "Managed connection");
+        synchronized (managedConn) {
+            final CPoolEntry entry = CPoolProxy.getPoolEntry(managedConn);
+            return entry.isRouteComplete();
+        }
+    }
+
+    @Override
+    public void closeIdleConnections(final long idleTimeout, final TimeUnit tunit) {
+        if (this.log.isDebugEnabled()) {
+            this.log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit);
+        }
+        this.pool.closeIdle(idleTimeout, tunit);
+    }
+
+    @Override
+    public void closeExpiredConnections() {
+        log.debug("Closing expired connections");
+        this.pool.closeExpired();
+    }
+
+    public void validatePendingRequests() {
+        log.debug("Validating pending requests");
+        this.pool.validatePendingRequests();
+    }
+
+    @Override
+    public int getMaxTotal() {
+        return this.pool.getMaxTotal();
+    }
+
+    @Override
+    public void setMaxTotal(final int max) {
+        this.pool.setMaxTotal(max);
+    }
+
+    @Override
+    public int getDefaultMaxPerRoute() {
+        return this.pool.getDefaultMaxPerRoute();
+    }
+
+    @Override
+    public void setDefaultMaxPerRoute(final int max) {
+        this.pool.setDefaultMaxPerRoute(max);
+    }
+
+    @Override
+    public int getMaxPerRoute(final HttpRoute route) {
+        return this.pool.getMaxPerRoute(route);
+    }
+
+    @Override
+    public void setMaxPerRoute(final HttpRoute route, final int max) {
+        this.pool.setMaxPerRoute(route, max);
+    }
+
+    @Override
+    public PoolStats getTotalStats() {
+        return this.pool.getTotalStats();
+    }
+
+    @Override
+    public PoolStats getStats(final HttpRoute route) {
+        return this.pool.getStats(route);
+    }
+
+    /**
+     * @since 4.1
+     */
+    public Set<HttpRoute> getRoutes() {
+        return this.pool.getRoutes();
+    }
+
+    public ConnectionConfig getDefaultConnectionConfig() {
+        return this.configData.getDefaultConnectionConfig();
+    }
+
+    public void setDefaultConnectionConfig(final ConnectionConfig defaultConnectionConfig) {
+        this.configData.setDefaultConnectionConfig(defaultConnectionConfig);
+    }
+
+    public ConnectionConfig getConnectionConfig(final HttpHost host) {
+        return this.configData.getConnectionConfig(host);
+    }
+
+    public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) {
+        this.configData.setConnectionConfig(host, connectionConfig);
+    }
+
+    static class ConfigData {
+
+        private final Map<HttpHost, ConnectionConfig> connectionConfigMap;
+        private volatile ConnectionConfig defaultConnectionConfig;
+
+        ConfigData() {
+            super();
+            this.connectionConfigMap = new ConcurrentHashMap<HttpHost, ConnectionConfig>();
+        }
+
+        public ConnectionConfig getDefaultConnectionConfig() {
+            return this.defaultConnectionConfig;
+        }
+
+        public void setDefaultConnectionConfig(final ConnectionConfig defaultConnectionConfig) {
+            this.defaultConnectionConfig = defaultConnectionConfig;
+        }
+
+        public ConnectionConfig getConnectionConfig(final HttpHost host) {
+            return this.connectionConfigMap.get(host);
+        }
+
+        public void setConnectionConfig(final HttpHost host, final ConnectionConfig connectionConfig) {
+            this.connectionConfigMap.put(host, connectionConfig);
+        }
+
+    }
+
+    static class InternalConnectionFactory implements NIOConnFactory<HttpRoute, ManagedNHttpClientConnection> {
+
+        private final ConfigData configData;
+        private final NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory;
+
+        InternalConnectionFactory(
+                final ConfigData configData,
+                final NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory) {
+            super();
+            this.configData = configData != null ? configData : new ConfigData();
+            this.connFactory = connFactory != null ? connFactory :
+                ManagedNHttpClientConnectionFactory.INSTANCE;
+        }
+
+        @Override
+        public ManagedNHttpClientConnection create(
+                final HttpRoute route, final IOSession iosession) throws IOException {
+            ConnectionConfig config = null;
+            if (route.getProxyHost() != null) {
+                config = this.configData.getConnectionConfig(route.getProxyHost());
+            }
+            if (config == null) {
+                config = this.configData.getConnectionConfig(route.getTargetHost());
+            }
+            if (config == null) {
+                config = this.configData.getDefaultConnectionConfig();
+            }
+            if (config == null) {
+                config = ConnectionConfig.DEFAULT;
+            }
+            final ManagedNHttpClientConnection conn = this.connFactory.create(iosession, config);
+            iosession.setAttribute(IOEventDispatch.CONNECTION_KEY, conn);
+            return conn;
+        }
+
+    }
+
+    static class InternalAddressResolver implements SocketAddressResolver<HttpRoute> {
+
+        private final SchemePortResolver schemePortResolver;
+        private final DnsResolver dnsResolver;
+
+        public InternalAddressResolver(
+                final SchemePortResolver schemePortResolver,
+                final DnsResolver dnsResolver) {
+            super();
+            this.schemePortResolver = schemePortResolver != null ? schemePortResolver :
+                DefaultSchemePortResolver.INSTANCE;
+            this.dnsResolver = dnsResolver != null ? dnsResolver :
+                    SystemDefaultDnsResolver.INSTANCE;
+        }
+
+        @Override
+        public SocketAddress resolveLocalAddress(final HttpRoute route) throws IOException {
+            return route.getLocalAddress() != null ? new InetSocketAddress(route.getLocalAddress(), 0) : null;
+        }
+
+        @Override
+        public SocketAddress resolveRemoteAddress(final HttpRoute route) throws IOException {
+            final HttpHost host;
+            if (route.getProxyHost() != null) {
+                host = route.getProxyHost();
+            } else {
+                host = route.getTargetHost();
+            }
+            final int port = this.schemePortResolver.resolve(host);
+            final InetAddress[] addresses = this.dnsResolver.resolve(host.getHostName());
+            return new InetSocketAddress(addresses[0], port);
+        }
+
+    }
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/Wire.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/Wire.java
new file mode 100644
index 0000000..8fe8437
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/Wire.java
@@ -0,0 +1,121 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.nio.ByteBuffer;
+
+import org.apache.commons.logging.Log;
+
+class Wire {
+
+    private final Log log;
+    private final String id;
+
+    public Wire(final Log log, final String id) {
+        super();
+        this.log = log;
+        this.id = id;
+    }
+
+    private void wire(final String header, final byte[] b, final int pos, final int off) {
+        final StringBuilder buffer = new StringBuilder();
+        for (int i = 0; i < off; i++) {
+            final int ch = b[pos + i];
+            if (ch == 13) {
+                buffer.append("[\\r]");
+            } else if (ch == 10) {
+                    buffer.append("[\\n]\"");
+                    buffer.insert(0, "\"");
+                    buffer.insert(0, header);
+                    this.log.debug(this.id + " " + buffer.toString());
+                    buffer.setLength(0);
+            } else if ((ch < 32) || (ch > 127)) {
+                buffer.append("[0x");
+                buffer.append(Integer.toHexString(ch));
+                buffer.append("]");
+            } else {
+                buffer.append((char) ch);
+            }
+        }
+        if (buffer.length() > 0) {
+            buffer.append('\"');
+            buffer.insert(0, '\"');
+            buffer.insert(0, header);
+            this.log.debug(this.id + " " + buffer.toString());
+        }
+    }
+
+
+    public boolean isEnabled() {
+        return this.log.isDebugEnabled();
+    }
+
+    public void output(final byte[] b, final int pos, final int off) {
+        wire(">> ", b, pos, off);
+    }
+
+    public void input(final byte[] b, final int pos, final int off) {
+        wire("<< ", b, pos, off);
+    }
+
+    public void output(final byte[] b) {
+        output(b, 0, b.length);
+    }
+
+    public void input(final byte[] b) {
+        input(b, 0, b.length);
+    }
+
+    public void output(final int b) {
+        output(new byte[] {(byte) b});
+    }
+
+    public void input(final int b) {
+        input(new byte[] {(byte) b});
+    }
+
+    public void output(final ByteBuffer b) {
+        if (b.hasArray()) {
+            output(b.array(), b.arrayOffset() + b.position(), b.remaining());
+        } else {
+            final byte[] tmp = new byte[b.remaining()];
+            b.get(tmp);
+            output(tmp);
+        }
+    }
+
+    public void input(final ByteBuffer b) {
+        if (b.hasArray()) {
+            input(b.array(), b.arrayOffset() + b.position(), b.remaining());
+        } else {
+            final byte[] tmp = new byte[b.remaining()];
+            b.get(tmp);
+            input(tmp);
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/package-info.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/package-info.java
new file mode 100644
index 0000000..a5c262d
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/impl/nio/conn/package-info.java
@@ -0,0 +1,32 @@
+/*
+ * ====================================================================
+ * 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/>.
+ *
+ */
+
+/**
+ * Default implementations of asynchronous client connection
+ * management functions.
+ */
+package org.apache.http.impl.nio.conn;
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/HttpAsyncClient.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/HttpAsyncClient.java
new file mode 100644
index 0000000..fd84c4f
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/HttpAsyncClient.java
@@ -0,0 +1,157 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * This interface represents only the most basic contract for HTTP request
+ * execution. It imposes no restrictions or particular details on the request
+ * execution process and leaves the specifics of state management,
+ * authentication and redirect handling up to individual implementations.
+ *
+ * @since 4.0
+ */
+public interface HttpAsyncClient {
+
+    /**
+     * Initiates asynchronous HTTP request execution using the given context.
+     * <p>
+     * The request producer passed to this method will be used to generate
+     * a request message and stream out its content without buffering it
+     * in memory. The response consumer passed to this method will be used
+     * to process a response message without buffering its content in memory.
+     * <p>
+     * Please note it may be unsafe to interact with the context instance
+     * while the request is still being executed.
+     *
+     * @param <T> the result type of request execution.
+     * @param requestProducer request producer callback.
+     * @param responseConsumer response consumer callaback.
+     * @param context HTTP context
+     * @param callback future callback.
+     * @return future representing pending completion of the operation.
+     */
+    <T> Future<T> execute(
+            HttpAsyncRequestProducer requestProducer,
+            HttpAsyncResponseConsumer<T> responseConsumer,
+            HttpContext context,
+            FutureCallback<T> callback);
+
+    /**
+     * Initiates asynchronous HTTP request execution using the default
+     * context.
+     * <p>
+     * The request producer passed to this method will be used to generate
+     * a request message and stream out its content without buffering it
+     * in memory. The response consumer passed to this method will be used
+     * to process a response message without buffering its content in memory.
+     *
+     * @param <T> the result type of request execution.
+     * @param requestProducer request producer callback.
+     * @param responseConsumer response consumer callaback.
+     * @param callback future callback.
+     * @return future representing pending completion of the operation.
+     */
+    <T> Future<T> execute(
+            HttpAsyncRequestProducer requestProducer,
+            HttpAsyncResponseConsumer<T> responseConsumer,
+            FutureCallback<T> callback);
+
+    /**
+     * Initiates asynchronous HTTP request execution against the given target
+     * using the given context.
+     * <p>
+     * Please note it may be unsafe to interact with the context instance
+     * while the request is still being executed.
+     *
+     * @param target    the target host for the request.
+     *                  Implementations may accept {@code null}
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param request   the request to execute
+     * @param context   the context to use for the execution, or
+     *                  {@code null} to use the default context
+     * @param callback future callback.
+     * @return future representing pending completion of the operation.
+     */
+    Future<HttpResponse> execute(
+            HttpHost target, HttpRequest request, HttpContext context,
+            FutureCallback<HttpResponse> callback);
+
+    /**
+     * Initiates asynchronous HTTP request execution against the given target.
+     *
+     * @param target    the target host for the request.
+     *                  Implementations may accept {@code null}
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param request   the request to execute
+     * @param callback future callback.
+     * @return future representing pending completion of the operation.
+     */
+    Future<HttpResponse> execute(
+            HttpHost target, HttpRequest request,
+            FutureCallback<HttpResponse> callback);
+
+    /**
+     * Initiates asynchronous HTTP request execution using the given
+     * context.
+     * <p>
+     * Please note it may be unsafe to interact with the context instance
+     * while the request is still being executed.
+     *
+     * @param request   the request to execute
+     * @param context HTTP context
+     * @param callback future callback.
+     * @return future representing pending completion of the operation.
+     */
+    Future<HttpResponse> execute(
+            HttpUriRequest request, HttpContext context,
+            FutureCallback<HttpResponse> callback);
+
+    /**
+     * Initiates asynchronous HTTP request execution.
+     *
+     * @param request   the request to execute
+     * @param callback future callback.
+     * @return future representing pending completion of the operation.
+     */
+    Future<HttpResponse> execute(
+            HttpUriRequest request,
+            FutureCallback<HttpResponse> callback);
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/HttpPipeliningClient.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/HttpPipeliningClient.java
new file mode 100644
index 0000000..69382f5
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/HttpPipeliningClient.java
@@ -0,0 +1,137 @@
+/*
+ * ====================================================================
+ * 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.nio.client;
+
+import java.util.List;
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * This interface represents only the most basic contract for HTTP request
+ * execution. It imposes no restrictions or particular details on the request
+ * execution process and leaves the specifics of state management,
+ * authentication and redirect handling up to individual implementations.
+ *
+ * @since 4.1
+ */
+public interface HttpPipeliningClient extends HttpAsyncClient {
+
+    /**
+     * Initiates pipelined execution of a sequence of requests.
+     * <p>
+     * The request producers passed to this method will be used to generate
+     * a request message and stream out its content without buffering it
+     * in memory. The response consumers passed to this method will be used
+     * to process a response message without buffering its content in memory.
+     * <p>
+     * Please note it may be unsafe to interact with the context instance
+     * while the request is still being executed.
+     *
+     * @param <T> the result type of request execution.
+     * @param target    the target host for the request.
+     * @param requestProducers list of request producers.
+     * @param responseConsumers list of response consumers.
+     * @param context HTTP context
+     * @param callback future callback.
+     * @return future representing pending completion of the operation.
+     */
+    <T> Future<List<T>> execute(
+            HttpHost target,
+            List<? extends HttpAsyncRequestProducer> requestProducers,
+            List<? extends HttpAsyncResponseConsumer<T>> responseConsumers,
+            HttpContext context,
+            FutureCallback<List<T>> callback);
+
+    /**
+     * Initiates pipelined execution of a sequence of requests.
+     * <p>
+     * The request producers passed to this method will be used to generate
+     * a request message and stream out its content without buffering it
+     * in memory. The response consumers passed to this method will be used
+     * to process a response message without buffering its content in memory.
+     *
+     * @param <T> the result type of request execution.
+     * @param target    the target host for the request.
+     * @param requestProducers list of request producers.
+     * @param responseConsumers list of response consumers.
+     * @param callback future callback.
+     * @return future representing pending completion of the operation.
+     */
+    <T> Future<List<T>> execute(
+            HttpHost target,
+            List<? extends HttpAsyncRequestProducer> requestProducers,
+            List<? extends HttpAsyncResponseConsumer<T>> responseConsumers,
+            FutureCallback<List<T>> callback);
+
+    /**
+     * Initiates pipelined execution of a sequence of requests against
+     * the given target using the given context.
+     * <p>
+     * Please note it may be unsafe to interact with the context instance
+     * while the request is still being executed.
+     *
+     * @param target    the target host for the requests.
+     *                  Implementations may accept {@code null}
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param requests  the requests to execute
+     * @param context   the context to use for the execution, or
+     *                  {@code null} to use the default context
+     * @param callback future callback.
+     * @return future representing pending completion of the operation.
+     */
+    Future<List<HttpResponse>> execute(
+            HttpHost target,
+            List<HttpRequest> requests,
+            HttpContext context,
+            FutureCallback<List<HttpResponse>> callback);
+
+    /**
+     * Initiates pipelined execution of a sequence of requests against
+     * the given target.
+     *
+     * @param target    the target host for the requests.
+     *                  Implementations may accept {@code null}
+     *                  if they can still determine a route, for example
+     *                  to a default target or by inspecting the request.
+     * @param requests  the requests to execute
+     * @param callback future callback.
+     * @return future representing pending completion of the operation.
+     */
+    Future<List<HttpResponse>> execute(
+            HttpHost target,
+            List<HttpRequest> requests,
+            FutureCallback<List<HttpResponse>> callback);
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/AsyncByteConsumer.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/AsyncByteConsumer.java
new file mode 100644
index 0000000..d62cdbe
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/AsyncByteConsumer.java
@@ -0,0 +1,93 @@
+/*
+ * ====================================================================
+ * 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.nio.client.methods;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.protocol.AbstractAsyncResponseConsumer;
+import org.apache.http.util.Asserts;
+
+/**
+ * {@link org.apache.http.nio.protocol.HttpAsyncResponseConsumer} implementation that
+ * provides convenience methods for processing of binary content entities enclosed
+ * in an HTTP response.
+ *
+ * @since 4.0
+ */
+public abstract class AsyncByteConsumer<T> extends AbstractAsyncResponseConsumer<T> {
+
+    private final ByteBuffer bbuf;
+
+    public AsyncByteConsumer(final int bufSize) {
+        super();
+        this.bbuf = ByteBuffer.allocate(bufSize);
+    }
+
+    public AsyncByteConsumer() {
+        this(8 * 1024);
+    }
+
+    /**
+     * Invoked to process a {@link ByteBuffer chunk} of content.
+     * The {@link IOControl} interface can be used to suspend input events
+     * if the consumer is temporarily unable to consume more content.
+     *
+     * @param buf chunk of content.
+     * @param ioctrl I/O control of the underlying connection.
+     * @throws IOException in case of an I/O error
+     */
+    protected abstract void onByteReceived(
+            ByteBuffer buf, IOControl ioctrl) throws IOException;
+
+    @Override
+    protected final void onEntityEnclosed(
+            final HttpEntity entity, final ContentType contentType) throws IOException {
+    }
+
+    @Override
+    protected final void onContentReceived(
+            final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
+        Asserts.notNull(this.bbuf, "Byte buffer");
+        final int bytesRead = decoder.read(this.bbuf);
+        if (bytesRead <= 0) {
+            return;
+        }
+        this.bbuf.flip();
+        onByteReceived(this.bbuf, ioctrl);
+        this.bbuf.clear();
+    }
+
+    @Override
+    protected void releaseResources() {
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/AsyncCharConsumer.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/AsyncCharConsumer.java
new file mode 100644
index 0000000..fa1660a
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/AsyncCharConsumer.java
@@ -0,0 +1,140 @@
+/*
+ * ====================================================================
+ * 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.nio.client.methods;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.protocol.AbstractAsyncResponseConsumer;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.util.Asserts;
+
+/**
+ * {@link org.apache.http.nio.protocol.HttpAsyncResponseConsumer} implementation that
+ * provides convenience methods for processing of textual content entities enclosed
+ * in an HTTP response.
+ *
+ * @since 4.0
+ */
+public abstract class AsyncCharConsumer<T> extends AbstractAsyncResponseConsumer<T> {
+
+    private final ByteBuffer bbuf;
+    private final CharBuffer cbuf;
+
+    private CharsetDecoder chardecoder;
+
+    public AsyncCharConsumer(final int bufSize) {
+        super();
+        this.bbuf = ByteBuffer.allocate(bufSize);
+        this.cbuf = CharBuffer.allocate(bufSize);
+    }
+
+    public AsyncCharConsumer() {
+        this(8 * 1024);
+    }
+
+    /**
+     * Invoked to process a {@link CharBuffer chunk} of content.
+     * The {@link IOControl} interface can be used to suspend input events
+     * if the consumer is temporarily unable to consume more content.
+     *
+     * @param buf chunk of content.
+     * @param ioctrl I/O control of the underlying connection.
+     * @throws IOException in case of an I/O error
+     */
+    protected abstract void onCharReceived(
+            CharBuffer buf, IOControl ioctrl) throws IOException;
+
+    /**
+     * Invoked to create a @{link CharsetDecoder} for contentType.
+     * This allows to use different default charsets for different content
+     * types and set appropriate coding error actions.
+     *
+     * @param contentType response Content-Type or null if not specified.
+     * @return content decoder.
+     *
+     * @since 4.1
+     */
+    protected CharsetDecoder createDecoder(final ContentType contentType) {
+        Charset charset = contentType != null ? contentType.getCharset() : null;
+        if (charset == null) {
+            charset = HTTP.DEF_CONTENT_CHARSET;
+        }
+        return charset.newDecoder();
+    }
+
+    @Override
+    protected final void onEntityEnclosed(
+            final HttpEntity entity, final ContentType contentType) throws IOException {
+        this.chardecoder = createDecoder(contentType != null ? contentType : ContentType.DEFAULT_TEXT);
+    }
+
+    @Override
+    protected final void onContentReceived(
+            final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
+        Asserts.notNull(this.bbuf, "Byte buffer");
+
+        final int bytesRead = decoder.read(this.bbuf);
+        if (bytesRead <= 0) {
+            return;
+        }
+        this.bbuf.flip();
+        final boolean completed = decoder.isCompleted();
+        CoderResult result = this.chardecoder.decode(this.bbuf, this.cbuf, completed);
+        handleDecodingResult(result, ioctrl);
+        this.bbuf.compact();
+        if (completed) {
+            result = this.chardecoder.flush(this.cbuf);
+            handleDecodingResult(result, ioctrl);
+        }
+    }
+
+    private void handleDecodingResult(
+            final CoderResult result, final IOControl ioctrl) throws IOException {
+        if (result.isError()) {
+            result.throwException();
+        }
+        this.cbuf.flip();
+        if (this.cbuf.hasRemaining()) {
+            onCharReceived(this.cbuf, ioctrl);
+        }
+        this.cbuf.clear();
+    }
+
+    @Override
+    protected void releaseResources() {
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/BaseZeroCopyRequestProducer.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/BaseZeroCopyRequestProducer.java
new file mode 100644
index 0000000..ae813b7
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/BaseZeroCopyRequestProducer.java
@@ -0,0 +1,150 @@
+/*
+ * ====================================================================
+ * 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.nio.client.methods;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.URI;
+import java.nio.channels.FileChannel;
+
+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.client.utils.URIUtils;
+import org.apache.http.entity.BasicHttpEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.ContentEncoderChannel;
+import org.apache.http.nio.FileContentEncoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.Args;
+
+abstract class BaseZeroCopyRequestProducer implements HttpAsyncRequestProducer {
+
+    private final URI requestURI;
+    private final File file;
+    private final RandomAccessFile accessfile;
+    private final ContentType contentType;
+
+    private FileChannel fileChannel;
+    private long idx = -1;
+
+    protected BaseZeroCopyRequestProducer(
+            final URI requestURI,
+            final File file, final ContentType contentType) throws FileNotFoundException {
+        super();
+        Args.notNull(requestURI, "Request URI");
+        Args.notNull(file, "Source file");
+        this.requestURI = requestURI;
+        this.file = file;
+        this.accessfile = new RandomAccessFile(file, "r");
+        this.contentType = contentType;
+    }
+
+    private void closeChannel() throws IOException {
+        if (this.fileChannel != null) {
+            this.fileChannel.close();
+            this.fileChannel = null;
+        }
+    }
+
+    protected abstract HttpEntityEnclosingRequest createRequest(final URI requestURI, final HttpEntity entity);
+
+    @Override
+    public HttpRequest generateRequest() throws IOException, HttpException {
+        final BasicHttpEntity entity = new BasicHttpEntity();
+        entity.setChunked(false);
+        entity.setContentLength(this.file.length());
+        if (this.contentType != null) {
+            entity.setContentType(this.contentType.toString());
+        }
+        return createRequest(this.requestURI, entity);
+    }
+
+    @Override
+    public synchronized HttpHost getTarget() {
+        return URIUtils.extractHost(this.requestURI);
+    }
+
+    @Override
+    public synchronized void produceContent(
+            final ContentEncoder encoder, final IOControl ioctrl) throws IOException {
+        if (this.fileChannel == null) {
+            this.fileChannel = this.accessfile.getChannel();
+            this.idx = 0;
+        }
+        final long transferred;
+        if (encoder instanceof FileContentEncoder) {
+            transferred = ((FileContentEncoder)encoder).transfer(
+                    this.fileChannel, this.idx, Integer.MAX_VALUE);
+        } else {
+            transferred = this.fileChannel.transferTo(
+                    this.idx, Integer.MAX_VALUE, new ContentEncoderChannel(encoder));
+        }
+        if (transferred > 0) {
+            this.idx += transferred;
+        }
+
+        if (this.idx >= this.fileChannel.size()) {
+            encoder.complete();
+            closeChannel();
+        }
+    }
+
+    @Override
+    public void requestCompleted(final HttpContext context) {
+    }
+
+    @Override
+    public void failed(final Exception ex) {
+    }
+
+    @Override
+    public boolean isRepeatable() {
+        return true;
+    }
+
+    @Override
+    public synchronized void resetRequest() throws IOException {
+        closeChannel();
+    }
+
+    @Override
+    public synchronized void close() throws IOException {
+        try {
+            this.accessfile.close();
+        } catch (final IOException ignore) {
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/HttpAsyncMethods.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/HttpAsyncMethods.java
new file mode 100644
index 0000000..772ba3c
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/HttpAsyncMethods.java
@@ -0,0 +1,428 @@
+/*
+ * ====================================================================
+ * 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.nio.client.methods;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+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.HttpTrace;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.entity.ContentType;
+import org.apache.http.nio.entity.HttpAsyncContentProducer;
+import org.apache.http.nio.entity.NByteArrayEntity;
+import org.apache.http.nio.entity.NStringEntity;
+import org.apache.http.nio.protocol.BasicAsyncRequestProducer;
+import org.apache.http.nio.protocol.BasicAsyncResponseConsumer;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.util.Args;
+
+/**
+ * Factory methods for asynchronous request producers and response consumers.
+ *
+ * @since 4.0
+ */
+public final class HttpAsyncMethods {
+
+    /**
+     * Creates asynchronous request generator for the given request message.
+     *
+     * @param target request target.
+     * @param request request message.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer create(final HttpHost target, final HttpRequest request) {
+        Args.notNull(target, "HTTP host");
+        Args.notNull(request, "HTTP request");
+        return new RequestProducerImpl(target, request);
+    }
+
+    /**
+     * Creates asynchronous request generator for the given request message.
+     *
+     * @param request request message.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer create(final HttpUriRequest request) {
+        Args.notNull(request, "HTTP request");
+        final HttpHost target = URIUtils.extractHost(request.getURI());
+        return new RequestProducerImpl(target, request);
+    }
+
+    /**
+     * Creates asynchronous {@code GET} request generator.
+     *
+     * @param requestURI request URI.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createGet(final URI requestURI) {
+        return create(new HttpGet(requestURI));
+    }
+
+    /**
+     * Creates asynchronous {@code GET} request generator.
+     *
+     * @param requestURI request URI.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createGet(final String requestURI) {
+        return create(new HttpGet(URI.create(requestURI)));
+    }
+
+    /**
+     * Creates asynchronous {@code HEAD} request generator.
+     *
+     * @param requestURI request URI.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createHead(final URI requestURI) {
+        return create(new HttpHead(requestURI));
+    }
+
+    /**
+     * Creates asynchronous {@code HEAD} request generator.
+     *
+     * @param requestURI request URI.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createHead(final String requestURI) {
+        return create(new HttpHead(URI.create(requestURI)));
+    }
+
+    /**
+     * Creates asynchronous {@code DELETE} request generator.
+     *
+     * @param requestURI request URI.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createDelete(final URI requestURI) {
+        return create(new HttpDelete(requestURI));
+    }
+
+    /**
+     * Creates asynchronous {@code DELETE} request generator.
+     *
+     * @param requestURI request URI.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createDelete(final String requestURI) {
+        return create(new HttpDelete(URI.create(requestURI)));
+    }
+
+    /**
+     * Creates asynchronous {@code OPTIONS} request generator.
+     *
+     * @param requestURI request URI.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createOptions(final URI requestURI) {
+        return create(new HttpOptions(requestURI));
+    }
+
+    /**
+     * Creates asynchronous {@code OPTIONS} request generator.
+     *
+     * @param requestURI request URI.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createOptions(final String requestURI) {
+        return create(new HttpOptions(URI.create(requestURI)));
+    }
+
+    /**
+     * Creates asynchronous {@code TRACE} request generator.
+     *
+     * @param requestURI request URI.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createTrace(final URI requestURI) {
+        return create(new HttpTrace(requestURI));
+    }
+
+    /**
+     * Creates asynchronous {@code TRACE} request generator.
+     *
+     * @param requestURI request URI.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createTrace(final String requestURI) {
+        return create(new HttpTrace(URI.create(requestURI)));
+    }
+
+    /**
+     * Creates asynchronous {@code POST} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createPost(
+            final URI requestURI,
+            final String content,
+            final ContentType contentType) throws UnsupportedEncodingException {
+        final HttpPost httppost = new HttpPost(requestURI);
+        final NStringEntity entity = new NStringEntity(content, contentType);
+        httppost.setEntity(entity);
+        final HttpHost target = URIUtils.extractHost(requestURI);
+        return new RequestProducerImpl(target, httppost, entity);
+    }
+
+    /**
+     * Creates asynchronous {@code POST} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createPost(
+            final String requestURI,
+            final String content,
+            final ContentType contentType) throws UnsupportedEncodingException {
+        return createPost(URI.create(requestURI), content, contentType);
+    }
+
+    /**
+     * Creates asynchronous {@code POST} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createPost(
+            final URI requestURI,
+            final byte[] content,
+            final ContentType contentType) {
+        final HttpPost httppost = new HttpPost(requestURI);
+        final NByteArrayEntity entity = new NByteArrayEntity(content, contentType);
+        httppost.setEntity(entity);
+        final HttpHost target = URIUtils.extractHost(requestURI);
+        return new RequestProducerImpl(target, httppost, entity);
+    }
+
+    /**
+     * Creates asynchronous {@code POST} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createPost(
+            final String requestURI,
+            final byte[] content,
+            final ContentType contentType) {
+        return createPost(URI.create(requestURI), content, contentType);
+    }
+
+    /**
+     * Creates asynchronous {@code PUT} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createPut(
+            final URI requestURI,
+            final String content,
+            final ContentType contentType) throws UnsupportedEncodingException {
+        final HttpPut httpput = new HttpPut(requestURI);
+        final NStringEntity entity = new NStringEntity(content, contentType);
+        httpput.setEntity(entity);
+        final HttpHost target = URIUtils.extractHost(requestURI);
+        return new RequestProducerImpl(target, httpput, entity);
+    }
+
+    /**
+     * Creates asynchronous {@code PUT} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createPut(
+            final String requestURI,
+            final String content,
+            final ContentType contentType) throws UnsupportedEncodingException {
+        return createPut(URI.create(requestURI), content, contentType);
+    }
+
+    /**
+     * Creates asynchronous {@code PUT} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createPut(
+            final URI requestURI,
+            final byte[] content,
+            final ContentType contentType) {
+        final HttpPut httpput = new HttpPut(requestURI);
+        final NByteArrayEntity entity = new NByteArrayEntity(content, contentType);
+        httpput.setEntity(entity);
+        final HttpHost target = URIUtils.extractHost(requestURI);
+        return new RequestProducerImpl(target, httpput, entity);
+    }
+
+    /**
+     * Creates asynchronous {@code PUT} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createPut(
+            final String requestURI,
+            final byte[] content,
+            final ContentType contentType) {
+        return createPut(URI.create(requestURI), content, contentType);
+    }
+
+    /**
+     * Creates asynchronous zero-copy {@code POST} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createZeroCopyPost(
+            final URI requestURI,
+            final File content,
+            final ContentType contentType) throws FileNotFoundException {
+        return new ZeroCopyPost(requestURI, content, contentType);
+    }
+
+    /**
+     * Creates asynchronous zero-copy {@code POST} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createZeroCopyPost(
+            final String requestURI,
+            final File content,
+            final ContentType contentType) throws FileNotFoundException {
+        return new ZeroCopyPost(URI.create(requestURI), content, contentType);
+    }
+
+    /**
+     * Creates asynchronous zero-copy {@code PUT} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createZeroCopyPut(
+            final URI requestURI,
+            final File content,
+            final ContentType contentType) throws FileNotFoundException {
+        return new ZeroCopyPut(requestURI, content, contentType);
+    }
+
+    /**
+     * Creates asynchronous zero-copy {@code PUT} request generator.
+     *
+     * @param requestURI request URI.
+     * @param content request content.
+     * @param contentType request contentType.
+     * @return asynchronous request generator
+     */
+    public static HttpAsyncRequestProducer createZeroCopyPut(
+            final String requestURI,
+            final File content,
+            final ContentType contentType) throws FileNotFoundException {
+        return new ZeroCopyPut(URI.create(requestURI), content, contentType);
+    }
+
+    /**
+     * Creates basic response consumer that will buffer response content in memory.
+     * @return asynchronous response consumer.
+     */
+    public static HttpAsyncResponseConsumer<HttpResponse> createConsumer() {
+        return new BasicAsyncResponseConsumer();
+    }
+
+    /**
+     * Creates zero-copy response consumer that will stream response content
+     * directly to the given file.
+     * @return asynchronous response consumer.
+     */
+    public static HttpAsyncResponseConsumer<HttpResponse> createZeroCopyConsumer(
+            final File file) throws FileNotFoundException {
+        return new ZeroCopyConsumer<HttpResponse>(file) {
+
+            @Override
+            protected HttpResponse process(
+                    final HttpResponse response,
+                    final File file,
+                    final ContentType contentType) {
+                return response;
+            }
+
+        };
+    }
+
+    static class RequestProducerImpl extends BasicAsyncRequestProducer {
+
+        protected RequestProducerImpl(
+                final HttpHost target,
+                final HttpEntityEnclosingRequest request,
+                final HttpAsyncContentProducer producer) {
+            super(target, request, producer);
+        }
+
+        public RequestProducerImpl(final HttpHost target, final HttpRequest request) {
+            super(target, request);
+        }
+
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/ZeroCopyConsumer.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/ZeroCopyConsumer.java
new file mode 100644
index 0000000..24a556f
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/ZeroCopyConsumer.java
@@ -0,0 +1,138 @@
+/*
+ * ====================================================================
+ * 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.nio.client.methods;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.FileEntity;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentDecoderChannel;
+import org.apache.http.nio.FileContentDecoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.protocol.AbstractAsyncResponseConsumer;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.Asserts;
+
+/**
+ * {@link org.apache.http.nio.protocol.HttpAsyncResponseConsumer} implementation that
+ * streams content entity enclosed in an HTTP response directly into a file
+ * without an intermediate in-memory buffer.
+ * <p>
+ * This consumer can be useful for file downloads.
+ *
+ * @since 4.0
+ */
+public abstract class ZeroCopyConsumer<T> extends AbstractAsyncResponseConsumer<T> {
+
+    private final File file;
+    private final RandomAccessFile accessfile;
+
+    private HttpResponse response;
+    private ContentType contentType;
+    private Header contentEncoding;
+    private FileChannel fileChannel;
+    private long idx = -1;
+
+    public ZeroCopyConsumer(final File file) throws FileNotFoundException {
+        super();
+        if (file == null) {
+            throw new IllegalArgumentException("File may nor be null");
+        }
+        this.file = file;
+        this.accessfile = new RandomAccessFile(this.file, "rw");
+    }
+
+    @Override
+    protected void onResponseReceived(final HttpResponse response) {
+        this.response = response;
+    }
+
+    @Override
+    protected void onEntityEnclosed(
+            final HttpEntity entity, final ContentType contentType) throws IOException {
+        this.contentType = contentType;
+        this.contentEncoding = entity.getContentEncoding();
+        this.fileChannel = this.accessfile.getChannel();
+        this.idx = 0;
+    }
+
+    @Override
+    protected void onContentReceived(
+            final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
+        Asserts.notNull(this.fileChannel, "File channel");
+        final long transferred;
+        if (decoder instanceof FileContentDecoder) {
+            transferred = ((FileContentDecoder)decoder).transfer(
+                    this.fileChannel, this.idx, Integer.MAX_VALUE);
+        } else {
+            transferred = this.fileChannel.transferFrom(
+                    new ContentDecoderChannel(decoder), this.idx, Integer.MAX_VALUE);
+        }
+        if (transferred > 0) {
+            this.idx += transferred;
+        }
+        if (decoder.isCompleted()) {
+            this.fileChannel.close();
+        }
+    }
+
+    /**
+     * Invoked to process received file.
+     *
+     * @param response original response head.
+     * @param file file containing response content.
+     * @param contentType the cotnent type.
+     * @return result of the response processing
+     */
+    protected abstract T process(
+            HttpResponse response, File file, ContentType contentType) throws Exception;
+
+    @Override
+    protected T buildResult(final HttpContext context) throws Exception {
+        final FileEntity entity = new FileEntity(this.file, this.contentType);
+        entity.setContentEncoding(this.contentEncoding);
+        this.response.setEntity(entity);
+        return process(this.response, this.file, this.contentType);
+    }
+
+    @Override
+    protected void releaseResources() {
+        try {
+            this.accessfile.close();
+        } catch (final IOException ignore) {
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/ZeroCopyPost.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/ZeroCopyPost.java
new file mode 100644
index 0000000..f061f9d
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/ZeroCopyPost.java
@@ -0,0 +1,69 @@
+/*
+ * ====================================================================
+ * 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.nio.client.methods;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URI;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ContentType;
+
+/**
+ * {@link org.apache.http.nio.protocol.HttpAsyncRequestProducer} implementation
+ * that generates an HTTP {@code POST} request enclosing content of a file.
+ * The request content will be streamed out directly from the underlying file
+ * without an intermediate in-memory buffer.
+ *
+ * @since 4.0
+ */
+public class ZeroCopyPost extends BaseZeroCopyRequestProducer {
+
+    public ZeroCopyPost(
+            final URI requestURI,
+            final File content,
+            final ContentType contentType) throws FileNotFoundException {
+        super(requestURI, content, contentType);
+    }
+
+    public ZeroCopyPost(
+            final String requestURI,
+            final File content,
+            final ContentType contentType) throws FileNotFoundException {
+        super(URI.create(requestURI), content, contentType);
+    }
+
+    @Override
+    protected HttpEntityEnclosingRequest createRequest(final URI requestURI, final HttpEntity entity) {
+        final HttpPost httppost = new HttpPost(requestURI);
+        httppost.setEntity(entity);
+        return httppost;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/ZeroCopyPut.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/ZeroCopyPut.java
new file mode 100644
index 0000000..ae257d7
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/ZeroCopyPut.java
@@ -0,0 +1,69 @@
+/*
+ * ====================================================================
+ * 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.nio.client.methods;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URI;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.ContentType;
+
+/**
+ * {@link org.apache.http.nio.protocol.HttpAsyncRequestProducer} implementation
+ * that generates an HTTP {@code PUT} request enclosing content of a file.
+ * The request content will be streamed out directly from the underlying file
+ * without an intermediate in-memory buffer.
+ *
+ * @since 4.0
+ */
+public class ZeroCopyPut extends BaseZeroCopyRequestProducer {
+
+    public ZeroCopyPut(
+            final URI requestURI,
+            final File content,
+            final ContentType contentType) throws FileNotFoundException {
+        super(requestURI, content, contentType);
+    }
+
+    public ZeroCopyPut(
+            final String requestURI,
+            final File content,
+            final ContentType contentType) throws FileNotFoundException {
+        super(URI.create(requestURI), content, contentType);
+    }
+
+    @Override
+    protected HttpEntityEnclosingRequest createRequest(final URI requestURI, final HttpEntity entity) {
+        final HttpPut httpput = new HttpPut(requestURI);
+        httpput.setEntity(entity);
+        return httpput;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/package-info.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/package-info.java
new file mode 100644
index 0000000..9fa081d
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/methods/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * ====================================================================
+ * 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/>.
+ *
+ */
+
+/**
+ * Asynchronous HTTP method implementations.
+ */
+package org.apache.http.nio.client.methods;
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/package-info.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/package-info.java
new file mode 100644
index 0000000..7ce425c
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * ====================================================================
+ * 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/>.
+ *
+ */
+
+/**
+ * Asynchronous HTTP client communication APIs.
+ */
+package org.apache.http.nio.client;
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/util/HttpAsyncClientUtils.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/util/HttpAsyncClientUtils.java
new file mode 100644
index 0000000..42e5bec
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/util/HttpAsyncClientUtils.java
@@ -0,0 +1,70 @@
+/*
+ * ====================================================================
+ * 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.nio.client.util;
+
+import java.io.IOException;
+
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+
+/**
+ * Static helpers for dealing with {@link org.apache.http.nio.client.HttpAsyncClient}.
+ */
+public class HttpAsyncClientUtils {
+
+    private HttpAsyncClientUtils() {
+    }
+
+    /**
+     * Unconditionally close a httpAsyncClient. Shuts down the underlying
+     * connection manager and releases the resources.
+     * <p>
+     * Example Code:
+     *
+     * <pre>
+     * HttpAsyncClient httpAsyncClient = null;
+     * try {
+     *   httpAsyncClient = ...;
+     * } catch (Exception e) {
+     *   // error handling
+     * } finally {
+     *   HttpAsyncClientUtils.closeQuietly(httpAsyncClient);
+     * }
+     * </pre>
+     *
+     * @param httpAsyncClient
+     *            the HttpAsyncClient to close, may be null or already closed.
+     */
+    public static void closeQuietly(final CloseableHttpAsyncClient httpAsyncClient) {
+        if (httpAsyncClient != null) {
+            try {
+                httpAsyncClient.close();
+            } catch (final IOException ignore) {
+            }
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/util/package-info.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/util/package-info.java
new file mode 100644
index 0000000..440881b
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/client/util/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * ====================================================================
+ * 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/>.
+ *
+ */
+
+/**
+ * Asynchronous client utility classes.
+ */
+package org.apache.http.nio.client.util;
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/ManagedNHttpClientConnection.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/ManagedNHttpClientConnection.java
new file mode 100644
index 0000000..d25343d
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/ManagedNHttpClientConnection.java
@@ -0,0 +1,69 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import javax.net.ssl.SSLSession;
+
+import org.apache.http.HttpInetConnection;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.reactor.IOSession;
+
+/**
+ * Represents a managed connection whose state and life cycle is managed by
+ * a connection manager. This interface extends {@link NHttpClientConnection}
+ * with methods to bind the connection to an arbitrary {@link IOSession} and
+ * to obtain SSL session details.
+ *
+ * @since 4.0
+ */
+public interface ManagedNHttpClientConnection extends NHttpClientConnection, HttpInetConnection {
+
+    /**
+     * Returns connection ID which is expected to be unique
+     * for the life span of the connection manager.
+     */
+    String getId();
+
+    /**
+     * Binds connection to the given I/O session.
+     */
+    void bind(IOSession iosession);
+
+    /**
+     * Returns the underlying I/O session.
+     */
+    IOSession getIOSession();
+
+    /**
+     * Obtains the SSL session of the underlying connection, if any.
+     *
+     * @return  the underlying SSL session if available,
+     *          {@code null} otherwise
+     */
+    SSLSession getSSLSession();
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/NHttpClientConnectionManager.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/NHttpClientConnectionManager.java
new file mode 100644
index 0000000..a886389
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/NHttpClientConnectionManager.java
@@ -0,0 +1,197 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.protocol.HttpContext;
+
+/**
+ * Represents a manager of persistent client connections.
+ * <p>
+ * The purpose of an HTTP connection manager is to serve as a factory for new
+ * HTTP connections, manage persistent connections and synchronize access to
+ * persistent connections making sure that only one thread of execution can
+ * have access to a connection at a time.
+ * <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.0
+ */
+public interface NHttpClientConnectionManager {
+
+    /**
+     * Returns a {@link Future} for a {@link NHttpClientConnection}.
+     * <p>
+     * Please note that the consumer of that connection is responsible
+     * for fully establishing the route the to the connection target
+     * by calling {@link #startRoute(org.apache.http.nio.NHttpClientConnection,
+     *   org.apache.http.conn.routing.HttpRoute,
+     *   org.apache.http.protocol.HttpContext) startRoute} in order to start
+     * the process of connection initialization, optionally calling
+     * {@link #upgrade(org.apache.http.nio.NHttpClientConnection,
+     *   org.apache.http.conn.routing.HttpRoute,
+     *   org.apache.http.protocol.HttpContext) upgrade} method to upgrade
+     * the connection after having executed {@code CONNECT} method to
+     * all intermediate proxy hops and and finally calling
+     * {@link #routeComplete(org.apache.http.nio.NHttpClientConnection,
+     *   org.apache.http.conn.routing.HttpRoute,
+     *   org.apache.http.protocol.HttpContext) routeComplete} to mark the route
+     * as fully completed.
+     *
+     * @param route HTTP route of the requested connection.
+     * @param state expected state of the connection or {@code null}
+     *              if the connection is not expected to carry any state.
+     * @param connectTimeout connect timeout.
+     * @param connectionRequestTimeout  connection request timeout.
+     * @param timeUnit time unit of the previous two timeout values.
+     * @param callback future callback.
+     */
+    Future<NHttpClientConnection> requestConnection(
+            HttpRoute route,
+            Object state,
+            long connectTimeout,
+            long connectionRequestTimeout,
+            TimeUnit timeUnit,
+            FutureCallback<NHttpClientConnection> callback);
+
+    /**
+     * Releases the connection back to the manager making it potentially
+     * re-usable by other consumers. Optionally, the maximum period
+     * of how long the manager should keep the connection alive can be
+     * defined using {@code validDuration} and {@code timeUnit}
+     * parameters.
+     *
+     * @param conn      the managed connection to release.
+     * @param validDuration the duration of time this connection is valid for reuse.
+     * @param timeUnit the time unit.
+     *
+     * @see #closeExpiredConnections()
+     */
+    void releaseConnection(
+            NHttpClientConnection conn, Object newState, long validDuration, TimeUnit timeUnit);
+
+    /**
+     * Starts the process of connection initialization. Connection route may consist of several
+     * intermediate hops and may require a protocol upgrade. Once the route is fully established
+     * the {@link #routeComplete(org.apache.http.nio.NHttpClientConnection,
+     *   org.apache.http.conn.routing.HttpRoute,
+     *   org.apache.http.protocol.HttpContext) routeComplete} method must be called.
+     *
+     * @param conn the managed connection to initialize.
+     * @param route the connection route.
+     * @param context the context
+     */
+    void startRoute(
+            NHttpClientConnection conn,
+            HttpRoute route,
+            HttpContext context) throws IOException;
+
+    /**
+     * Upgrades the underlying connection I/O session to TLS/SSL (or another layering
+     * protocol) after having executed {@code CONNECT} method to all
+     * intermediate proxy hops.
+     *
+     * @param conn the managed connection to upgrade.
+     * @param route the connection route.
+     * @param context the context
+     */
+    void upgrade(
+            NHttpClientConnection conn,
+            HttpRoute route,
+            HttpContext context) throws IOException;
+
+    /**
+     * Marks the connection as fully established with all its intermediate
+     * hops completed.
+     *
+     * @param conn the managed connection to mark as route complete.
+     * @param route the connection route.
+     * @param context the context
+     */
+    void routeComplete(
+            NHttpClientConnection conn,
+            HttpRoute route,
+            HttpContext context);
+
+    /**
+     * Determines if the given connection has been fully established and
+     * marked as route complete.
+     *
+     * @param conn the managed connection.
+     */
+    boolean isRouteComplete(NHttpClientConnection conn);
+
+    /**
+     * Closes idle connections in the pool.
+     * <p>
+     * Open connections in the pool that have not been used for the
+     * timespan given by the argument will be closed.
+     * Currently allocated connections are not subject to this method.
+     * Times will be checked with milliseconds precision
+     *
+     * All expired connections will also be closed.
+     *
+     * @param idletime  the idle time of connections to be closed
+     * @param tunit     the unit for the {@code idletime}
+     *
+     * @see #closeExpiredConnections()
+     */
+    void closeIdleConnections(long idletime, TimeUnit tunit);
+
+    /**
+     * Closes all expired connections in the pool.
+     * <p>
+     * Open connections in the pool that have not been used for
+     * the timespan defined when the connection was released will be closed.
+     * Currently allocated connections are not subject to this method.
+     * Times will be checked with milliseconds precision.
+     */
+    void closeExpiredConnections();
+
+    /**
+     * Starts the underlying I/O reactor and initiates the dispatch of
+     * I/O event notifications to the given {@link IOEventDispatch}.
+     */
+    void execute(IOEventDispatch eventDispatch) throws IOException;
+
+    /**
+     * Shuts down this connection manager and releases allocated resources.
+     * This includes closing all connections, whether they are currently
+     * used or not.
+     */
+    void shutdown() throws IOException;
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/NHttpConnectionFactory.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/NHttpConnectionFactory.java
new file mode 100644
index 0000000..0b87925
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/NHttpConnectionFactory.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.nio.conn;
+
+import org.apache.http.config.ConnectionConfig;
+import org.apache.http.nio.NHttpConnection;
+import org.apache.http.nio.reactor.IOSession;
+
+/**
+ * Generic {@link NHttpConnection} factory.
+ *
+ * @since 4.0
+ */
+public interface NHttpConnectionFactory<T extends NHttpConnection> {
+
+    T create(IOSession iosession, ConnectionConfig config);
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/NoopIOSessionStrategy.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/NoopIOSessionStrategy.java
new file mode 100644
index 0000000..e88053b
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/NoopIOSessionStrategy.java
@@ -0,0 +1,52 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import org.apache.http.HttpHost;
+import org.apache.http.nio.reactor.IOSession;
+
+/**
+ * Noop implementation for protocol schemes that have no transport level
+ * security.
+ *
+ * @since 4.0
+ */
+public class NoopIOSessionStrategy implements SchemeIOSessionStrategy {
+
+    public static final NoopIOSessionStrategy INSTANCE = new NoopIOSessionStrategy();
+
+    @Override
+    public IOSession upgrade(final HttpHost host, final IOSession iosession) {
+        return iosession;
+    }
+
+    @Override
+    public boolean isLayeringRequired() {
+        return false;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/SchemeIOSessionStrategy.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/SchemeIOSessionStrategy.java
new file mode 100644
index 0000000..b9b1037
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/SchemeIOSessionStrategy.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.nio.conn;
+
+import org.apache.http.HttpHost;
+import org.apache.http.nio.reactor.IOSession;
+
+import java.io.IOException;
+
+/**
+ * I/O session layering strategy for complex protocol schemes, which employ
+ * a transport level security protocol to secure HTTP communication
+ * (in other words those schemes 'layer' HTTP on top of a transport level
+ * protocol such as TLS/SSL).
+ *
+ * @since 4.0
+ */
+public interface SchemeIOSessionStrategy {
+
+    /**
+     * Determines whether or not protocol layering is required. If this method
+     * returns {@code false} the {@link #upgrade(org.apache.http.HttpHost,
+     * org.apache.http.nio.reactor.IOSession) upgrade} method  is expected
+     * to have no effect and should not be called.
+     */
+    boolean isLayeringRequired();
+
+    /**
+     * Decorates the original {@link IOSession} with a transport level security
+     * protocol implementation.
+     * @param host the target host.
+     * @param iosession the I/O session.
+     * @return upgraded I/O session.
+     */
+    IOSession upgrade(HttpHost host, IOSession iosession) throws IOException;
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/package-info.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/package-info.java
new file mode 100644
index 0000000..0562ff8
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * ====================================================================
+ * 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/>.
+ *
+ */
+
+/**
+ * Asynchronous client connection management APIs.
+ */
+package org.apache.http.nio.conn;
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/ssl/SSLIOSessionStrategy.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/ssl/SSLIOSessionStrategy.java
new file mode 100644
index 0000000..29d6b47
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/ssl/SSLIOSessionStrategy.java
@@ -0,0 +1,218 @@
+/*
+ * ====================================================================
+ * 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.nio.conn.ssl;
+
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.security.auth.x500.X500Principal;
+
+import org.apache.http.HttpHost;
+import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
+import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.conn.ssl.StrictHostnameVerifier;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.conn.util.PublicSuffixMatcherLoader;
+import org.apache.http.nio.conn.SchemeIOSessionStrategy;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.nio.reactor.ssl.SSLIOSession;
+import org.apache.http.nio.reactor.ssl.SSLMode;
+import org.apache.http.nio.reactor.ssl.SSLSetupHandler;
+import org.apache.http.ssl.SSLContexts;
+import org.apache.http.util.Args;
+import org.apache.http.util.Asserts;
+import org.apache.http.util.TextUtils;
+
+/**
+ * TLS/SSL transport level security strategy.
+ *
+ * @since 4.0
+ */
+public class SSLIOSessionStrategy implements SchemeIOSessionStrategy {
+
+    @Deprecated
+    public static final X509HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER =
+            new AllowAllHostnameVerifier();
+
+    @Deprecated
+    public static final X509HostnameVerifier BROWSER_COMPATIBLE_HOSTNAME_VERIFIER =
+            new BrowserCompatHostnameVerifier();
+
+    @Deprecated
+    public static final X509HostnameVerifier STRICT_HOSTNAME_VERIFIER =
+            new StrictHostnameVerifier();
+
+    private static String[] split(final String s) {
+        if (TextUtils.isBlank(s)) {
+            return null;
+        }
+        return s.split(" *, *");
+    }
+
+    /**
+     * @since 4.1
+     */
+    public static HostnameVerifier getDefaultHostnameVerifier() {
+        return new DefaultHostnameVerifier(PublicSuffixMatcherLoader.getDefault());
+    }
+
+    public static SSLIOSessionStrategy getDefaultStrategy() {
+        return new SSLIOSessionStrategy(
+                SSLContexts.createDefault(),
+                getDefaultHostnameVerifier());
+    }
+
+    public static SSLIOSessionStrategy getSystemDefaultStrategy() {
+        return new SSLIOSessionStrategy(
+                SSLContexts.createSystemDefault(),
+                split(System.getProperty("https.protocols")),
+                split(System.getProperty("https.cipherSuites")),
+                getDefaultHostnameVerifier());
+    }
+
+    private final SSLContext sslContext;
+    private final String[] supportedProtocols;
+    private final String[] supportedCipherSuites;
+    private final HostnameVerifier hostnameVerifier;
+
+    /**
+     * @deprecated (4.1) use {@link SSLIOSessionStrategy#SSLIOSessionStrategy(
+     *   javax.net.ssl.SSLContext, String[], String[], javax.net.ssl.HostnameVerifier)}
+     */
+    @Deprecated
+    public SSLIOSessionStrategy(
+            final SSLContext sslContext,
+            final String[] supportedProtocols,
+            final String[] supportedCipherSuites,
+            final X509HostnameVerifier hostnameVerifier) {
+        this(sslContext, supportedProtocols, supportedCipherSuites, (HostnameVerifier) hostnameVerifier);
+    }
+
+    /**
+     * @deprecated (4.1)
+     */
+    @Deprecated
+    public SSLIOSessionStrategy(
+            final SSLContext sslcontext,
+            final X509HostnameVerifier hostnameVerifier) {
+        this(sslcontext, null, null, (HostnameVerifier) hostnameVerifier);
+    }
+
+    /**
+     * @since 4.1
+     */
+    public SSLIOSessionStrategy(
+            final SSLContext sslContext,
+            final String[] supportedProtocols,
+            final String[] supportedCipherSuites,
+            final HostnameVerifier hostnameVerifier) {
+        super();
+        this.sslContext = Args.notNull(sslContext, "SSL context");
+        this.supportedProtocols = supportedProtocols;
+        this.supportedCipherSuites = supportedCipherSuites;
+        this.hostnameVerifier = hostnameVerifier != null ? hostnameVerifier : getDefaultHostnameVerifier();
+    }
+
+    /**
+     * @since 4.1
+     */
+    public SSLIOSessionStrategy(
+            final SSLContext sslcontext,
+            final HostnameVerifier hostnameVerifier) {
+        this(sslcontext, null, null, hostnameVerifier);
+    }
+
+    public SSLIOSessionStrategy(final SSLContext sslcontext) {
+        this(sslcontext, null, null, getDefaultHostnameVerifier());
+    }
+
+    @Override
+    public SSLIOSession upgrade(final HttpHost host, final IOSession iosession) throws IOException {
+        Asserts.check(!(iosession instanceof SSLIOSession), "I/O session is already upgraded to TLS/SSL");
+        final SSLIOSession ssliosession = new SSLIOSession(
+            iosession,
+            SSLMode.CLIENT,
+            host,
+            this.sslContext,
+            new SSLSetupHandler() {
+
+                @Override
+                public void initalize(
+                        final SSLEngine sslengine) throws SSLException {
+                    if (supportedProtocols != null) {
+                        sslengine.setEnabledProtocols(supportedProtocols);
+                    }
+                    if (supportedCipherSuites != null) {
+                        sslengine.setEnabledCipherSuites(supportedCipherSuites);
+                    }
+                    initializeEngine(sslengine);
+                }
+
+                @Override
+                public void verify(
+                        final IOSession iosession,
+                        final SSLSession sslsession) throws SSLException {
+                    verifySession(host, iosession, sslsession);
+                }
+
+        });
+        iosession.setAttribute(SSLIOSession.SESSION_KEY, ssliosession);
+        ssliosession.initialize();
+        return ssliosession;
+    }
+
+    protected void initializeEngine(final SSLEngine engine) {
+    }
+
+    protected void verifySession(
+            final HttpHost host,
+            final IOSession iosession,
+            final SSLSession sslsession) throws SSLException {
+        if (!this.hostnameVerifier.verify(host.getHostName(), sslsession)) {
+            final Certificate[] certs = sslsession.getPeerCertificates();
+            final X509Certificate x509 = (X509Certificate) certs[0];
+            final X500Principal x500Principal = x509.getSubjectX500Principal();
+            throw new SSLPeerUnverifiedException("Host name '" + host.getHostName() + "' does not match " +
+                    "the certificate subject provided by the peer (" + x500Principal.toString() + ")");
+        }
+    }
+
+    @Override
+    public boolean isLayeringRequired() {
+        return true;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/ssl/package-info.java b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/ssl/package-info.java
new file mode 100644
index 0000000..bc2a5b5
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/java/org/apache/http/nio/conn/ssl/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * ====================================================================
+ * 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/>.
+ *
+ */
+
+/**
+ * Asynchronous client TLS/SSL support.
+ */
+package org.apache.http.nio.conn.ssl;
diff --git a/4.1.x/httpasyncclient/src/main/resources/org/apache/http/nio/client/version.properties b/4.1.x/httpasyncclient/src/main/resources/org/apache/http/nio/client/version.properties
new file mode 100644
index 0000000..2c67ce8
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/main/resources/org/apache/http/nio/client/version.properties
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+info.module    = HttpAsyncClient
+info.release   = ${pom.version}
+info.timestamp = ${mvn.timestamp}
+# timestamp requires Maven 2.1
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/impl/nio/conn/CPoolUtils.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/impl/nio/conn/CPoolUtils.java
new file mode 100644
index 0000000..0148327
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/impl/nio/conn/CPoolUtils.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.nio.conn;
+
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.pool.PoolEntry;
+
+public class CPoolUtils {
+
+    public static PoolEntry<HttpRoute, ManagedNHttpClientConnection> getPoolEntry(final NHttpClientConnection managedConn) {
+        return CPoolProxy.getPoolEntry(managedConn);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/impl/nio/conn/TestPoolingHttpClientAsyncConnectionManager.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/impl/nio/conn/TestPoolingHttpClientAsyncConnectionManager.java
new file mode 100644
index 0000000..6c94695
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/impl/nio/conn/TestPoolingHttpClientAsyncConnectionManager.java
@@ -0,0 +1,601 @@
+/*
+ * ====================================================================
+ * 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.nio.conn;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.Calendar;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.logging.Log;
+import org.apache.http.HttpHost;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.config.ConnectionConfig;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.DnsResolver;
+import org.apache.http.conn.SchemePortResolver;
+import org.apache.http.conn.UnsupportedSchemeException;
+import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.ConfigData;
+import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.InternalAddressResolver;
+import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager.InternalConnectionFactory;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.conn.ManagedNHttpClientConnection;
+import org.apache.http.nio.conn.NHttpConnectionFactory;
+import org.apache.http.nio.conn.SchemeIOSessionStrategy;
+import org.apache.http.nio.reactor.ConnectingIOReactor;
+import org.apache.http.nio.reactor.IOSession;
+import org.apache.http.nio.reactor.SessionRequest;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+public class TestPoolingHttpClientAsyncConnectionManager {
+
+    @Mock
+    private ConnectingIOReactor ioreactor;
+    @Mock
+    private CPool pool;
+    @Mock
+    private SchemeIOSessionStrategy noopStrategy;
+    @Mock
+    private SchemeIOSessionStrategy sslStrategy;
+    @Mock
+    private SchemePortResolver schemePortResolver;
+    @Mock
+    private DnsResolver dnsResolver;
+    @Mock
+    private FutureCallback<NHttpClientConnection> connCallback;
+    @Captor
+    private ArgumentCaptor<FutureCallback<CPoolEntry>> poolEntryCallbackCaptor;
+    @Mock
+    private ManagedNHttpClientConnection conn;
+    @Mock
+    private NHttpConnectionFactory<ManagedNHttpClientConnection> connFactory;
+    @Mock
+    private SessionRequest sessionRequest;
+    @Mock
+    private IOSession iosession;
+
+    private Registry<SchemeIOSessionStrategy> layeringStrategyRegistry;
+    private PoolingNHttpClientConnectionManager connman;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        Mockito.when(sslStrategy.isLayeringRequired()).thenReturn(Boolean.TRUE);
+
+        layeringStrategyRegistry = RegistryBuilder.<SchemeIOSessionStrategy>create()
+            .register("http", noopStrategy)
+            .register("https", sslStrategy)
+            .build();
+        connman = new PoolingNHttpClientConnectionManager(
+            ioreactor, pool, layeringStrategyRegistry);
+    }
+
+    @Test
+    public void testShutdown() throws Exception {
+        connman.shutdown();
+
+        Mockito.verify(pool).shutdown(2000);
+    }
+
+    @Test
+    public void testShutdownMs() throws Exception {
+        connman.shutdown(500);
+
+        Mockito.verify(pool).shutdown(500);
+    }
+
+    @Test
+    public void testRequestReleaseConnection() throws Exception {
+        final HttpHost target = new HttpHost("localhost");
+        final HttpRoute route = new HttpRoute(target);
+        final Future<NHttpClientConnection> future = connman.requestConnection(
+            route, "some state", 1000L, 2000L, TimeUnit.MILLISECONDS, connCallback);
+        Assert.assertNotNull(future);
+
+        Mockito.verify(pool).lease(
+                Mockito.same(route),
+                Mockito.eq("some state"),
+                Mockito.eq(1000L),
+                Mockito.eq(2000L),
+                Mockito.eq(TimeUnit.MILLISECONDS),
+                poolEntryCallbackCaptor.capture());
+        final FutureCallback<CPoolEntry> callaback = poolEntryCallbackCaptor.getValue();
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        poolentry.markRouteComplete();
+        callaback.completed(poolentry);
+
+        Assert.assertTrue(future.isDone());
+        final NHttpClientConnection managedConn = future.get();
+        Mockito.verify(connCallback).completed(Mockito.<NHttpClientConnection>any());
+
+        Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE);
+        connman.releaseConnection(managedConn, "new state", 5, TimeUnit.SECONDS);
+
+        Mockito.verify(pool).release(poolentry, true);
+        Assert.assertEquals("new state", poolentry.getState());
+        final Calendar cal = Calendar.getInstance();
+        cal.setTimeInMillis(poolentry.getUpdated());
+        cal.add(Calendar.SECOND, 5);
+        Assert.assertEquals(cal.getTimeInMillis(), poolentry.getExpiry());
+    }
+
+    @Test
+    public void testReleaseConnectionIncompleteRoute() throws Exception {
+        final HttpHost target = new HttpHost("localhost");
+        final HttpRoute route = new HttpRoute(target);
+        final Future<NHttpClientConnection> future = connman.requestConnection(
+            route, "some state", 1000L, 2000L, TimeUnit.MILLISECONDS, connCallback);
+        Assert.assertNotNull(future);
+
+        Mockito.verify(pool).lease(
+                Mockito.same(route),
+                Mockito.eq("some state"),
+                Mockito.eq(1000L),
+                Mockito.eq(2000L),
+                Mockito.eq(TimeUnit.MILLISECONDS),
+                poolEntryCallbackCaptor.capture());
+        final FutureCallback<CPoolEntry> callaback = poolEntryCallbackCaptor.getValue();
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        callaback.completed(poolentry);
+
+        Assert.assertTrue(future.isDone());
+        final NHttpClientConnection managedConn = future.get();
+        Mockito.verify(connCallback).completed(Mockito.<NHttpClientConnection>any());
+
+        Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE);
+        connman.releaseConnection(managedConn, "new state", 5, TimeUnit.SECONDS);
+
+        Mockito.verify(pool).release(poolentry, false);
+    }
+
+    @Test
+    public void testRequestConnectionFutureCancelled() throws Exception {
+        final HttpHost target = new HttpHost("localhost");
+        final HttpRoute route = new HttpRoute(target);
+        final Future<NHttpClientConnection> future = connman.requestConnection(
+            route, "some state", 1000L, 2000L, TimeUnit.MILLISECONDS, null);
+        Assert.assertNotNull(future);
+        future.cancel(true);
+
+        Mockito.verify(pool).lease(
+                Mockito.same(route),
+                Mockito.eq("some state"),
+                Mockito.eq(1000L),
+                Mockito.eq(2000L),
+                Mockito.eq(TimeUnit.MILLISECONDS),
+                poolEntryCallbackCaptor.capture());
+        final FutureCallback<CPoolEntry> callaback = poolEntryCallbackCaptor.getValue();
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        poolentry.markRouteComplete();
+        callaback.completed(poolentry);
+
+        Mockito.verify(pool).release(poolentry, true);
+    }
+
+    @Test(expected=ExecutionException.class)
+    public void testRequestConnectionFailed() throws Exception {
+        final HttpHost target = new HttpHost("localhost");
+        final HttpRoute route = new HttpRoute(target);
+        final Future<NHttpClientConnection> future = connman.requestConnection(
+            route, "some state", 1000L, 2000L, TimeUnit.MILLISECONDS, null);
+        Assert.assertNotNull(future);
+
+        Mockito.verify(pool).lease(
+                Mockito.same(route),
+                Mockito.eq("some state"),
+                Mockito.eq(1000L),
+                Mockito.eq(2000L),
+                Mockito.eq(TimeUnit.MILLISECONDS),
+                poolEntryCallbackCaptor.capture());
+        final FutureCallback<CPoolEntry> callaback = poolEntryCallbackCaptor.getValue();
+        callaback.failed(new Exception());
+
+        Assert.assertTrue(future.isDone());
+        future.get();
+    }
+
+    @Test(expected = CancellationException.class)
+    public void testRequestConnectionCancelled() throws Exception {
+        final HttpHost target = new HttpHost("localhost");
+        final HttpRoute route = new HttpRoute(target);
+        final Future<NHttpClientConnection> future = connman.requestConnection(
+            route, "some state", 1000L, 2000L, TimeUnit.MILLISECONDS, null);
+        Assert.assertNotNull(future);
+
+        Mockito.verify(pool).lease(
+                Mockito.same(route),
+                Mockito.eq("some state"),
+                Mockito.eq(1000L),
+                Mockito.eq(2000L),
+                Mockito.eq(TimeUnit.MILLISECONDS),
+                poolEntryCallbackCaptor.capture());
+        final FutureCallback<CPoolEntry> callaback = poolEntryCallbackCaptor.getValue();
+        callaback.cancelled();
+
+        Assert.assertTrue(future.isDone());
+        Assert.assertTrue(future.isCancelled());
+        future.get();
+    }
+
+    @Test
+    public void testConnectionInitialize() throws Exception {
+        final HttpHost target = new HttpHost("somehost", -1, "http");
+        final HttpRoute route = new HttpRoute(target);
+        final HttpContext context = new BasicHttpContext();
+
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        final NHttpClientConnection managedConn = CPoolProxy.newProxy(poolentry);
+
+        Mockito.when(conn.getIOSession()).thenReturn(iosession);
+        Mockito.when(sslStrategy.upgrade(target, iosession)).thenReturn(iosession);
+
+        connman.startRoute(managedConn, route, context);
+
+        Mockito.verify(noopStrategy, Mockito.never()).upgrade(target, iosession);
+        Mockito.verify(conn, Mockito.never()).bind(iosession);
+
+        Assert.assertFalse(connman.isRouteComplete(managedConn));
+    }
+
+    @Test
+    public void testConnectionInitializeHttps() throws Exception {
+        final HttpHost target = new HttpHost("somehost", 443, "https");
+        final HttpRoute route = new HttpRoute(target, null, true);
+        final HttpContext context = new BasicHttpContext();
+
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        poolentry.markRouteComplete();
+        final NHttpClientConnection managedConn = CPoolProxy.newProxy(poolentry);
+
+        Mockito.when(conn.getIOSession()).thenReturn(iosession);
+        Mockito.when(sslStrategy.upgrade(target, iosession)).thenReturn(iosession);
+
+        connman.startRoute(managedConn, route, context);
+
+        Mockito.verify(sslStrategy).upgrade(target, iosession);
+        Mockito.verify(conn).bind(iosession);
+    }
+
+    @Test
+    public void testConnectionInitializeContextSpecific() throws Exception {
+        final HttpHost target = new HttpHost("somehost", 80, "http11");
+        final HttpRoute route = new HttpRoute(target);
+        final HttpContext context = new BasicHttpContext();
+
+        final Registry<SchemeIOSessionStrategy> reg = RegistryBuilder.<SchemeIOSessionStrategy>create()
+                .register("http11", noopStrategy)
+                .build();
+        context.setAttribute(PoolingNHttpClientConnectionManager.IOSESSION_FACTORY_REGISTRY, reg);
+
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        final NHttpClientConnection managedConn = CPoolProxy.newProxy(poolentry);
+
+        Mockito.when(conn.getIOSession()).thenReturn(iosession);
+        Mockito.when(sslStrategy.upgrade(target, iosession)).thenReturn(iosession);
+
+        connman.startRoute(managedConn, route, context);
+
+        Mockito.verify(noopStrategy, Mockito.never()).upgrade(target, iosession);
+        Mockito.verify(conn, Mockito.never()).bind(iosession);
+
+        Assert.assertFalse(connman.isRouteComplete(managedConn));
+    }
+
+    @Test(expected=UnsupportedSchemeException.class)
+    public void testConnectionInitializeUnknownScheme() throws Exception {
+        final HttpHost target = new HttpHost("somehost", -1, "whatever");
+        final HttpRoute route = new HttpRoute(target, null, true);
+        final HttpContext context = new BasicHttpContext();
+
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        poolentry.markRouteComplete();
+        final NHttpClientConnection managedConn = CPoolProxy.newProxy(poolentry);
+
+        Mockito.when(conn.getIOSession()).thenReturn(iosession);
+        Mockito.when(sslStrategy.upgrade(target, iosession)).thenReturn(iosession);
+
+        connman.startRoute(managedConn, route, context);
+    }
+
+    @Test
+    public void testConnectionUpgrade() throws Exception {
+        final HttpHost target = new HttpHost("somehost", 443, "https");
+        final HttpRoute route = new HttpRoute(target);
+        final HttpContext context = new BasicHttpContext();
+
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        poolentry.markRouteComplete();
+        final NHttpClientConnection managedConn = CPoolProxy.newProxy(poolentry);
+
+        Mockito.when(conn.getIOSession()).thenReturn(iosession);
+        Mockito.when(sslStrategy.upgrade(target, iosession)).thenReturn(iosession);
+
+        connman.upgrade(managedConn, route, context);
+
+        Mockito.verify(sslStrategy).upgrade(target, iosession);
+        Mockito.verify(conn).bind(iosession);
+    }
+
+    @Test(expected=UnsupportedSchemeException.class)
+    public void testConnectionUpgradeUnknownScheme() throws Exception {
+        final HttpHost target = new HttpHost("somehost", -1, "whatever");
+        final HttpRoute route = new HttpRoute(target);
+        final HttpContext context = new BasicHttpContext();
+
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        poolentry.markRouteComplete();
+        final NHttpClientConnection managedConn = CPoolProxy.newProxy(poolentry);
+
+        Mockito.when(conn.getIOSession()).thenReturn(iosession);
+        Mockito.when(sslStrategy.upgrade(target, iosession)).thenReturn(iosession);
+
+        connman.upgrade(managedConn, route, context);
+    }
+
+    @Test(expected=UnsupportedSchemeException.class)
+    public void testConnectionUpgradeIllegalScheme() throws Exception {
+        final HttpHost target = new HttpHost("somehost", 80, "http");
+        final HttpRoute route = new HttpRoute(target);
+        final HttpContext context = new BasicHttpContext();
+
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        poolentry.markRouteComplete();
+        final NHttpClientConnection managedConn = CPoolProxy.newProxy(poolentry);
+
+        Mockito.when(conn.getIOSession()).thenReturn(iosession);
+        Mockito.when(sslStrategy.upgrade(target, iosession)).thenReturn(iosession);
+
+        connman.upgrade(managedConn, route, context);
+    }
+
+    @Test
+    public void testConnectionRouteComplete() throws Exception {
+        final HttpHost target = new HttpHost("somehost", 80, "http");
+        final HttpRoute route = new HttpRoute(target);
+        final HttpContext context = new BasicHttpContext();
+
+        final Log log = Mockito.mock(Log.class);
+        final CPoolEntry poolentry = new CPoolEntry(log, "some-id", route, conn, -1, TimeUnit.MILLISECONDS);
+        poolentry.markRouteComplete();
+        final NHttpClientConnection managedConn = CPoolProxy.newProxy(poolentry);
+
+        Mockito.when(conn.getIOSession()).thenReturn(iosession);
+        Mockito.when(sslStrategy.upgrade(target, iosession)).thenReturn(iosession);
+
+        connman.startRoute(managedConn, route, context);
+        connman.routeComplete(managedConn, route, context);
+
+        Assert.assertTrue(connman.isRouteComplete(managedConn));
+    }
+
+    @Test
+    public void testDelegationToCPool() throws Exception {
+        connman.closeExpiredConnections();
+        Mockito.verify(pool).closeExpired();
+
+        connman.closeIdleConnections(3, TimeUnit.SECONDS);
+        Mockito.verify(pool).closeIdle(3, TimeUnit.SECONDS);
+
+        connman.getMaxTotal();
+        Mockito.verify(pool).getMaxTotal();
+
+        connman.getDefaultMaxPerRoute();
+        Mockito.verify(pool).getDefaultMaxPerRoute();
+
+        final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80));
+        connman.getMaxPerRoute(route);
+        Mockito.verify(pool).getMaxPerRoute(route);
+
+        connman.setMaxTotal(200);
+        Mockito.verify(pool).setMaxTotal(200);
+
+        connman.setDefaultMaxPerRoute(100);
+        Mockito.verify(pool).setDefaultMaxPerRoute(100);
+
+        connman.setMaxPerRoute(route, 150);
+        Mockito.verify(pool).setMaxPerRoute(route, 150);
+
+        connman.getTotalStats();
+        Mockito.verify(pool).getTotalStats();
+
+        connman.getStats(route);
+        Mockito.verify(pool).getStats(route);
+    }
+
+    @Test
+    public void testInternalConnFactoryCreate() throws Exception {
+        final ConfigData configData = new ConfigData();
+        final InternalConnectionFactory internalConnFactory = new InternalConnectionFactory(
+            configData, connFactory);
+
+        final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80));
+        internalConnFactory.create(route, iosession);
+
+        Mockito.verify(sslStrategy, Mockito.never()).upgrade(Mockito.eq(new HttpHost("somehost", 80)),
+                Mockito.<IOSession>any());
+        Mockito.verify(connFactory).create(Mockito.same(iosession), Mockito.<ConnectionConfig>any());
+    }
+
+    @Test
+    public void testInternalConnFactoryCreateViaProxy() throws Exception {
+        final ConfigData configData = new ConfigData();
+        final InternalConnectionFactory internalConnFactory = new InternalConnectionFactory(
+            configData, connFactory);
+
+        final HttpHost target = new HttpHost("somehost", 80);
+        final HttpHost proxy = new HttpHost("someproxy", 8888);
+        final HttpRoute route = new HttpRoute(target, null, proxy, false);
+
+        final ConnectionConfig config = ConnectionConfig.custom().build();
+        configData.setConnectionConfig(proxy, config);
+
+        internalConnFactory.create(route, iosession);
+
+        Mockito.verify(connFactory).create(iosession, config);
+    }
+
+    @Test
+    public void testInternalConnFactoryCreateDirect() throws Exception {
+        final ConfigData configData = new ConfigData();
+        final InternalConnectionFactory internalConnFactory = new InternalConnectionFactory(
+            configData, connFactory);
+
+        final HttpHost target = new HttpHost("somehost", 80);
+        final HttpRoute route = new HttpRoute(target);
+
+        final ConnectionConfig config = ConnectionConfig.custom().build();
+        configData.setConnectionConfig(target, config);
+
+        internalConnFactory.create(route, iosession);
+
+        Mockito.verify(connFactory).create(iosession, config);
+    }
+
+    @Test
+    public void testInternalConnFactoryCreateDefaultConfig() throws Exception {
+        final ConfigData configData = new ConfigData();
+        final InternalConnectionFactory internalConnFactory = new InternalConnectionFactory(
+            configData, connFactory);
+
+        final HttpHost target = new HttpHost("somehost", 80);
+        final HttpRoute route = new HttpRoute(target);
+
+        final ConnectionConfig config = ConnectionConfig.custom().build();
+        configData.setDefaultConnectionConfig(config);
+
+        internalConnFactory.create(route, iosession);
+
+        Mockito.verify(connFactory).create(iosession, config);
+    }
+
+    @Test
+    public void testInternalConnFactoryCreateGlobalDefaultConfig() throws Exception {
+        final ConfigData configData = new ConfigData();
+        final InternalConnectionFactory internalConnFactory = new InternalConnectionFactory(
+            configData, connFactory);
+
+        final HttpHost target = new HttpHost("somehost", 80);
+        final HttpRoute route = new HttpRoute(target);
+
+        configData.setDefaultConnectionConfig(null);
+
+        internalConnFactory.create(route, iosession);
+
+        Mockito.verify(connFactory).create(iosession, ConnectionConfig.DEFAULT);
+    }
+
+    @Test
+    public void testResolveLocalAddress() throws Exception {
+        final InternalAddressResolver addressResolver = new InternalAddressResolver(
+                schemePortResolver, dnsResolver);
+
+        final HttpHost target = new HttpHost("localhost");
+        final byte[] ip = new byte[] {10, 0, 0, 10};
+        final HttpRoute route = new HttpRoute(target, InetAddress.getByAddress(ip), false);
+        final InetSocketAddress address = (InetSocketAddress) addressResolver.resolveLocalAddress(route);
+
+        Assert.assertNotNull(address);
+        Assert.assertEquals(InetAddress.getByAddress(ip), address.getAddress());
+        Assert.assertEquals(0, address.getPort());
+    }
+
+    @Test
+    public void testResolveLocalAddressNull() throws Exception {
+        final InternalAddressResolver addressResolver = new InternalAddressResolver(
+                schemePortResolver, dnsResolver);
+
+        final HttpHost target = new HttpHost("localhost");
+        final HttpRoute route = new HttpRoute(target);
+        final InetSocketAddress address = (InetSocketAddress) addressResolver.resolveLocalAddress(route);
+
+        Assert.assertNull(address);
+    }
+
+    @Test
+    public void testResolveRemoteAddress() throws Exception {
+        final InternalAddressResolver addressResolver = new InternalAddressResolver(
+                schemePortResolver, dnsResolver);
+
+        final HttpHost target = new HttpHost("somehost", 80);
+        final HttpRoute route = new HttpRoute(target);
+
+        Mockito.when(schemePortResolver.resolve(target)).thenReturn(123);
+        final byte[] ip = new byte[] {10, 0, 0, 10};
+        Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] {InetAddress.getByAddress(ip)});
+
+        final InetSocketAddress address = (InetSocketAddress) addressResolver.resolveRemoteAddress(route);
+
+        Assert.assertNotNull(address);
+        Assert.assertEquals(InetAddress.getByAddress(ip), address.getAddress());
+        Assert.assertEquals(123, address.getPort());
+    }
+
+    @Test
+    public void testResolveRemoteAddressViaProxy() throws Exception {
+        final InternalAddressResolver addressResolver = new InternalAddressResolver(
+                schemePortResolver, dnsResolver);
+
+        final HttpHost target = new HttpHost("somehost", 80);
+        final HttpHost proxy = new HttpHost("someproxy");
+        final HttpRoute route = new HttpRoute(target, null, proxy, false);
+
+        Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8888);
+        final byte[] ip = new byte[] {10, 0, 0, 10};
+        Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {InetAddress.getByAddress(ip)});
+
+        final InetSocketAddress address = (InetSocketAddress) addressResolver.resolveRemoteAddress(route);
+
+        Assert.assertNotNull(address);
+        Assert.assertEquals(InetAddress.getByAddress(ip), address.getAddress());
+        Assert.assertEquals(8888, address.getPort());
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/AbstractAsyncTest.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/AbstractAsyncTest.java
new file mode 100644
index 0000000..980b1e9
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/AbstractAsyncTest.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.localserver;
+
+import java.net.InetSocketAddress;
+import java.net.URL;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.ExceptionLogger;
+import org.apache.http.HttpHost;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.ssl.DefaultHostnameVerifier;
+import org.apache.http.impl.nio.bootstrap.HttpServer;
+import org.apache.http.impl.nio.bootstrap.ServerBootstrap;
+import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
+import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
+import org.apache.http.impl.nio.reactor.IOReactorConfig;
+import org.apache.http.nio.conn.NoopIOSessionStrategy;
+import org.apache.http.nio.conn.SchemeIOSessionStrategy;
+import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
+import org.apache.http.nio.reactor.ListenerEndpoint;
+import org.apache.http.ssl.SSLContextBuilder;
+import org.junit.After;
+import org.junit.Before;
+
+public abstract class AbstractAsyncTest {
+
+    public enum ProtocolScheme { http, https };
+
+    protected final ProtocolScheme scheme;
+
+    protected ServerBootstrap serverBootstrap;
+    protected HttpServer server;
+    protected PoolingNHttpClientConnectionManager connMgr;
+
+    public AbstractAsyncTest(final ProtocolScheme scheme) {
+        this.scheme = scheme;
+    }
+
+    public AbstractAsyncTest() {
+        this(ProtocolScheme.http);
+    }
+
+    public String getSchemeName() {
+        return this.scheme.name();
+    }
+
+    protected SSLContext createServerSSLContext() throws Exception {
+        final URL keyStoreURL = getClass().getResource("/test.keystore");
+        final String storePassword = "nopassword";
+        return SSLContextBuilder.create()
+                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
+                .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
+                .build();
+    }
+
+    protected SSLContext createClientSSLContext() throws Exception {
+        final URL keyStoreURL = getClass().getResource("/test.keystore");
+        final String storePassword = "nopassword";
+        return SSLContextBuilder.create()
+                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
+                .build();
+    }
+
+    public HttpHost startServer() throws Exception {
+        this.server = this.serverBootstrap.create();
+        this.server.start();
+
+        final ListenerEndpoint endpoint = this.server.getEndpoint();
+        endpoint.waitFor();
+
+        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
+        return new HttpHost("localhost", address.getPort(), this.scheme.name());
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        this.serverBootstrap = ServerBootstrap.bootstrap();
+        final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
+                .setSoTimeout(15000)
+                .build();
+        this.serverBootstrap.setServerInfo("TEST/1.1");
+        this.serverBootstrap.setIOReactorConfig(ioReactorConfig);
+        this.serverBootstrap.setExceptionLogger(new ExceptionLogger() {
+
+            private final Log log = LogFactory.getLog(AbstractAsyncTest.class);
+
+            @Override
+            public void log(final Exception ex) {
+                log.error(ex.getMessage(), ex);
+            }
+        });
+        if (this.scheme.equals(ProtocolScheme.https)) {
+            this.serverBootstrap.setSslContext(createServerSSLContext());
+        }
+
+        final RegistryBuilder<SchemeIOSessionStrategy> builder = RegistryBuilder.create();
+        builder.register("http", NoopIOSessionStrategy.INSTANCE);
+        if (this.scheme.equals(ProtocolScheme.https)) {
+            builder.register("https", new SSLIOSessionStrategy(
+                    createClientSSLContext(),
+                    new DefaultHostnameVerifier()));
+        }
+        final Registry<SchemeIOSessionStrategy> registry =  builder.build();
+        final DefaultConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(ioReactorConfig);
+        this.connMgr = new PoolingNHttpClientConnectionManager(ioReactor, registry);
+    }
+
+    @After
+    public void shutDown() throws Exception {
+        if (this.server != null) {
+            this.server.shutdown(10, TimeUnit.SECONDS);
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/BasicAuthTokenExtractor.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/BasicAuthTokenExtractor.java
new file mode 100644
index 0000000..3bb93c6
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/BasicAuthTokenExtractor.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.localserver;
+
+import org.apache.commons.codec.BinaryDecoder;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.http.Header;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.ProtocolException;
+import org.apache.http.auth.AUTH;
+import org.apache.http.util.EncodingUtils;
+
+public class BasicAuthTokenExtractor {
+
+    public String extract(final HttpRequest request) throws HttpException {
+        String auth = null;
+
+        final Header h = request.getFirstHeader(AUTH.WWW_AUTH_RESP);
+        if (h != null) {
+            final String s = h.getValue();
+            if (s != null) {
+                auth = s.trim();
+            }
+        }
+
+        if (auth != null) {
+            final int i = auth.indexOf(' ');
+            if (i == -1) {
+                throw new ProtocolException("Invalid Authorization header: " + auth);
+            }
+            final String authscheme = auth.substring(0, i);
+            if (authscheme.equalsIgnoreCase("basic")) {
+                final String s = auth.substring(i + 1).trim();
+                try {
+                    final byte[] credsRaw = EncodingUtils.getAsciiBytes(s);
+                    final BinaryDecoder codec = new Base64();
+                    auth = EncodingUtils.getAsciiString(codec.decode(credsRaw));
+                } catch (final DecoderException ex) {
+                    throw new ProtocolException("Malformed BASIC credentials");
+                }
+            }
+        }
+        return auth;
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/EchoHandler.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/EchoHandler.java
new file mode 100644
index 0000000..21b6d54
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/EchoHandler.java
@@ -0,0 +1,99 @@
+/*
+ * ====================================================================
+ * 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.localserver;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.MethodNotSupportedException;
+import org.apache.http.nio.entity.NByteArrayEntity;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.util.EntityUtils;
+
+/**
+ * A handler that echos the incoming request entity.
+ */
+public class EchoHandler
+    implements HttpRequestHandler {
+
+    // public default constructor
+
+    /**
+     * Handles a request by echoing the incoming request entity.
+     * If there is no request entity, an empty document is returned.
+     *
+     * @param request   the request
+     * @param response  the response
+     * @param context   the context
+     *
+     * @throws HttpException    in case of a problem
+     * @throws IOException      in case of an IO problem
+     */
+    @Override
+    public void handle(final HttpRequest request,
+                       final HttpResponse response,
+                       final HttpContext context)
+        throws HttpException, IOException {
+
+        final String method = request.getRequestLine().getMethod().toUpperCase(Locale.ROOT);
+        if (!"GET".equals(method) &&
+            !"POST".equals(method) &&
+            !"PUT".equals(method)
+            ) {
+            throw new MethodNotSupportedException
+                (method + " not supported by " + getClass().getName());
+        }
+
+        HttpEntity entity = null;
+        if (request instanceof HttpEntityEnclosingRequest) {
+            entity = ((HttpEntityEnclosingRequest)request).getEntity();
+        }
+
+        // For some reason, just putting the incoming entity into
+        // the response will not work. We have to buffer the message.
+        final byte[] data;
+        if (entity == null) {
+            data = new byte [0];
+        } else {
+            data = EntityUtils.toByteArray(entity);
+        }
+
+        final NByteArrayEntity bae = new NByteArrayEntity(data);
+        if (entity != null) {
+            bae.setContentType(entity.getContentType());
+        }
+        response.setEntity(bae);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/HttpAsyncTestBase.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/HttpAsyncTestBase.java
new file mode 100644
index 0000000..dd05d98
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/HttpAsyncTestBase.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.localserver;
+
+import org.apache.http.HttpHost;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
+import org.junit.After;
+import org.junit.Before;
+
+public abstract class HttpAsyncTestBase extends AbstractAsyncTest{
+
+    protected HttpAsyncClientBuilder clientBuilder;
+    protected CloseableHttpAsyncClient httpclient;
+
+    public HttpAsyncTestBase() {
+        super();
+    }
+
+    public HttpAsyncTestBase(final ProtocolScheme scheme) {
+        super(scheme);
+    }
+
+    public HttpHost start() throws Exception {
+        final HttpHost serverEndpoint = startServer();
+
+        this.httpclient = this.clientBuilder.build();
+        this.httpclient.start();
+
+        return serverEndpoint;
+    }
+
+    @Before @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        this.clientBuilder = HttpAsyncClientBuilder.create();
+        this.clientBuilder.setConnectionManager(this.connMgr);
+    }
+
+    @After  @Override
+    public void shutDown() throws Exception {
+        if (this.httpclient != null) {
+            this.httpclient.close();
+        }
+        super.shutDown();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/RandomHandler.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/RandomHandler.java
new file mode 100644
index 0000000..966b500
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/RandomHandler.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.localserver;
+
+import java.io.IOException;
+import java.util.Locale;
+
+import org.apache.http.Consts;
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.MethodNotSupportedException;
+import org.apache.http.entity.ContentType;
+import org.apache.http.nio.entity.NByteArrayEntity;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+
+/**
+ * A handler that generates random data.
+ */
+public class RandomHandler implements HttpRequestHandler {
+
+    private final static byte[] RANGE;
+    static {
+        RANGE = ("abcdefghijklmnopqrstuvwxyz" +
+                "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"
+        ).getBytes(Consts.ASCII);
+    }
+
+    /**
+     * Handles a request by generating random data.
+     * The length of the response can be specified in the request URI
+     * as a number after the last /. For example /random/whatever/20
+     * will generate 20 random bytes in the printable ASCII range.
+     * If the request URI ends with /, a random number of random bytes
+     * is generated, but at least one.
+     *
+     * @param request   the request
+     * @param response  the response
+     * @param context   the context
+     *
+     * @throws HttpException    in case of a problem
+     * @throws IOException      in case of an IO problem
+     */
+    @Override
+    public void handle(final HttpRequest request,
+                       final HttpResponse response,
+                       final HttpContext context)
+        throws HttpException, IOException {
+
+        final String method = request.getRequestLine().getMethod().toUpperCase(Locale.ROOT);
+        if (!"GET".equals(method) && !"HEAD".equals(method)) {
+            throw new MethodNotSupportedException
+                (method + " not supported by " + getClass().getName());
+        }
+
+        final String uri = request.getRequestLine().getUri();
+        final int  slash = uri.lastIndexOf('/');
+        int length = -1;
+        if (slash < uri.length()-1) {
+            try {
+                // no more than Integer, 2 GB ought to be enough for anybody
+                length = Integer.parseInt(uri.substring(slash+1));
+
+                if (length < 0) {
+                    response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
+                    response.setReasonPhrase("LENGTH " + length);
+                }
+            } catch (final NumberFormatException nfx) {
+                response.setStatusCode(HttpStatus.SC_BAD_REQUEST);
+                response.setReasonPhrase(nfx.toString());
+            }
+        } else {
+            // random length, but make sure at least something is sent
+            length = 1 + (int)(Math.random() * 79.0);
+        }
+
+        if (length >= 0) {
+            final byte[] data = new byte[length];
+            for (int i = 0; i < length; i++) {
+                double value = 0.0;
+                // we get 5 random characters out of one random value
+                if (i%5 == 0) {
+                    value = Math.random();
+                }
+                value = value * RANGE.length;
+                final int d = (int) value;
+                value = value - d;
+                data[i] = RANGE[d];
+            }
+            final NByteArrayEntity bae = new NByteArrayEntity(data, ContentType.DEFAULT_TEXT);
+            response.setEntity(bae);
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/RequestBasicAuth.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/RequestBasicAuth.java
new file mode 100644
index 0000000..5723bd6
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/RequestBasicAuth.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.localserver;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpRequestInterceptor;
+import org.apache.http.protocol.HttpContext;
+
+public class RequestBasicAuth implements HttpRequestInterceptor {
+
+    private final BasicAuthTokenExtractor authTokenExtractor;
+
+    public RequestBasicAuth() {
+        super();
+        this.authTokenExtractor = new BasicAuthTokenExtractor();
+    }
+
+    @Override
+    public void process(
+            final HttpRequest request,
+            final HttpContext context) throws HttpException, IOException {
+        context.setAttribute("creds", this.authTokenExtractor.extract(request));
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/ResponseBasicUnauthorized.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/ResponseBasicUnauthorized.java
new file mode 100644
index 0000000..a1c58d8
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/localserver/ResponseBasicUnauthorized.java
@@ -0,0 +1,50 @@
+/*
+ * ====================================================================
+ * 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.localserver;
+
+import java.io.IOException;
+
+import org.apache.http.HttpException;
+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.protocol.HttpContext;
+
+public class ResponseBasicUnauthorized implements HttpResponseInterceptor {
+
+    @Override
+    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\"");
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthentication.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthentication.java
new file mode 100644
index 0000000..774f95f
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthentication.java
@@ -0,0 +1,474 @@
+/*
+ * ====================================================================
+ * 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.nio.client.integration;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.apache.http.Consts;
+import org.apache.http.localserver.HttpAsyncTestBase;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.ProtocolVersion;
+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.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.TargetAuthenticationStrategy;
+import org.apache.http.localserver.BasicAuthTokenExtractor;
+import org.apache.http.localserver.RequestBasicAuth;
+import org.apache.http.localserver.ResponseBasicUnauthorized;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.nio.entity.NByteArrayEntity;
+import org.apache.http.nio.entity.NStringEntity;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.nio.protocol.BasicAsyncResponseProducer;
+import org.apache.http.nio.protocol.HttpAsyncExchange;
+import org.apache.http.nio.protocol.HttpAsyncExpectationVerifier;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpCoreContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TestClientAuthentication extends HttpAsyncTestBase {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> protocols() {
+        return Arrays.asList(new Object[][]{
+                {ProtocolScheme.http},
+                {ProtocolScheme.https},
+        });
+    }
+
+    public TestClientAuthentication(final ProtocolScheme scheme) {
+        super(scheme);
+    }
+
+    @Before @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        this.serverBootstrap.addInterceptorFirst(new RequestBasicAuth());
+        this.serverBootstrap.addInterceptorLast(new ResponseBasicUnauthorized());
+    }
+
+    static class AuthHandler implements HttpRequestHandler {
+
+        private final boolean keepAlive;
+
+        AuthHandler(final boolean keepAlive) {
+            super();
+            this.keepAlive = keepAlive;
+        }
+
+        AuthHandler() {
+            this(true);
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final String creds = (String) context.getAttribute("creds");
+            if (creds == null || !creds.equals("test:test")) {
+                response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
+            } else {
+                response.setStatusCode(HttpStatus.SC_OK);
+                final NStringEntity entity = new NStringEntity("success", Consts.ASCII);
+                response.setEntity(entity);
+            }
+            response.setHeader(HTTP.CONN_DIRECTIVE,
+                    this.keepAlive ? HTTP.CONN_KEEP_ALIVE : HTTP.CONN_CLOSE);
+        }
+
+    }
+
+    static class TestTargetAuthenticationStrategy extends TargetAuthenticationStrategy {
+
+        private int count;
+
+        public TestTargetAuthenticationStrategy() {
+            super();
+            this.count = 0;
+        }
+
+        @Override
+        public boolean isAuthenticationRequested(
+                final HttpHost authhost,
+                final HttpResponse response,
+                final HttpContext context) {
+            final boolean res = super.isAuthenticationRequested(authhost, response, context);
+            if (res) {
+                synchronized (this) {
+                    this.count++;
+                }
+            }
+            return res;
+        }
+
+        public int getCount() {
+            synchronized (this) {
+                return this.count;
+            }
+        }
+
+    }
+
+    static class AuthExpectationVerifier implements HttpAsyncExpectationVerifier {
+
+        private final BasicAuthTokenExtractor authTokenExtractor;
+
+        public AuthExpectationVerifier() {
+            super();
+            this.authTokenExtractor = new BasicAuthTokenExtractor();
+        }
+
+        @Override
+        public void verify(
+                final HttpAsyncExchange httpexchange,
+                final HttpContext context) throws HttpException, IOException {
+            final HttpRequest request = httpexchange.getRequest();
+            ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+            if (!ver.lessEquals(HttpVersion.HTTP_1_1)) {
+                ver = HttpVersion.HTTP_1_1;
+            }
+            final String creds = this.authTokenExtractor.extract(request);
+            if (creds == null || !creds.equals("test:test")) {
+                final HttpResponse response = new BasicHttpResponse(ver, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
+                httpexchange.submitResponse(new BasicAsyncResponseProducer(response));
+            } else {
+                httpexchange.submitResponse();
+            }
+        }
+
+    }
+
+    static class TestCredentialsProvider implements CredentialsProvider {
+
+        private final Credentials creds;
+        private AuthScope authscope;
+
+        TestCredentialsProvider(final Credentials creds) {
+            super();
+            this.creds = creds;
+        }
+
+        @Override
+        public void clear() {
+        }
+
+        @Override
+        public Credentials getCredentials(final AuthScope authscope) {
+            this.authscope = authscope;
+            return this.creds;
+        }
+
+        @Override
+        public void setCredentials(final AuthScope authscope, final Credentials credentials) {
+        }
+
+        public AuthScope getAuthScope() {
+            return this.authscope;
+        }
+
+    }
+
+    @Test
+    public void testBasicAuthenticationNoCreds() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(null);
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+        final HttpGet httpget = new HttpGet("/");
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationFailure() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "all-wrong"));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+        final HttpGet httpget = new HttpGet("/");
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccess() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test"));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+        final HttpGet httpget = new HttpGet("/");
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccessNonPersistentConnection() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler(false)));
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test"));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final HttpGet httpget = new HttpGet("/");
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccessWithNonRepeatableExpectContinue() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        this.serverBootstrap.setExpectationVerifier(new AuthExpectationVerifier());
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test"));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+        final HttpPut httpput = new HttpPut("/");
+
+        final NByteArrayEntity entity = new NByteArrayEntity(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }) {
+
+            @Override
+            public boolean isRepeatable() {
+                return false;
+            }
+
+        };
+
+        httpput.setEntity(entity);
+        httpput.setConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpput, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+    }
+
+    @Test(expected=ExecutionException.class)
+    public void testBasicAuthenticationFailureWithNonRepeatableEntityExpectContinueOff() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test"));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+        final HttpPut httpput = new HttpPut("/");
+
+        final NByteArrayEntity requestEntity = new NByteArrayEntity(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }) {
+
+            @Override
+            public boolean isRepeatable() {
+                return false;
+            }
+
+        };
+
+        httpput.setEntity(requestEntity);
+        httpput.setConfig(RequestConfig.custom().setExpectContinueEnabled(false).build());
+
+        try {
+            final Future<HttpResponse> future = this.httpclient.execute(target, httpput, context, null);
+            future.get();
+            Assert.fail("ExecutionException should have been thrown");
+        } catch (final ExecutionException ex) {
+            final Throwable cause = ex.getCause();
+            Assert.assertNotNull(cause);
+            throw ex;
+        }
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccessOnRepeatablePost() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test"));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+        final HttpPost httppost = new HttpPost("/");
+        httppost.setEntity(new NStringEntity("some important stuff", Consts.ISO_8859_1));
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httppost, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationCredentialsCaching() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        final TestTargetAuthenticationStrategy authStrategy = new TestTargetAuthenticationStrategy();
+        this.clientBuilder.setTargetAuthenticationStrategy(authStrategy);
+        final HttpHost target = start();
+
+        final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
+        credsProvider.setCredentials(AuthScope.ANY,
+                new UsernamePasswordCredentials("test", "test"));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final HttpGet httpget1 = new HttpGet("/");
+        final Future<HttpResponse> future1 = this.httpclient.execute(target, httpget1, context, null);
+        final HttpResponse response1 = future1.get();
+        Assert.assertNotNull(response1);
+        Assert.assertEquals(HttpStatus.SC_OK, response1.getStatusLine().getStatusCode());
+
+        final HttpGet httpget2 = new HttpGet("/");
+        final Future<HttpResponse> future2 = this.httpclient.execute(target, httpget2, context, null);
+        final HttpResponse response2 = future2.get();
+        Assert.assertNotNull(response2);
+        Assert.assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
+
+        Assert.assertEquals(1, authStrategy.getCount());
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRequestSuccess() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        final HttpHost target = start();
+
+        final HttpGet httpget = new HttpGet("http://test:test@" +  target.toHostString() + "/");
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        final HttpEntity entity = response.getEntity();
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertNotNull(entity);
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRequestFailure() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        final HttpHost target = start();
+
+        final HttpGet httpget = new HttpGet("http://test:all-wrong@" +  target.toHostString() + "/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        final HttpEntity entity = response.getEntity();
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode());
+        Assert.assertNotNull(entity);
+    }
+
+    private class RedirectHandler implements HttpRequestHandler {
+
+        public RedirectHandler() {
+            super();
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final HttpInetConnection conn = (HttpInetConnection) context.getAttribute(HttpCoreContext.HTTP_CONNECTION);
+            final int port = conn.getLocalPort();
+            response.setStatusCode(HttpStatus.SC_MOVED_PERMANENTLY);
+            response.addHeader(new BasicHeader("Location", getSchemeName() + "://test:test@localhost:" + port + "/"));
+        }
+
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRedirectSuccess() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        this.serverBootstrap.registerHandler("/thatway", new BasicAsyncRequestHandler(new RedirectHandler()));
+        final HttpHost target = start();
+
+        final HttpGet httpget = new HttpGet(target.getSchemeName() + "://test:test@" +  target.toHostString() + "/thatway");
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        final HttpEntity entity = response.getEntity();
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertNotNull(entity);
+    }
+
+}
\ No newline at end of file
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthenticationFallBack.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthenticationFallBack.java
new file mode 100644
index 0000000..d6fcc07
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthenticationFallBack.java
@@ -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/>.
+ *
+ */
+package org.apache.http.nio.client.integration;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+
+import org.apache.http.Consts;
+import org.apache.http.localserver.HttpAsyncTestBase;
+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.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.client.protocol.HttpClientContext;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.localserver.RequestBasicAuth;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.util.EntityUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestClientAuthenticationFallBack extends HttpAsyncTestBase {
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        this.serverBootstrap.addInterceptorFirst(new RequestBasicAuth());
+        this.serverBootstrap.addInterceptorLast(new ResponseBasicUnauthorized());
+    }
+
+    public class ResponseBasicUnauthorized implements HttpResponseInterceptor {
+
+        @Override
+        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\"");
+            }
+        }
+
+    }
+
+    static class AuthHandler implements HttpRequestHandler {
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final String creds = (String) context.getAttribute("creds");
+            if (creds == null || !creds.equals("test:test")) {
+                response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
+            } else {
+                response.setStatusCode(HttpStatus.SC_OK);
+                final 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;
+        }
+
+        @Override
+        public void clear() {
+        }
+
+        @Override
+        public Credentials getCredentials(final AuthScope authscope) {
+            this.authscope = authscope;
+            return this.creds;
+        }
+
+        @Override
+        public void setCredentials(final AuthScope authscope, final Credentials credentials) {
+        }
+
+        public AuthScope getAuthScope() {
+            return this.authscope;
+        }
+
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccess() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test"));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final HttpGet httpget = new HttpGet("/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        final HttpEntity entity = response.getEntity();
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertNotNull(entity);
+        EntityUtils.consume(entity);
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientReauthentication.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientReauthentication.java
new file mode 100644
index 0000000..40bf2bb
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientReauthentication.java
@@ -0,0 +1,205 @@
+/*
+ * ====================================================================
+ * 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.nio.client.integration;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.http.Consts;
+import org.apache.http.localserver.HttpAsyncTestBase;
+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.HttpResponseInterceptor;
+import org.apache.http.HttpStatus;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthScheme;
+import org.apache.http.auth.AuthSchemeProvider;
+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.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.auth.BasicSchemeFactory;
+import org.apache.http.impl.client.TargetAuthenticationStrategy;
+import org.apache.http.localserver.RequestBasicAuth;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.http.util.EntityUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestClientReauthentication extends HttpAsyncTestBase {
+
+    @Before @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        this.serverBootstrap.addInterceptorFirst(new RequestBasicAuth());
+        this.serverBootstrap.addInterceptorLast(new ResponseBasicUnauthorized());
+    }
+
+    public class ResponseBasicUnauthorized implements HttpResponseInterceptor {
+
+        @Override
+        public void process(
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
+                response.addHeader(AUTH.WWW_AUTH, "MyBasic realm=\"test realm\"");
+            }
+        }
+
+    }
+
+    static class AuthHandler implements HttpRequestHandler {
+
+        private final AtomicLong count = new AtomicLong(0);
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final 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);
+                    final 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;
+        }
+
+        @Override
+        public void clear() {
+        }
+
+        @Override
+        public Credentials getCredentials(final AuthScope authscope) {
+            this.authscope = authscope;
+            return this.creds;
+        }
+
+        @Override
+        public void setCredentials(final AuthScope authscope, final Credentials credentials) {
+        }
+
+        public AuthScope getAuthScope() {
+            return this.authscope;
+        }
+
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccess() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new AuthHandler()));
+
+        final BasicSchemeFactory myBasicAuthSchemeFactory = new BasicSchemeFactory() {
+
+            @Override
+            public AuthScheme create(final HttpContext context) {
+                return new BasicScheme() {
+                    private static final long serialVersionUID = 1L;
+
+                    @Override
+                    public String getSchemeName() {
+                        return "MyBasic";
+                    }
+
+                };
+            }
+
+        };
+
+        final TargetAuthenticationStrategy myAuthStrategy = new TargetAuthenticationStrategy() {
+
+            @Override
+            protected boolean isCachable(final AuthScheme authScheme) {
+                return "MyBasic".equalsIgnoreCase(authScheme.getSchemeName());
+            }
+
+        };
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test"));
+
+        final Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
+            .register("MyBasic", myBasicAuthSchemeFactory)
+            .build();
+        this.clientBuilder.setDefaultAuthSchemeRegistry(authSchemeRegistry);
+        this.clientBuilder.setTargetAuthenticationStrategy(myAuthStrategy);
+        final HttpHost target = start();
+
+        final RequestConfig config = RequestConfig.custom()
+                .setTargetPreferredAuthSchemes(Arrays.asList("MyBasic"))
+                .build();
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        for (int i = 0; i < 10; i++) {
+            final HttpGet httpget = new HttpGet("/");
+            httpget.setConfig(config);
+            final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+            final HttpResponse response = future.get();
+            Assert.assertNotNull(response);
+            final HttpEntity entity = response.getEntity();
+            Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+            Assert.assertNotNull(entity);
+            EntityUtils.consume(entity);
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsync.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsync.java
new file mode 100644
index 0000000..38fd35c
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsync.java
@@ -0,0 +1,285 @@
+/*
+ * ====================================================================
+ * 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.nio.client.integration;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.Random;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.localserver.HttpAsyncTestBase;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.localserver.EchoHandler;
+import org.apache.http.localserver.RandomHandler;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.client.methods.HttpAsyncMethods;
+import org.apache.http.nio.client.util.HttpAsyncClientUtils;
+import org.apache.http.nio.entity.NByteArrayEntity;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.nio.protocol.BasicAsyncResponseConsumer;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.util.EntityUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TestHttpAsync extends HttpAsyncTestBase {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> protocols() {
+        return Arrays.asList(new Object[][]{
+                { ProtocolScheme.http },
+                { ProtocolScheme.https },
+        });
+    }
+
+    public TestHttpAsync(final ProtocolScheme scheme) {
+        super(scheme);
+    }
+
+    @Before @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        this.serverBootstrap.registerHandler("/echo/*", new BasicAsyncRequestHandler(new EchoHandler()));
+        this.serverBootstrap.registerHandler("/random/*", new BasicAsyncRequestHandler(new RandomHandler()));
+    }
+
+    @Test
+    public void testSingleGet() throws Exception {
+        final HttpHost target = start();
+        final HttpGet httpget = new HttpGet("/random/2048");
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+    }
+
+    @Test
+    public void testMultipleHead() throws Exception {
+        final HttpHost target = start();
+        for (int i = 0; i < 3; i++) {
+            final HttpHead httpHead = new HttpHead("/random/2048");
+            final Future<HttpResponse> future = this.httpclient.execute(target, httpHead, null);
+            final HttpResponse response = future.get();
+            Assert.assertNotNull(response);
+            Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+        }
+    }
+
+    @Test
+    public void testSinglePost() throws Exception {
+        final HttpHost target = start();
+        final byte[] b1 = new byte[1024];
+        final Random rnd = new Random(System.currentTimeMillis());
+        rnd.nextBytes(b1);
+
+        final HttpPost httppost = new HttpPost("/echo/stuff");
+        httppost.setEntity(new NByteArrayEntity(b1));
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httppost, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNotNull(entity);
+        final byte[] b2 = EntityUtils.toByteArray(entity);
+        Assert.assertArrayEquals(b1, b2);
+    }
+
+    @Test
+    public void testHttpAsyncMethods() throws Exception {
+        final HttpHost target = start();
+        final byte[] b1 = new byte[1024];
+        final Random rnd = new Random(System.currentTimeMillis());
+        rnd.nextBytes(b1);
+
+        final Future<HttpResponse> future = this.httpclient.execute(
+            HttpAsyncMethods.createPost(target + "/echo/post", b1, null),
+            new BasicAsyncResponseConsumer(),
+            null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNotNull(entity);
+        final byte[] b2 = EntityUtils.toByteArray(entity);
+        Assert.assertArrayEquals(b1, b2);
+    }
+
+    @Test
+    public void testMultiplePostsOverMultipleConnections() throws Exception {
+        final HttpHost target = start();
+        final byte[] b1 = new byte[1024];
+        final Random rnd = new Random(System.currentTimeMillis());
+        rnd.nextBytes(b1);
+
+        final int reqCount = 20;
+
+        this.connMgr.setDefaultMaxPerRoute(reqCount);
+        this.connMgr.setMaxTotal(100);
+
+        final Queue<Future<HttpResponse>> queue = new LinkedList<Future<HttpResponse>>();
+
+        for (int i = 0; i < reqCount; i++) {
+            final HttpPost httppost = new HttpPost("/echo/stuff");
+            httppost.setEntity(new NByteArrayEntity(b1));
+            queue.add(this.httpclient.execute(target, httppost, null));
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<HttpResponse> future = queue.remove();
+            final HttpResponse response = future.get();
+            Assert.assertNotNull(response);
+            Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+            final HttpEntity entity = response.getEntity();
+            Assert.assertNotNull(entity);
+            final byte[] b2 = EntityUtils.toByteArray(entity);
+            Assert.assertArrayEquals(b1, b2);
+        }
+    }
+
+    @Test
+    public void testMultiplePostsOverSingleConnection() throws Exception {
+        final HttpHost target = start();
+        final byte[] b1 = new byte[1024];
+        final Random rnd = new Random(System.currentTimeMillis());
+        rnd.nextBytes(b1);
+
+        final int reqCount = 20;
+
+        this.connMgr.setDefaultMaxPerRoute(1);
+        this.connMgr.setMaxTotal(100);
+
+        final Queue<Future<HttpResponse>> queue = new LinkedList<Future<HttpResponse>>();
+
+        for (int i = 0; i < reqCount; i++) {
+            final HttpPost httppost = new HttpPost("/echo/stuff");
+            httppost.setEntity(new NByteArrayEntity(b1));
+            queue.add(this.httpclient.execute(target, httppost, null));
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<HttpResponse> future = queue.remove();
+            final HttpResponse response = future.get();
+            Assert.assertNotNull(response);
+            Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+            final HttpEntity entity = response.getEntity();
+            Assert.assertNotNull(entity);
+            final byte[] b2 = EntityUtils.toByteArray(entity);
+            Assert.assertArrayEquals(b1, b2);
+        }
+    }
+
+    @Test
+    public void testRequestFailure() throws Exception {
+        final HttpHost target = start();
+        final HttpGet httpget = new HttpGet("/random/2048");
+        final HttpAsyncRequestProducer requestProducer = HttpAsyncMethods.create(target, httpget) ;
+        final BasicAsyncResponseConsumer responseConsumer = new BasicAsyncResponseConsumer() {
+
+            @Override
+            public void onContentReceived(final ContentDecoder decoder, final IOControl ioctrl)
+                    throws IOException {
+                throw new IOException("Kaboom");
+            }
+
+        };
+        final Future<HttpResponse> future = this.httpclient.execute(requestProducer, responseConsumer, null);
+        try {
+            future.get();
+            Assert.fail("ExecutionException expected");
+        } catch (final ExecutionException ex) {
+            final Throwable t = ex.getCause();
+            Assert.assertNotNull(t);
+            Assert.assertTrue(t instanceof IOException);
+            Assert.assertEquals("Kaboom", t.getMessage());
+        }
+    }
+
+    @Test
+    public void testSharedPool() throws Exception {
+        final HttpHost target = start();
+        final HttpGet httpget = new HttpGet("/random/2048");
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+
+
+        final CloseableHttpAsyncClient httpclient2 = HttpAsyncClients.custom()
+                .setConnectionManager(this.connMgr)
+                .setConnectionManagerShared(true)
+                .build();
+        try {
+            httpclient2.start();
+            final HttpGet httpget2 = new HttpGet("/random/2048");
+            final Future<HttpResponse> future2 = httpclient2.execute(target, httpget2, null);
+            final HttpResponse response2 = future2.get();
+            Assert.assertNotNull(response2);
+            Assert.assertEquals(200, response2.getStatusLine().getStatusCode());
+
+        } finally {
+            httpclient2.close();
+        }
+
+        final HttpGet httpget3 = new HttpGet("/random/2048");
+        final Future<HttpResponse> future3 = this.httpclient.execute(target, httpget3, null);
+        final HttpResponse response3 = future3.get();
+        Assert.assertNotNull(response3);
+        Assert.assertEquals(200, response3.getStatusLine().getStatusCode());
+    }
+
+    @Test
+    public void testClientCloseloseQuietly() throws Exception {
+        final HttpHost target = start();
+        final HttpGet httpget = new HttpGet("/random/2048");
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+
+        HttpAsyncClientUtils.closeQuietly(this.httpclient);
+        // Close it twice
+        HttpAsyncClientUtils.closeQuietly(this.httpclient);
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsyncMinimal.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsyncMinimal.java
new file mode 100644
index 0000000..3993d10
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsyncMinimal.java
@@ -0,0 +1,192 @@
+/*
+ * ====================================================================
+ * 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.nio.client.integration;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.Random;
+import java.util.concurrent.Future;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.localserver.AbstractAsyncTest;
+import org.apache.http.localserver.EchoHandler;
+import org.apache.http.localserver.RandomHandler;
+import org.apache.http.nio.entity.NByteArrayEntity;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.util.EntityUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TestHttpAsyncMinimal extends AbstractAsyncTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> protocols() {
+        return Arrays.asList(new Object[][]{
+                {ProtocolScheme.http},
+                {ProtocolScheme.https},
+        });
+    }
+
+    protected CloseableHttpAsyncClient httpclient;
+
+    public TestHttpAsyncMinimal(final ProtocolScheme scheme) {
+        super(scheme);
+    }
+
+    @Before @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        this.serverBootstrap.registerHandler("/echo/*", new BasicAsyncRequestHandler(new EchoHandler()));
+        this.serverBootstrap.registerHandler("/random/*", new BasicAsyncRequestHandler(new RandomHandler()));
+
+        this.httpclient = HttpAsyncClients.createMinimal(this.connMgr);
+    }
+
+    @After @Override
+    public void shutDown() throws Exception {
+        if (this.httpclient != null) {
+            this.httpclient.close();
+        }
+        super.shutDown();
+    }
+
+    public HttpHost start() throws Exception {
+        final HttpHost serverEndpoint = startServer();
+
+        this.httpclient.start();
+
+        return serverEndpoint;
+    }
+
+    @Test
+    public void testSingleGet() throws Exception {
+        final HttpHost target = start();
+        final HttpGet httpget = new HttpGet("/random/2048");
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+    }
+
+    @Test
+    public void testSinglePost() throws Exception {
+        final HttpHost target = start();
+        final byte[] b1 = new byte[1024];
+        final Random rnd = new Random(System.currentTimeMillis());
+        rnd.nextBytes(b1);
+
+        final HttpPost httppost = new HttpPost("/echo/stuff");
+        httppost.setEntity(new NByteArrayEntity(b1));
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httppost, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNotNull(entity);
+        final byte[] b2 = EntityUtils.toByteArray(entity);
+        Assert.assertArrayEquals(b1, b2);
+    }
+
+    @Test
+    public void testMultiplePostsOverMultipleConnections() throws Exception {
+        final HttpHost target = start();
+        final byte[] b1 = new byte[1024];
+        final Random rnd = new Random(System.currentTimeMillis());
+        rnd.nextBytes(b1);
+
+        final int reqCount = 20;
+
+        this.connMgr.setDefaultMaxPerRoute(reqCount);
+        this.connMgr.setMaxTotal(100);
+
+        final Queue<Future<HttpResponse>> queue = new LinkedList<Future<HttpResponse>>();
+
+        for (int i = 0; i < reqCount; i++) {
+            final HttpPost httppost = new HttpPost("/echo/stuff");
+            httppost.setEntity(new NByteArrayEntity(b1));
+            queue.add(this.httpclient.execute(target, httppost, null));
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<HttpResponse> future = queue.remove();
+            final HttpResponse response = future.get();
+            Assert.assertNotNull(response);
+            Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+            final HttpEntity entity = response.getEntity();
+            Assert.assertNotNull(entity);
+            final byte[] b2 = EntityUtils.toByteArray(entity);
+            Assert.assertArrayEquals(b1, b2);
+        }
+    }
+
+    @Test
+    public void testMultiplePostsOverSingleConnection() throws Exception {
+        final HttpHost target = start();
+        final byte[] b1 = new byte[1024];
+        final Random rnd = new Random(System.currentTimeMillis());
+        rnd.nextBytes(b1);
+
+        final int reqCount = 20;
+
+        this.connMgr.setDefaultMaxPerRoute(1);
+        this.connMgr.setMaxTotal(100);
+
+        final Queue<Future<HttpResponse>> queue = new LinkedList<Future<HttpResponse>>();
+
+        for (int i = 0; i < reqCount; i++) {
+            final HttpPost httppost = new HttpPost("/echo/stuff");
+            httppost.setEntity(new NByteArrayEntity(b1));
+            queue.add(this.httpclient.execute(target, httppost, null));
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<HttpResponse> future = queue.remove();
+            final HttpResponse response = future.get();
+            Assert.assertNotNull(response);
+            Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+            final HttpEntity entity = response.getEntity();
+            Assert.assertNotNull(entity);
+            final byte[] b2 = EntityUtils.toByteArray(entity);
+            Assert.assertArrayEquals(b1, b2);
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsyncPipelining.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsyncPipelining.java
new file mode 100644
index 0000000..d3d7bb9
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsyncPipelining.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.nio.client.integration;
+
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.nio.client.CloseableHttpPipeliningClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.localserver.AbstractAsyncTest;
+import org.apache.http.localserver.EchoHandler;
+import org.apache.http.localserver.RandomHandler;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.util.EntityUtils;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Future;
+
+@RunWith(Parameterized.class)
+public class TestHttpAsyncPipelining extends AbstractAsyncTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> protocols() {
+        return Arrays.asList(new Object[][]{
+                {ProtocolScheme.http},
+                {ProtocolScheme.https},
+        });
+    }
+
+    protected CloseableHttpPipeliningClient httpclient;
+
+    public TestHttpAsyncPipelining(final ProtocolScheme scheme) {
+        super(scheme);
+    }
+
+    @Before @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        this.serverBootstrap.registerHandler("/echo/*", new BasicAsyncRequestHandler(new EchoHandler()));
+        this.serverBootstrap.registerHandler("/random/*", new BasicAsyncRequestHandler(new RandomHandler()));
+
+        this.httpclient = HttpAsyncClients.createPipelining(this.connMgr);
+    }
+
+    @After @Override
+    public void shutDown() throws Exception {
+        if (this.httpclient != null) {
+            this.httpclient.close();
+        }
+        super.shutDown();
+    }
+
+    public HttpHost start() throws Exception {
+        final HttpHost serverEndpoint = startServer();
+
+        this.connMgr.setDefaultMaxPerRoute(1);
+        this.httpclient.start();
+
+        return serverEndpoint;
+    }
+
+    @Test
+    public void testPipelinedGets() throws Exception {
+        final HttpHost target = start();
+
+        final Queue<Future<List<HttpResponse>>> queue = new ConcurrentLinkedQueue<Future<List<HttpResponse>>>();
+        for (int i = 0; i < 10; i++) {
+            final HttpRequest httpget1 = new HttpGet("/random/512");
+            final HttpRequest httpget2 = new HttpGet("/random/1024");
+            final HttpRequest httpget3 = new HttpGet("/random/2048");
+            queue.add(this.httpclient.execute(target, Arrays.asList(httpget1, httpget2, httpget3), null));
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<List<HttpResponse>> future = queue.remove();
+            final List<HttpResponse> responses = future.get();
+            Assert.assertNotNull(responses);
+            Assert.assertEquals(3, responses.size());
+            final HttpResponse response1 = responses.get(0);
+            Assert.assertEquals(200, response1.getStatusLine().getStatusCode());
+            final byte[] bytes1 = EntityUtils.toByteArray(response1.getEntity());
+            Assert.assertNotNull(bytes1);
+            Assert.assertEquals(512, bytes1.length);
+            final HttpResponse response2 = responses.get(1);
+            Assert.assertEquals(200, response2.getStatusLine().getStatusCode());
+            final byte[] bytes2 = EntityUtils.toByteArray(response2.getEntity());
+            Assert.assertNotNull(bytes2);
+            Assert.assertEquals(1024, bytes2.length);
+            final HttpResponse response3 = responses.get(2);
+            Assert.assertEquals(200, response3.getStatusLine().getStatusCode());
+            final byte[] bytes3 = EntityUtils.toByteArray(response3.getEntity());
+            Assert.assertNotNull(bytes3);
+            Assert.assertEquals(2048, bytes3.length);
+        }
+
+    }
+
+    @Test
+    public void testPipelinedPostsAndGets() throws Exception {
+        final HttpHost target = start();
+
+        final Queue<Future<List<HttpResponse>>> queue = new ConcurrentLinkedQueue<Future<List<HttpResponse>>>();
+        for (int i = 0; i < 10; i++) {
+            final HttpEntityEnclosingRequest httppost1 = new HttpPost("/echo/");
+            httppost1.setEntity(new StringEntity("this and that"));
+            final HttpRequest httpget2 = new HttpGet("/echo/");
+            final HttpEntityEnclosingRequest httppost3 = new HttpPost("/echo/");
+            httppost3.setEntity(new StringEntity("all sorts of things"));
+            queue.add(this.httpclient.execute(target, Arrays.asList(httppost1, httpget2, httppost3), null));
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<List<HttpResponse>> future = queue.remove();
+            final List<HttpResponse> responses = future.get();
+            Assert.assertNotNull(responses);
+            Assert.assertEquals(3, responses.size());
+            final HttpResponse response1 = responses.get(0);
+            Assert.assertEquals(200, response1.getStatusLine().getStatusCode());
+            final String s1 = EntityUtils.toString(response1.getEntity());
+            Assert.assertNotNull(s1);
+            Assert.assertEquals("this and that", s1);
+            final HttpResponse response2 = responses.get(1);
+            Assert.assertEquals(200, response2.getStatusLine().getStatusCode());
+            final String s2 = EntityUtils.toString(response2.getEntity());
+            Assert.assertNotNull(s2);
+            Assert.assertEquals("", s2);
+            final HttpResponse response3 = responses.get(2);
+            Assert.assertEquals(200, response3.getStatusLine().getStatusCode());
+            final String s3 = EntityUtils.toString(response3.getEntity());
+            Assert.assertNotNull(s3);
+            Assert.assertEquals("all sorts of things", s3);
+        }
+
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsyncPrematureTermination.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsyncPrematureTermination.java
new file mode 100644
index 0000000..2e32eca
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestHttpAsyncPrematureTermination.java
@@ -0,0 +1,401 @@
+/*
+ * ====================================================================
+ * 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.nio.client.integration;
+
+import java.io.IOException;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.http.HttpConnection;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.localserver.EchoHandler;
+import org.apache.http.localserver.HttpAsyncTestBase;
+import org.apache.http.localserver.RandomHandler;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.client.methods.HttpAsyncMethods;
+import org.apache.http.nio.entity.NStringEntity;
+import org.apache.http.nio.protocol.BasicAsyncRequestConsumer;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.nio.protocol.BasicAsyncRequestProducer;
+import org.apache.http.nio.protocol.BasicAsyncResponseProducer;
+import org.apache.http.nio.protocol.HttpAsyncExchange;
+import org.apache.http.nio.protocol.HttpAsyncRequestConsumer;
+import org.apache.http.nio.protocol.HttpAsyncRequestHandler;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpCoreContext;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class TestHttpAsyncPrematureTermination extends HttpAsyncTestBase {
+
+    @Test
+    public void testConnectionTerminatedProcessingRequest() throws Exception {
+        this.serverBootstrap.registerHandler("*", new HttpAsyncRequestHandler<HttpRequest>() {
+
+            @Override
+            public HttpAsyncRequestConsumer<HttpRequest> processRequest(
+                    final HttpRequest request,
+                    final HttpContext context) throws HttpException, IOException {
+                final HttpConnection conn = (HttpConnection) context.getAttribute(
+                        HttpCoreContext.HTTP_CONNECTION);
+                conn.shutdown();
+                return new BasicAsyncRequestConsumer();
+            }
+
+            @Override
+            public void handle(
+                    final HttpRequest request,
+                    final HttpAsyncExchange httpExchange,
+                    final HttpContext context) throws HttpException, IOException {
+                final HttpResponse response = httpExchange.getResponse();
+                response.setEntity(new NStringEntity("all is well", ContentType.TEXT_PLAIN));
+                httpExchange.submitResponse();
+            }
+
+        });
+
+        final HttpHost target = start();
+        final HttpGet httpget = new HttpGet("/");
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        final FutureCallback<HttpResponse> callback = new FutureCallback<HttpResponse>() {
+
+            @Override
+            public void cancelled() {
+                latch.countDown();
+            }
+
+            @Override
+            public void failed(final Exception ex) {
+                latch.countDown();
+            }
+
+            @Override
+            public void completed(final HttpResponse response) {
+                Assert.fail();
+            }
+
+        };
+
+        this.httpclient.execute(target, httpget, callback);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testConnectionTerminatedHandlingRequest() throws Exception {
+        this.serverBootstrap.registerHandler("*", new HttpAsyncRequestHandler<HttpRequest>() {
+
+            @Override
+            public HttpAsyncRequestConsumer<HttpRequest> processRequest(
+                    final HttpRequest request,
+                    final HttpContext context) throws HttpException, IOException {
+                return new BasicAsyncRequestConsumer();
+            }
+
+            @Override
+            public void handle(
+                    final HttpRequest request,
+                    final HttpAsyncExchange httpExchange,
+                    final HttpContext context) throws HttpException, IOException {
+                final HttpConnection conn = (HttpConnection) context.getAttribute(
+                        HttpCoreContext.HTTP_CONNECTION);
+                conn.shutdown();
+                final HttpResponse response = httpExchange.getResponse();
+                response.setEntity(new NStringEntity("all is well", ContentType.TEXT_PLAIN));
+                httpExchange.submitResponse();
+            }
+
+        });
+
+        final HttpHost target = start();
+        final HttpGet httpget = new HttpGet("/");
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        final FutureCallback<HttpResponse> callback = new FutureCallback<HttpResponse>() {
+
+            @Override
+            public void cancelled() {
+                latch.countDown();
+            }
+
+            @Override
+            public void failed(final Exception ex) {
+                latch.countDown();
+            }
+
+            @Override
+            public void completed(final HttpResponse response) {
+                Assert.fail();
+            }
+
+        };
+
+        this.httpclient.execute(target, httpget, callback);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void testConnectionTerminatedSendingResponse() throws Exception {
+        this.serverBootstrap.registerHandler("*", new HttpAsyncRequestHandler<HttpRequest>() {
+
+            @Override
+            public HttpAsyncRequestConsumer<HttpRequest> processRequest(
+                    final HttpRequest request,
+                    final HttpContext context) throws HttpException, IOException {
+                return new BasicAsyncRequestConsumer();
+            }
+
+            @Override
+            public void handle(
+                    final HttpRequest request,
+                    final HttpAsyncExchange httpExchange,
+                    final HttpContext context) throws HttpException, IOException {
+                final HttpResponse response = httpExchange.getResponse();
+                response.setEntity(new NStringEntity("all is well", ContentType.TEXT_PLAIN));
+                httpExchange.submitResponse(new BasicAsyncResponseProducer(response) {
+
+                    @Override
+                    public synchronized void produceContent(
+                            final ContentEncoder encoder,
+                            final IOControl ioctrl) throws IOException {
+                        ioctrl.shutdown();
+                    }
+
+                });
+            }
+
+        });
+
+        final HttpHost target = start();
+        final HttpGet httpget = new HttpGet("/");
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        final FutureCallback<HttpResponse> callback = new FutureCallback<HttpResponse>() {
+
+            @Override
+            public void cancelled() {
+                latch.countDown();
+            }
+
+            @Override
+            public void failed(final Exception ex) {
+                latch.countDown();
+            }
+
+            @Override
+            public void completed(final HttpResponse response) {
+                Assert.fail();
+            }
+
+        };
+
+        this.httpclient.execute(target, httpget, callback);
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+
+    @Test @Ignore(value = "Fails on some Windows platforms")
+    public void testConnectionRequestFailure() throws Exception {
+        this.httpclient = HttpAsyncClients.custom()
+                .setConnectionManager(this.connMgr)
+                .build();
+        this.httpclient.start();
+
+        final HttpGet get = new HttpGet("http://0.0.0.0/");
+        final HttpAsyncRequestProducer producer = HttpAsyncMethods.create(get);
+
+        final AtomicBoolean closed = new AtomicBoolean(false);
+        final AtomicBoolean cancelled = new AtomicBoolean(false);
+        final AtomicBoolean failed = new AtomicBoolean(false);
+
+        final HttpAsyncResponseConsumer<?> consumer = new HttpAsyncResponseConsumer<Object>() {
+
+            @Override
+            public void close() throws IOException {
+                closed.set(true);
+            }
+
+            @Override
+            public boolean cancel() {
+                cancelled.set(true);
+                return false;
+            }
+
+            @Override
+            public void failed(final Exception ex) {
+                failed.set(true);
+            }
+
+            @Override
+            public void responseReceived(
+                    final HttpResponse response) throws IOException, HttpException {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public void consumeContent(
+                    final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public void responseCompleted(final HttpContext context) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public Exception getException() {
+                return null;
+            }
+
+            @Override
+            public String getResult() {
+                return null;
+            }
+
+            @Override
+            public boolean isDone() {
+                return false;
+            }
+        };
+
+        final Future<?> future = this.httpclient.execute(producer, consumer, null, null);
+        try {
+            future.get();
+            Assert.fail();
+        } catch (final ExecutionException e) {
+            final Throwable cause = e.getCause();
+            Assert.assertTrue("Unexpected cause: " + cause, cause instanceof IOException);
+        }
+        this.connMgr.shutdown(1000);
+
+        Assert.assertTrue(closed.get());
+        Assert.assertFalse(cancelled.get());
+        Assert.assertTrue(failed.get());
+    }
+
+    @Test
+    public void testConsumerIsDone() throws Exception {
+        this.serverBootstrap.registerHandler("/echo/*", new BasicAsyncRequestHandler(new EchoHandler()));
+        this.serverBootstrap.registerHandler("/random/*", new BasicAsyncRequestHandler(new RandomHandler()));
+
+        final HttpHost target = start();
+
+        final AtomicInteger producerClosed = new AtomicInteger(0);
+        final AtomicInteger consumerClosed = new AtomicInteger(0);
+
+        final HttpAsyncRequestProducer producer = new BasicAsyncRequestProducer(target, new HttpGet("/")) {
+
+            @Override
+            public synchronized void close() throws IOException {
+                producerClosed.incrementAndGet();
+                super.close();
+            }
+        };
+
+        final HttpAsyncResponseConsumer<?> consumer = new HttpAsyncResponseConsumer<Object>() {
+
+            @Override
+            public void close() throws IOException {
+                consumerClosed.incrementAndGet();
+            }
+
+            @Override
+            public boolean cancel() {
+                return false;
+            }
+
+            @Override
+            public void failed(final Exception ex) {
+            }
+
+            @Override
+            public void responseReceived(
+                    final HttpResponse response) throws IOException, HttpException {
+            }
+
+            @Override
+            public void consumeContent(
+                    final ContentDecoder decoder, final IOControl ioctrl) throws IOException {
+            }
+
+            @Override
+            public void responseCompleted(final HttpContext context) {
+            }
+
+            @Override
+            public Exception getException() {
+                return null;
+            }
+
+            @Override
+            public String getResult() {
+                return null;
+            }
+
+            @Override
+            public boolean isDone() {
+                return true; // cancels fetching the response-body
+            }
+        };
+
+        final Future<?> future = this.httpclient.execute(producer, consumer, null, null);
+        try {
+            future.get();
+            Assert.fail("CancellationException expected");
+        } catch (final CancellationException expected) {
+        }
+
+        connMgr.shutdown(1000);
+
+        Assert.assertTrue(future.isCancelled());
+        Assert.assertTrue(future.isCancelled());
+
+        Assert.assertEquals(1, producerClosed.get());
+        Assert.assertEquals(1, consumerClosed.get());
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestRedirects.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestRedirects.java
new file mode 100644
index 0000000..d5a68e0
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestRedirects.java
@@ -0,0 +1,939 @@
+/*
+ * ====================================================================
+ * 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.nio.client.integration;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.http.Header;
+import org.apache.http.impl.client.DefaultRedirectStrategy;
+import org.apache.http.localserver.HttpAsyncTestBase;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpInetConnection;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolException;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.client.CircularRedirectException;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.RedirectException;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.client.utils.URIBuilder;
+import org.apache.http.cookie.SM;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicCookieStore;
+import org.apache.http.impl.cookie.BasicClientCookie;
+import org.apache.http.impl.nio.bootstrap.HttpServer;
+import org.apache.http.localserver.RandomHandler;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.nio.entity.NStringEntity;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.nio.reactor.ListenerEndpoint;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpCoreContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/**
+ * Redirection test cases.
+ */
+@RunWith(Parameterized.class)
+public class TestRedirects extends HttpAsyncTestBase {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> protocols() {
+        return Arrays.asList(new Object[][]{
+                {ProtocolScheme.http},
+                {ProtocolScheme.https},
+        });
+    }
+
+    public TestRedirects(final ProtocolScheme scheme) {
+        super(scheme);
+    }
+
+    static class BasicRedirectService implements HttpRequestHandler {
+
+        private final String schemeName;
+        private final int statuscode;
+
+        public BasicRedirectService(final String schemeName, final int statuscode) {
+            super();
+            this.schemeName = schemeName;
+            this.statuscode = statuscode;
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final HttpInetConnection conn = (HttpInetConnection) context.getAttribute(
+                    HttpCoreContext.HTTP_CONNECTION);
+            final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+            final String uri = request.getRequestLine().getUri();
+            if (uri.equals("/oldlocation/")) {
+                final String redirectUrl = this.schemeName + "://localhost:" + conn.getLocalPort() + "/newlocation/";
+                response.setStatusLine(ver, this.statuscode);
+                response.addHeader(new BasicHeader("Location", redirectUrl));
+                response.addHeader(new BasicHeader("Connection", "close"));
+            } else if (uri.equals("/newlocation/")) {
+                response.setStatusLine(ver, HttpStatus.SC_OK);
+                final StringEntity entity = new StringEntity("Successful redirect");
+                response.setEntity(entity);
+            } else {
+                response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
+            }
+        }
+    }
+
+    static class CircularRedirectService implements HttpRequestHandler {
+
+        public CircularRedirectService() {
+            super();
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+            final String uri = request.getRequestLine().getUri();
+            if (uri.startsWith("/circular-oldlocation")) {
+                response.setStatusLine(ver, HttpStatus.SC_MOVED_TEMPORARILY);
+                response.addHeader(new BasicHeader("Location", "/circular-location2"));
+            } else if (uri.startsWith("/circular-location2")) {
+                response.setStatusLine(ver, HttpStatus.SC_MOVED_TEMPORARILY);
+                response.addHeader(new BasicHeader("Location", "/circular-oldlocation"));
+            } else {
+                response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
+            }
+        }
+    }
+
+    static class RelativeRedirectService implements HttpRequestHandler {
+
+        public RelativeRedirectService() {
+            super();
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+            final String uri = request.getRequestLine().getUri();
+            if (uri.equals("/oldlocation/")) {
+                response.setStatusLine(ver, HttpStatus.SC_MOVED_TEMPORARILY);
+                response.addHeader(new BasicHeader("Location", "/relativelocation/"));
+            } else if (uri.equals("/relativelocation/")) {
+                response.setStatusLine(ver, HttpStatus.SC_OK);
+                final StringEntity entity = new StringEntity("Successful redirect");
+                response.setEntity(entity);
+            } else {
+                response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
+            }
+        }
+    }
+
+    static class RelativeRedirectService2 implements HttpRequestHandler {
+
+        public RelativeRedirectService2() {
+            super();
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+            final String uri = request.getRequestLine().getUri();
+            if (uri.equals("/test/oldlocation")) {
+                response.setStatusLine(ver, HttpStatus.SC_MOVED_TEMPORARILY);
+                response.addHeader(new BasicHeader("Location", "relativelocation"));
+            } else if (uri.equals("/test/relativelocation")) {
+                response.setStatusLine(ver, HttpStatus.SC_OK);
+                final StringEntity entity = new StringEntity("Successful redirect");
+                response.setEntity(entity);
+            } else {
+                response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
+            }
+        }
+    }
+
+    static class BogusRedirectService implements HttpRequestHandler {
+
+        private final String schemeName;
+        private final String url;
+        private final boolean absolute;
+
+        public BogusRedirectService(final String schemeName, final String url, final boolean absolute) {
+            super();
+            this.schemeName = schemeName;
+            this.url = url;
+            this.absolute = absolute;
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final HttpInetConnection conn = (HttpInetConnection) context.getAttribute(
+                    HttpCoreContext.HTTP_CONNECTION);
+            String redirectUrl = this.url;
+            if (!this.absolute) {
+                redirectUrl = this.schemeName + "://localhost:" + conn.getLocalPort() + redirectUrl;
+            }
+
+            final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+            final String uri = request.getRequestLine().getUri();
+            if (uri.equals("/oldlocation/")) {
+                response.setStatusLine(ver, HttpStatus.SC_MOVED_TEMPORARILY);
+                response.addHeader(new BasicHeader("Location", redirectUrl));
+            } else if (uri.equals("/relativelocation/")) {
+                response.setStatusLine(ver, HttpStatus.SC_OK);
+                final StringEntity entity = new StringEntity("Successful redirect");
+                response.setEntity(entity);
+            } else {
+                response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
+            }
+        }
+    }
+
+    private static class RomeRedirectService implements HttpRequestHandler {
+
+        public RomeRedirectService() {
+            super();
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final String uri = request.getRequestLine().getUri();
+            if (uri.equals("/rome")) {
+                response.setStatusCode(HttpStatus.SC_OK);
+                final StringEntity entity = new StringEntity("Successful redirect");
+                response.setEntity(entity);
+            } else {
+                response.setStatusCode(HttpStatus.SC_MOVED_TEMPORARILY);
+                response.addHeader(new BasicHeader("Location", "/rome"));
+            }
+        }
+    }
+
+    private static class DifferentHostRedirectService implements HttpRequestHandler {
+
+        private final String schemeName;
+        private final int statusCode;
+        private int targetHostPort;
+
+        public DifferentHostRedirectService(final String schemeName, final int statusCode) {
+            this.schemeName = schemeName;
+            this.statusCode = statusCode;
+        }
+
+        @Override
+        public void handle(final HttpRequest request, final HttpResponse response,
+            final HttpContext context) throws HttpException, IOException {
+
+            final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+            final String uri = request.getRequestLine().getUri();
+            if (uri.equals("/oldlocation/")) {
+                final String redirectUrl =
+                    this.schemeName + "://localhost:" + targetHostPort + "/newlocation/";
+                response.setStatusLine(ver, this.statusCode);
+                response.addHeader(new BasicHeader("Location", redirectUrl));
+                response.addHeader(new BasicHeader("Connection", "close"));
+            } else if (uri.equals("/newlocation/")) {
+                final String hostHeaderValue = request.getFirstHeader("Host").getValue();
+
+                if (hostHeaderValue.equals("localhost:" + targetHostPort)) {
+                    response.setStatusLine(ver, HttpStatus.SC_OK);
+                    final StringEntity entity = new StringEntity("Successful redirect");
+                    response.setEntity(entity);
+                } else {
+                    response.setStatusLine(ver, 421, "Misdirected Request");
+                }
+            } else {
+                response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
+            }
+        }
+
+        public void setTargetHostPort(final int targetHostPort) {
+            this.targetHostPort = targetHostPort;
+        }
+    }
+
+    @Test
+    public void testBasicRedirect300() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_MULTIPLE_CHOICES)));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_MULTIPLE_CHOICES, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/oldlocation/", reqWrapper.getRequestLine().getUri());
+    }
+
+    @Test
+    public void testBasicRedirect301() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_MOVED_PERMANENTLY)));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(target, host);
+    }
+
+    @Test
+    public void testBasicRedirect302() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_MOVED_TEMPORARILY)));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(target, host);
+    }
+
+    @Test
+    public void testBasicRedirect302NoLocation() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new HttpRequestHandler() {
+
+            @Override
+            public void handle(
+                    final HttpRequest request,
+                    final HttpResponse response,
+                    final HttpContext context) throws HttpException, IOException {
+                response.setStatusCode(HttpStatus.SC_MOVED_TEMPORARILY);
+            }
+
+        }));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/oldlocation/", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(target, host);
+    }
+
+    @Test
+    public void testBasicRedirect303() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_SEE_OTHER)));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(target, host);
+    }
+
+    @Test
+    public void testBasicRedirect304() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_NOT_MODIFIED)));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/oldlocation/", reqWrapper.getRequestLine().getUri());
+    }
+
+    @Test
+    public void testBasicRedirect305() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_USE_PROXY)));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_USE_PROXY, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/oldlocation/", reqWrapper.getRequestLine().getUri());
+    }
+
+    @Test
+    public void testBasicRedirect307() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_TEMPORARY_REDIRECT)));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(target, host);
+    }
+
+    @Test(expected=ExecutionException.class)
+    public void testMaxRedirectCheck() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new CircularRedirectService()));
+        final HttpHost target = start();
+
+        final RequestConfig config = RequestConfig.custom()
+                .setCircularRedirectsAllowed(true)
+                .setMaxRedirects(5).build();
+
+        final HttpGet httpget = new HttpGet("/circular-oldlocation/");
+        httpget.setConfig(config);
+        try {
+            final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+            future.get();
+        } catch (final ExecutionException e) {
+            Assert.assertTrue(e.getCause() instanceof RedirectException);
+            throw e;
+        }
+    }
+
+    @Test(expected=ExecutionException.class)
+    public void testCircularRedirect() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new CircularRedirectService()));
+        final HttpHost target = start();
+
+        final RequestConfig config = RequestConfig.custom()
+                .setCircularRedirectsAllowed(false)
+                .setRelativeRedirectsAllowed(true)
+                .build();
+
+        final HttpGet httpget = new HttpGet("/circular-oldlocation/");
+        httpget.setConfig(config);
+        try {
+            final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+            future.get();
+        } catch (final ExecutionException e) {
+            Assert.assertTrue(e.getCause() instanceof CircularRedirectException);
+            throw e;
+        }
+    }
+
+    @Test
+    public void testPostNoRedirect() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_MOVED_TEMPORARILY)));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpPost httppost = new HttpPost("/oldlocation/");
+        httppost.setEntity(new NStringEntity("stuff"));
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httppost, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/oldlocation/", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals("POST", reqWrapper.getRequestLine().getMethod());
+    }
+
+    @Test
+    public void testPostRedirectSeeOther() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_SEE_OTHER)));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpPost httppost = new HttpPost("/oldlocation/");
+        httppost.setEntity(new NStringEntity("stuff"));
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httppost, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals("GET", reqWrapper.getRequestLine().getMethod());
+    }
+
+    @Test
+    public void testRelativeRedirect() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new RelativeRedirectService()));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final RequestConfig config = RequestConfig.custom()
+                .setRelativeRedirectsAllowed(true)
+                .build();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+        httpget.setConfig(config);
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/relativelocation/", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(target, host);
+    }
+
+    @Test
+    public void testRelativeRedirect2() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new RelativeRedirectService2()));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final RequestConfig config = RequestConfig.custom()
+                .setRelativeRedirectsAllowed(true)
+                .build();
+
+        final HttpGet httpget = new HttpGet("/test/oldlocation");
+        httpget.setConfig(config);
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/test/relativelocation", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(target, host);
+    }
+
+    @Test(expected=ExecutionException.class)
+    public void testRejectRelativeRedirect() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new RelativeRedirectService()));
+        final HttpHost target = start();
+
+        final RequestConfig config = RequestConfig.custom()
+                .setRelativeRedirectsAllowed(false)
+                .build();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+        httpget.setConfig(config);
+        try {
+            final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+            future.get();
+        } catch (final ExecutionException e) {
+            Assert.assertTrue(e.getCause() instanceof ProtocolException);
+            throw e;
+        }
+    }
+
+    @Test(expected=ExecutionException.class)
+    public void testRejectBogusRedirectLocation() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BogusRedirectService(getSchemeName(), "xxx://bogus", true)));
+        final HttpHost target = start();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        try {
+            final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+            future.get();
+        } catch (final ExecutionException ex) {
+            Assert.assertTrue(ex.getCause() instanceof HttpException);
+            throw ex;
+        }
+    }
+
+    @Test(expected=ExecutionException.class)
+    public void testRejectInvalidRedirectLocation() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BogusRedirectService(getSchemeName(), "/newlocation/?p=I have spaces", false)));
+        final HttpHost target = start();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+        try {
+            final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+            future.get();
+        } catch (final ExecutionException e) {
+            Assert.assertTrue(e.getCause() instanceof ProtocolException);
+            throw e;
+        }
+    }
+
+    @Test
+    public void testRedirectWithCookie() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_MOVED_TEMPORARILY)));
+        final HttpHost target = start();
+
+        final CookieStore cookieStore = new BasicCookieStore();
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCookieStore(cookieStore);
+
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        cookie.setDomain(target.getHostName());
+        cookie.setPath("/");
+
+        cookieStore.addCookie(cookie);
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
+
+        final Header[] headers = reqWrapper.getHeaders(SM.COOKIE);
+        Assert.assertEquals("There can only be one (cookie)", 1, headers.length);
+    }
+
+    @Test
+    public void testDefaultHeadersRedirect() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(
+                new BasicRedirectService(getSchemeName(), HttpStatus.SC_MOVED_TEMPORARILY)));
+
+        final List<Header> defaultHeaders = new ArrayList<Header>(1);
+        defaultHeaders.add(new BasicHeader(HTTP.USER_AGENT, "my-test-client"));
+        this.clientBuilder.setDefaultHeaders(defaultHeaders);
+
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpGet httpget = new HttpGet("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
+
+        final Header header = reqWrapper.getFirstHeader(HTTP.USER_AGENT);
+        Assert.assertEquals("my-test-client", header.getValue());
+    }
+
+    static class CrossSiteRedirectService implements HttpRequestHandler {
+
+        private final HttpHost host;
+
+        public CrossSiteRedirectService(final HttpHost host) {
+            super();
+            this.host = host;
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
+            final String location;
+            try {
+                final URIBuilder uribuilder = new URIBuilder(request.getRequestLine().getUri());
+                uribuilder.setScheme(this.host.getSchemeName());
+                uribuilder.setHost(this.host.getHostName());
+                uribuilder.setPort(this.host.getPort());
+                uribuilder.setPath("/random/1024");
+                location = uribuilder.build().toASCIIString();
+            } catch (final URISyntaxException ex) {
+                throw new ProtocolException("Invalid request URI", ex);
+            }
+            response.setStatusLine(ver, HttpStatus.SC_TEMPORARY_REDIRECT);
+            response.addHeader(new BasicHeader("Location", location));
+        }
+    }
+
+    @Test
+    public void testCrossSiteRedirect() throws Exception {
+        this.serverBootstrap.registerHandler("/random/*", new BasicAsyncRequestHandler(
+                new RandomHandler()));
+        final HttpHost redirectTarget = start();
+
+        this.serverBootstrap.registerHandler("/redirect/*", new BasicAsyncRequestHandler(
+                new CrossSiteRedirectService(redirectTarget)));
+
+        final HttpServer secondServer = this.serverBootstrap.create();
+        try {
+            secondServer.start();
+            final ListenerEndpoint endpoint2 = secondServer.getEndpoint();
+            endpoint2.waitFor();
+
+            final InetSocketAddress address2 = (InetSocketAddress) endpoint2.getAddress();
+            final HttpHost initialTarget = new HttpHost("localhost", address2.getPort(), getSchemeName());
+
+            final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
+            for (int i = 0; i < 4; i++) {
+                final HttpClientContext context = HttpClientContext.create();
+                final HttpGet httpget = new HttpGet("/redirect/anywhere");
+                queue.add(this.httpclient.execute(initialTarget, httpget, context, null));
+            }
+            while (!queue.isEmpty()) {
+                final Future<HttpResponse> future = queue.remove();
+                final HttpResponse response = future.get();
+                Assert.assertNotNull(response);
+                Assert.assertEquals(200, response.getStatusLine().getStatusCode());
+            }
+        } finally {
+            this.server.shutdown(10, TimeUnit.SECONDS);
+        }
+    }
+
+    @Test
+    public void testRepeatRequest() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new RomeRedirectService()));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final RequestConfig config = RequestConfig.custom().setRelativeRedirectsAllowed(true).build();
+        final HttpGet first = new HttpGet("/rome");
+        first.setConfig(config);
+
+        final Future<HttpResponse> future1 = this.httpclient.execute(target, first, context, null);
+        final HttpResponse response1 = future1.get();
+        Assert.assertNotNull(response1);
+
+        final HttpGet second = new HttpGet("/rome");
+        second.setConfig(config);
+
+        final Future<HttpResponse> future2 = this.httpclient.execute(target, second, context, null);
+        final HttpResponse response2 = future2.get();
+        Assert.assertNotNull(response2);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
+        Assert.assertEquals("/rome", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(host, target);
+    }
+
+    @Test
+    public void testRepeatRequestRedirect() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new RomeRedirectService()));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final RequestConfig config = RequestConfig.custom().setRelativeRedirectsAllowed(true).build();
+        final HttpGet first = new HttpGet("/lille");
+        first.setConfig(config);
+
+        final Future<HttpResponse> future1 = this.httpclient.execute(target, first, context, null);
+        final HttpResponse response1 = future1.get();
+        Assert.assertNotNull(response1);
+
+        final HttpGet second = new HttpGet("/lille");
+        second.setConfig(config);
+
+        final Future<HttpResponse> future2 = this.httpclient.execute(target, second, context, null);
+        final HttpResponse response2 = future2.get();
+        Assert.assertNotNull(response2);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
+        Assert.assertEquals("/rome", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(host, target);
+    }
+
+    @Test
+    public void testDifferentRequestSameRedirect() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new RomeRedirectService()));
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final RequestConfig config = RequestConfig.custom().setRelativeRedirectsAllowed(true).build();
+        final HttpGet first = new HttpGet("/alian");
+        first.setConfig(config);
+
+        final Future<HttpResponse> future1 = this.httpclient.execute(target, first, context, null);
+        final HttpResponse response1 = future1.get();
+        Assert.assertNotNull(response1);
+
+        final HttpGet second = new HttpGet("/lille");
+        second.setConfig(config);
+
+        final Future<HttpResponse> future2 = this.httpclient.execute(target, second, context, null);
+        final HttpResponse response2 = future2.get();
+        Assert.assertNotNull(response2);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
+        Assert.assertEquals("/rome", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(host, target);
+    }
+
+    @Test
+    public void testPostRedirectWithDifferentHost() throws Exception {
+        // do redirect for post requests
+        this.clientBuilder.setRedirectStrategy(new DefaultRedirectStrategy() {
+            @Override
+            public boolean isRedirected(final HttpRequest request, final HttpResponse response,
+                final HttpContext context)
+                throws ProtocolException {
+                // allow 307 redirect for all methods
+                return super.isRedirected(request, response, context)
+                    || response.getStatusLine().getStatusCode() == HttpStatus.SC_TEMPORARY_REDIRECT;
+            }
+        });
+
+        final DifferentHostRedirectService differentHostRequestHandler = new DifferentHostRedirectService(
+            getSchemeName(), HttpStatus.SC_TEMPORARY_REDIRECT);
+
+        this.serverBootstrap.registerHandler("*",
+            new BasicAsyncRequestHandler(differentHostRequestHandler));
+        final HttpHost originalHost = start(); // to start the original host and build the client
+        final HttpHost targetHost = startServer(); // to start the target host
+
+        differentHostRequestHandler.setTargetHostPort(targetHost.getPort());
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final HttpPost httpPost = new HttpPost("/oldlocation/");
+
+        final Future<HttpResponse> future = this.httpclient.execute(originalHost, httpPost, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest reqWrapper = context.getRequest();
+        final HttpHost host = context.getTargetHost();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+        Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
+        Assert.assertEquals(targetHost, host);
+    }
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestStatefulConnManagement.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestStatefulConnManagement.java
new file mode 100644
index 0000000..5020073
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestStatefulConnManagement.java
@@ -0,0 +1,286 @@
+/*
+ * ====================================================================
+ * 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.nio.client.integration;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Future;
+
+import org.apache.http.localserver.HttpAsyncTestBase;
+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;
+import org.apache.http.client.UserTokenHandler;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.nio.conn.CPoolUtils;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.NHttpClientConnection;
+import org.apache.http.nio.client.HttpAsyncClient;
+import org.apache.http.nio.entity.NStringEntity;
+import org.apache.http.nio.protocol.AbstractAsyncResponseConsumer;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.nio.protocol.BasicAsyncRequestProducer;
+import org.apache.http.nio.reactor.IOEventDispatch;
+import org.apache.http.pool.PoolEntry;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestStatefulConnManagement extends HttpAsyncTestBase {
+
+    static class SimpleService implements HttpRequestHandler {
+
+        public SimpleService() {
+            super();
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            response.setStatusCode(HttpStatus.SC_OK);
+            final NStringEntity entity = new NStringEntity("Whatever");
+            response.setEntity(entity);
+        }
+    }
+
+    @Test
+    public void testStatefulConnections() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new SimpleService()));
+
+        final UserTokenHandler userTokenHandler = new UserTokenHandler() {
+
+            @Override
+            public Object getUserToken(final HttpContext context) {
+                return context.getAttribute("user");
+            }
+
+        };
+        this.clientBuilder.setUserTokenHandler(userTokenHandler);
+        final HttpHost target = start();
+
+        final int workerCount = 2;
+        final int requestCount = 5;
+
+        final HttpContext[] contexts = new HttpContext[workerCount];
+        final HttpWorker[] workers = new HttpWorker[workerCount];
+        for (int i = 0; i < contexts.length; i++) {
+            final HttpContext context = new BasicHttpContext();
+            final Object token = Integer.valueOf(i);
+            context.setAttribute("user", token);
+            contexts[i] = context;
+            workers[i] = new HttpWorker(context, requestCount, target, this.httpclient);
+        }
+
+        for (final HttpWorker worker : workers) {
+            worker.start();
+        }
+        for (final HttpWorker worker : workers) {
+            worker.join(10000);
+        }
+        for (final HttpWorker worker : workers) {
+            final Exception ex = worker.getException();
+            if (ex != null) {
+                throw ex;
+            }
+            Assert.assertEquals(requestCount, worker.getCount());
+        }
+
+        for (final HttpContext context : contexts) {
+            final Integer id = (Integer) context.getAttribute("user");
+
+            for (int r = 1; r < requestCount; r++) {
+                final Integer state = (Integer) context.getAttribute("r" + r);
+                Assert.assertEquals(id, state);
+            }
+        }
+
+    }
+
+    static class HttpWorker extends Thread {
+
+        private final HttpContext context;
+        private final int requestCount;
+        private final HttpHost target;
+        private final HttpAsyncClient httpclient;
+
+        private volatile Exception exception;
+        private volatile int count;
+
+        public HttpWorker(
+                final HttpContext context,
+                final int requestCount,
+                final HttpHost target,
+                final HttpAsyncClient httpclient) {
+            super();
+            this.context = context;
+            this.requestCount = requestCount;
+            this.target = target;
+            this.httpclient = httpclient;
+            this.count = 0;
+        }
+
+        public int getCount() {
+            return this.count;
+        }
+
+        public Exception getException() {
+            return this.exception;
+        }
+
+        @Override
+        public void run() {
+            try {
+                for (int r = 0; r < this.requestCount; r++) {
+                    final HttpGet httpget = new HttpGet("/");
+                    final Future<Object> future = this.httpclient.execute(
+                            new BasicAsyncRequestProducer(this.target, httpget),
+                            new AbstractAsyncResponseConsumer<Object>() {
+
+                                @Override
+                                protected void onResponseReceived(final HttpResponse response) {
+                                }
+
+                                @Override
+                                protected void onEntityEnclosed(
+                                        final HttpEntity entity,
+                                        final ContentType contentType) throws IOException {
+                                }
+
+                                @Override
+                                protected void onContentReceived(
+                                        final ContentDecoder decoder,
+                                        final IOControl ioctrl) throws IOException {
+                                    final ByteBuffer buf = ByteBuffer.allocate(2048);
+                                    decoder.read(buf);
+                                }
+
+                                @Override
+                                protected Object buildResult(final HttpContext context) throws Exception {
+                                    final NHttpClientConnection conn = (NHttpClientConnection) context.getAttribute(
+                                            IOEventDispatch.CONNECTION_KEY);
+
+                                    final PoolEntry<?, ?> entry = CPoolUtils.getPoolEntry(conn);
+                                    return entry.getState();
+                                }
+
+                                @Override
+                                protected void releaseResources() {
+                                }
+
+                            },
+                            this.context,
+                            null);
+                    this.count++;
+                    final Object state = future.get();
+                    this.context.setAttribute("r" + r, state);
+                }
+
+            } catch (final Exception ex) {
+                this.exception = ex;
+            }
+        }
+
+    }
+
+    @Test
+    public void testRouteSpecificPoolRecylcing() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new SimpleService()));
+        // This tests what happens when a maxed connection pool needs
+        // to kill the last idle connection to a route to build a new
+        // one to the same route.
+        final UserTokenHandler userTokenHandler = new UserTokenHandler() {
+
+            @Override
+            public Object getUserToken(final HttpContext context) {
+                return context.getAttribute("user");
+            }
+
+        };
+        this.clientBuilder.setUserTokenHandler(userTokenHandler);
+
+        final HttpHost target = start();
+        final int maxConn = 2;
+        // We build a client with 2 max active // connections, and 2 max per route.
+        this.connMgr.setMaxTotal(maxConn);
+        this.connMgr.setDefaultMaxPerRoute(maxConn);
+
+        // Bottom of the pool : a *keep alive* connection to Route 1.
+        final HttpContext context1 = new BasicHttpContext();
+        context1.setAttribute("user", "stuff");
+
+        final Future<HttpResponse> future1 = this.httpclient.execute(
+                target, new HttpGet("/"), context1, null);
+        final HttpResponse response1 = future1.get();
+        Assert.assertNotNull(response1);
+        Assert.assertEquals(200, response1.getStatusLine().getStatusCode());
+
+        // The ConnPoolByRoute now has 1 free connection, out of 2 max
+        // The ConnPoolByRoute has one RouteSpcfcPool, that has one free connection
+        // for [localhost][stuff]
+
+        Thread.sleep(100);
+
+        // Send a very simple HTTP get (it MUST be simple, no auth, no proxy, no 302, no 401, ...)
+        // Send it to another route. Must be a keepalive.
+        final HttpContext context2 = new BasicHttpContext();
+
+        final Future<HttpResponse> future2 = this.httpclient.execute(
+                new HttpHost("127.0.0.1", target.getPort(), target.getSchemeName()),
+                new HttpGet("/"), context2, null);
+        final HttpResponse response2 = future2.get();
+        Assert.assertNotNull(response2);
+        Assert.assertEquals(200, response2.getStatusLine().getStatusCode());
+
+        // ConnPoolByRoute now has 2 free connexions, out of its 2 max.
+        // The [localhost][stuff] RouteSpcfcPool is the same as earlier
+        // And there is a [127.0.0.1][null] pool with 1 free connection
+
+        Thread.sleep(100);
+
+        // This will put the ConnPoolByRoute to the targeted state :
+        // [localhost][stuff] will not get reused because this call is [localhost][null]
+        // So the ConnPoolByRoute will need to kill one connection (it is maxed out globally).
+        // The killed conn is the oldest, which means the first HTTPGet ([localhost][stuff]).
+        // When this happens, the RouteSpecificPool becomes empty.
+        final HttpContext context3 = new BasicHttpContext();
+        final Future<HttpResponse> future3 = this.httpclient.execute(
+                target, new HttpGet("/"), context3, null);
+        final HttpResponse response3 = future3.get();
+        Assert.assertNotNull(response3);
+        Assert.assertEquals(200, response3.getStatusLine().getStatusCode());
+    }
+
+}
\ No newline at end of file
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/methods/TestAsyncConsumers.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/methods/TestAsyncConsumers.java
new file mode 100644
index 0000000..ea31612
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/methods/TestAsyncConsumers.java
@@ -0,0 +1,258 @@
+/*
+ * ====================================================================
+ * 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.nio.client.methods;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.http.Consts;
+import org.apache.http.localserver.HttpAsyncTestBase;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.entity.ContentType;
+import org.apache.http.localserver.EchoHandler;
+import org.apache.http.localserver.RandomHandler;
+import org.apache.http.nio.IOControl;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
+import org.apache.http.protocol.HttpContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TestAsyncConsumers extends HttpAsyncTestBase {
+
+    @Before @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        this.serverBootstrap.registerHandler("/echo/*", new BasicAsyncRequestHandler(new EchoHandler()));
+        this.serverBootstrap.registerHandler("/random/*", new BasicAsyncRequestHandler(new RandomHandler()));
+    }
+
+    static class ByteCountingConsumer extends AsyncByteConsumer<Long> {
+
+        public ByteCountingConsumer() {
+            super();
+        }
+
+        public ByteCountingConsumer(final int bufSize) {
+            super(bufSize);
+        }
+
+        private final AtomicLong count = new AtomicLong(0);
+
+        @Override
+        protected void onResponseReceived(final HttpResponse response) {
+        }
+
+        @Override
+        protected void onByteReceived(final ByteBuffer buf, final IOControl ioctrl) {
+            this.count.addAndGet(buf.remaining());
+        }
+
+        @Override
+        protected Long buildResult(final HttpContext context) throws Exception {
+            return count.get();
+        }
+
+    }
+
+    @Test
+    public void testByteConsumer() throws Exception {
+        final HttpHost target = start();
+        for (int i = 0; i < 5; i++) {
+            final HttpAsyncRequestProducer httpget = HttpAsyncMethods.createGet(target.toURI() + "/random/20480");
+            final AsyncByteConsumer<Long> consumer = new ByteCountingConsumer();
+            final Future<Long> future = this.httpclient.execute(httpget, consumer, null);
+            final Long count = future.get();
+            Assert.assertEquals(20480, count.longValue());
+        }
+    }
+
+    @Test
+    public void testByteConsumerSmallBufffer() throws Exception {
+        final HttpHost target = start();
+        for (int i = 0; i < 5; i++) {
+            final HttpAsyncRequestProducer httpget = HttpAsyncMethods.createGet(target.toURI() + "/random/20480");
+            final AsyncByteConsumer<Long> consumer = new ByteCountingConsumer(512);
+            final Future<Long> future = this.httpclient.execute(httpget, consumer, null);
+            final Long count = future.get();
+            Assert.assertEquals(20480, count.longValue());
+        }
+    }
+
+    static class BufferingCharConsumer extends AsyncCharConsumer<String> {
+
+        public BufferingCharConsumer() {
+            super();
+        }
+
+        public BufferingCharConsumer(final int bufSize) {
+            super(bufSize);
+        }
+
+        private final StringBuilder sb = new StringBuilder();
+
+        @Override
+        public void onResponseReceived(final HttpResponse response) {
+        }
+
+        @Override
+        protected void onCharReceived(final CharBuffer buf, final IOControl ioctrl) throws IOException {
+            while (buf.hasRemaining()) {
+                this.sb.append(buf.get());
+            }
+        }
+
+        @Override
+        protected void releaseResources() {
+            super.releaseResources();
+            this.sb.setLength(0);
+        }
+
+        @Override
+        protected String buildResult(final HttpContext context) throws Exception {
+            return this.sb.toString();
+        }
+
+    }
+
+    @Test
+    public void testCharConsumer() throws Exception {
+        final HttpHost target = start();
+        final StringBuilder sb = new StringBuilder();
+        for (int i= 0; i < 25; i++) {
+            sb.append("blah blah blah blah\r\n");
+            sb.append("yada yada yada yada\r\n");
+        }
+        final String s = sb.toString();
+
+        for (int i = 0; i < 5; i++) {
+            final HttpAsyncRequestProducer httppost = HttpAsyncMethods.createPost(
+                    target.toURI() + "/echo/stuff", s,
+                    ContentType.create("text/plain", Consts.ASCII));
+            final AsyncCharConsumer<String> consumer = new BufferingCharConsumer();
+            final Future<String> future = this.httpclient.execute(httppost, consumer, null);
+            final String result = future.get();
+            Assert.assertEquals(s, result);
+        }
+    }
+
+    @Test
+    public void testCharConsumerSmallBufffer() throws Exception {
+        final HttpHost target = start();
+        final StringBuilder sb = new StringBuilder();
+        for (int i= 0; i < 25; i++) {
+            sb.append("blah blah blah blah\r\n");
+            sb.append("yada yada yada yada\r\n");
+        }
+        final String s = sb.toString();
+
+        for (int i = 0; i < 5; i++) {
+            final HttpAsyncRequestProducer httppost = HttpAsyncMethods.createPost(
+                    target.toURI() + "/echo/stuff", s,
+                    ContentType.create("text/plain", Consts.ASCII));
+            final AsyncCharConsumer<String> consumer = new BufferingCharConsumer(512);
+            final Future<String> future = this.httpclient.execute(httppost, consumer, null);
+            final String result = future.get();
+            Assert.assertEquals(s, result);
+        }
+    }
+
+    @Test
+    public void testResourceReleaseOnSuccess() throws Exception {
+        final HttpHost target = start();
+        final StringBuilder sb = new StringBuilder();
+        for (int i= 0; i < 25; i++) {
+            sb.append("blah blah blah blah\r\n");
+            sb.append("yada yada yada yada\r\n");
+        }
+        final String s = sb.toString();
+
+        final HttpAsyncRequestProducer httppost = HttpAsyncMethods.createPost(
+                target.toURI() + "/echo/stuff", s,
+                ContentType.create("text/plain", Consts.ASCII));
+        final BufferingCharConsumer consumer = Mockito.spy(new BufferingCharConsumer());
+        final Future<String> future = this.httpclient.execute(httppost, consumer, null);
+        final String result = future.get();
+        Assert.assertEquals(s, result);
+        Mockito.verify(consumer).buildResult(Mockito.any(HttpContext.class));
+        Mockito.verify(consumer).releaseResources();
+    }
+
+    @Test
+    public void testResourceReleaseOnException() throws Exception {
+        final HttpHost target = start();
+        final HttpAsyncRequestProducer httppost = HttpAsyncMethods.createPost(
+                target.toURI() + "/echo/stuff", "stuff",
+                ContentType.create("text/plain", Consts.ASCII));
+        final AsyncCharConsumer<String> consumer = Mockito.spy(new BufferingCharConsumer());
+        Mockito.doThrow(new IOException("Kaboom")).when(consumer).onCharReceived(
+                Mockito.any(CharBuffer.class), Mockito.any(IOControl.class));
+
+        final Future<String> future = this.httpclient.execute(httppost, consumer, null);
+        try {
+            future.get();
+            Assert.fail("ExecutionException expected");
+        } catch (final ExecutionException ex) {
+            final Throwable t = ex.getCause();
+            Assert.assertNotNull(t);
+            Assert.assertTrue(t instanceof IOException);
+            Assert.assertEquals("Kaboom", t.getMessage());
+        }
+        Mockito.verify(consumer).releaseResources();
+    }
+
+    @Test
+    public void testResourceReleaseOnBuildFailure() throws Exception {
+        final HttpHost target = start();
+        final HttpAsyncRequestProducer httppost = HttpAsyncMethods.createPost(
+                target.toURI() + "/echo/stuff", "stuff",
+                ContentType.create("text/plain", Consts.ASCII));
+        final BufferingCharConsumer consumer = Mockito.spy(new BufferingCharConsumer());
+        Mockito.doThrow(new HttpException("Kaboom")).when(consumer).buildResult(Mockito.any(HttpContext.class));
+
+        final Future<String> future = this.httpclient.execute(httppost, consumer, null);
+        try {
+            future.get();
+            Assert.fail("ExecutionException expected");
+        } catch (final ExecutionException ex) {
+            final Throwable t = ex.getCause();
+            Assert.assertNotNull(t);
+            Assert.assertTrue(t instanceof HttpException);
+            Assert.assertEquals("Kaboom", t.getMessage());
+        }
+        Mockito.verify(consumer).releaseResources();
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/methods/TestZeroCopy.java b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/methods/TestZeroCopy.java
new file mode 100644
index 0000000..7b7bb31
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/java/org/apache/http/nio/client/methods/TestZeroCopy.java
@@ -0,0 +1,269 @@
+/*
+ * ====================================================================
+ * 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.nio.client.methods;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.concurrent.Future;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.LineIterator;
+import org.apache.commons.io.output.FileWriterWithEncoding;
+import org.apache.http.Consts;
+import org.apache.http.localserver.HttpAsyncTestBase;
+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.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.BasicHttpEntity;
+import org.apache.http.entity.ContentType;
+import org.apache.http.nio.entity.NFileEntity;
+import org.apache.http.nio.entity.NStringEntity;
+import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestZeroCopy extends HttpAsyncTestBase {
+
+    private static final String[] TEXT = {
+        "blah blah blah blah blah blah blah blah blah blah blah blah blah blah",
+        "yada yada yada yada yada yada yada yada yada yada yada yada yada yada",
+        "da da da da da da da da da da da da da da da da da da da da da da da da",
+        "nyet nyet nyet nyet nyet nyet nyet nyet nyet nyet nyet nyet nyet nyet"
+    };
+
+    private static final Charset ASCII = Charset.forName("ascii");
+    private static File TEST_FILE;
+    private File tmpfile;
+
+    @BeforeClass
+    public static void createSrcFile() throws Exception {
+        final File tmpdir = FileUtils.getTempDirectory();
+        TEST_FILE = new File(tmpdir, "src.test");
+        final FileWriterWithEncoding out = new FileWriterWithEncoding(TEST_FILE, ASCII);
+        try {
+            for (int i = 0; i < 500; i++) {
+                for (final String line: TEXT) {
+                    out.write(line);
+                    out.write("\r\n");
+                }
+            }
+        } finally {
+            out.close();
+        }
+    }
+
+    @AfterClass
+    public static void deleteSrcFile() throws Exception {
+        if (TEST_FILE != null) {
+            TEST_FILE.delete();
+            TEST_FILE = null;
+        }
+    }
+
+    @After
+    public void cleanUp() throws Exception {
+        if (this.tmpfile != null && this.tmpfile.exists()) {
+            this.tmpfile.delete();
+        }
+    }
+
+    static class TestZeroCopyPost extends BaseZeroCopyRequestProducer {
+
+        private final boolean forceChunking;
+
+        protected TestZeroCopyPost(
+                final String requestURI,
+                final boolean forceChunking) throws FileNotFoundException {
+            super(URI.create(requestURI), TEST_FILE, ContentType.create("text/plain"));
+            this.forceChunking = forceChunking;
+        }
+
+        @Override
+        protected HttpEntityEnclosingRequest createRequest(final URI requestURI, final HttpEntity entity) {
+            final HttpPost httppost = new HttpPost(requestURI);
+            if (this.forceChunking) {
+                final BasicHttpEntity chunkedEntity = new BasicHttpEntity();
+                chunkedEntity.setChunked(true);
+                httppost.setEntity(chunkedEntity);
+            } else {
+                httppost.setEntity(entity);
+            }
+            return httppost;
+        }
+
+    }
+
+    static class TestZeroCopyConsumer extends ZeroCopyConsumer<Integer> {
+
+        public TestZeroCopyConsumer(final File file) throws FileNotFoundException {
+            super(file);
+        }
+
+        @Override
+        protected Integer process(
+                final HttpResponse response,
+                final File file,
+                final ContentType contentType) {
+            return response.getStatusLine().getStatusCode();
+        }
+
+    }
+
+    static class TestHandler implements HttpRequestHandler {
+
+        private final boolean forceChunking;
+
+        TestHandler(final boolean forceChunking) {
+            super();
+            this.forceChunking = forceChunking;
+        }
+
+        @Override
+        public void handle(
+                final HttpRequest request,
+                final HttpResponse response,
+                final HttpContext context) throws HttpException, IOException {
+            HttpEntity requestEntity = null;
+            if (request instanceof HttpEntityEnclosingRequest) {
+                requestEntity = ((HttpEntityEnclosingRequest) request).getEntity();
+            }
+            if (requestEntity == null) {
+                response.setEntity(new NStringEntity("Empty content"));
+                return;
+            }
+
+            boolean ok = true;
+
+            final InputStream instream = requestEntity.getContent();
+            try {
+                final ContentType contentType = ContentType.getOrDefault(requestEntity);
+                Charset charset = contentType.getCharset();
+                if (charset == null) {
+                    charset = Consts.ISO_8859_1;
+                }
+                final LineIterator it = IOUtils.lineIterator(instream, charset.name());
+                int count = 0;
+                while (it.hasNext()) {
+                    final String line = it.next();
+                    final int i = count % TEXT.length;
+                    final String expected = TEXT[i];
+                    if (!line.equals(expected)) {
+                        ok = false;
+                        break;
+                    }
+                    count++;
+                }
+            } finally {
+                instream.close();
+            }
+            if (ok) {
+                final NFileEntity responseEntity = new NFileEntity(TEST_FILE,
+                        ContentType.create("text/plian"));
+                if (this.forceChunking) {
+                    responseEntity.setChunked(true);
+                }
+                response.setEntity(responseEntity);
+            } else {
+                response.setEntity(new NStringEntity("Invalid content"));
+            }
+        }
+    }
+
+    @Test
+    public void testTwoWayZeroCopy() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new TestHandler(false)));
+        final HttpHost target = start();
+
+        final File tmpdir = FileUtils.getTempDirectory();
+        this.tmpfile = new File(tmpdir, "dst.test");
+        final TestZeroCopyPost httppost = new TestZeroCopyPost(target.toURI() + "/bounce", false);
+        final TestZeroCopyConsumer consumer = new TestZeroCopyConsumer(this.tmpfile);
+        final Future<Integer> future = this.httpclient.execute(httppost, consumer, null);
+        final Integer status = future.get();
+        Assert.assertNotNull(status);
+        Assert.assertEquals(HttpStatus.SC_OK, status.intValue());
+        final InputStream instream = new FileInputStream(this.tmpfile);
+        try {
+            final LineIterator it = IOUtils.lineIterator(instream, ASCII.name());
+            int count = 0;
+            while (it.hasNext()) {
+                final String line = it.next();
+                final int i = count % TEXT.length;
+                final String expected = TEXT[i];
+                Assert.assertEquals(expected, line);
+                count++;
+            }
+        } finally {
+            instream.close();
+        }
+    }
+
+    @Test
+    public void testZeroCopyFallback() throws Exception {
+        this.serverBootstrap.registerHandler("*", new BasicAsyncRequestHandler(new TestHandler(true)));
+        final HttpHost target = start();
+        final File tmpdir = FileUtils.getTempDirectory();
+        this.tmpfile = new File(tmpdir, "dst.test");
+        final TestZeroCopyPost httppost = new TestZeroCopyPost(target.toURI() + "/bounce", true);
+        final TestZeroCopyConsumer consumer = new TestZeroCopyConsumer(this.tmpfile);
+        final Future<Integer> future = this.httpclient.execute(httppost, consumer, null);
+        final Integer status = future.get();
+        Assert.assertNotNull(status);
+        Assert.assertEquals(HttpStatus.SC_OK, status.intValue());
+        final InputStream instream = new FileInputStream(this.tmpfile);
+        try {
+            final LineIterator it = IOUtils.lineIterator(instream, ASCII.name());
+            int count = 0;
+            while (it.hasNext()) {
+                final String line = it.next();
+                final int i = count % TEXT.length;
+                final String expected = TEXT[i];
+                Assert.assertEquals(expected, line);
+                count++;
+            }
+        } finally {
+            instream.close();
+        }
+    }
+
+}
diff --git a/4.1.x/httpasyncclient/src/test/resources/commons-logging.properties b/4.1.x/httpasyncclient/src/test/resources/commons-logging.properties
new file mode 100644
index 0000000..2fa1b3b
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/resources/commons-logging.properties
@@ -0,0 +1,26 @@
+# ====================================================================
+# 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/>.
+
+# Disable logging for unit tests
+org.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog
diff --git a/4.1.x/httpasyncclient/src/test/resources/test-DSA-1024.keystore b/4.1.x/httpasyncclient/src/test/resources/test-DSA-1024.keystore
new file mode 100644
index 0000000..917f83e
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/resources/test-DSA-1024.keystore
Binary files differ
diff --git a/4.1.x/httpasyncclient/src/test/resources/test.keystore b/4.1.x/httpasyncclient/src/test/resources/test.keystore
new file mode 100644
index 0000000..8234a3d
--- /dev/null
+++ b/4.1.x/httpasyncclient/src/test/resources/test.keystore
Binary files differ
diff --git a/4.1.x/pom.xml b/4.1.x/pom.xml
new file mode 100644
index 0000000..8504075
--- /dev/null
+++ b/4.1.x/pom.xml
@@ -0,0 +1,313 @@
+<?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">
+  <parent>
+      <artifactId>project</artifactId>
+      <groupId>org.apache.httpcomponents</groupId>
+      <version>7</version>
+      <relativePath>../project/pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+  <artifactId>httpcomponents-asyncclient</artifactId>
+  <name>Apache HttpComponents AsyncClient</name>
+  <version>4.1.4</version>
+  <description>Apache components to build asynchronous client side HTTP services</description>
+  <url>http://hc.apache.org/httpcomponents-asyncclient</url>
+  <inceptionYear>2010</inceptionYear>
+  <packaging>pom</packaging>
+
+  <organization>
+    <name>The Apache Software Foundation</name>
+    <url>http://www.apache.org/</url>
+  </organization>
+
+  <licenses>
+    <license>
+      <name>Apache License, Version 2.0</name>
+      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+
+  <issueManagement>
+    <system>Jira</system>
+    <url>http://issues.apache.org/jira/browse/HTTPASYNC</url>
+  </issueManagement>
+
+  <scm>
+    <connection>scm:svn:https://svn.apache.org/repos/asf/httpcomponents/httpasyncclient/branches/4.1.x</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/httpcomponents/httpasyncclient/branches/4.1.x</developerConnection>
+    <url>https://svn.apache.org/repos/asf/httpcomponents/httpasyncclient/branches/4.1.x</url>
+  </scm>
+
+  <properties>
+    <maven.compiler.source>1.6</maven.compiler.source>
+    <maven.compiler.target>1.6</maven.compiler.target>
+    <httpcore.version>4.4.10</httpcore.version>
+    <httpclient.version>4.5.6</httpclient.version>
+    <commons-logging.version>1.2</commons-logging.version>
+    <commons-io.version>2.4</commons-io.version>
+    <junit.version>4.11</junit.version>
+    <easymock.version>2.5.2</easymock.version>
+    <mockito.version>1.8.5</mockito.version>
+    <hc.stylecheck.version>1</hc.stylecheck.version>
+    <api.comparison.version>4.0</api.comparison.version>
+  </properties>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpcore</artifactId>
+        <version>${httpcore.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpcore-nio</artifactId>
+        <version>${httpcore.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpclient</artifactId>
+        <version>${httpclient.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpclient-cache</artifactId>
+        <version>${httpclient.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>commons-logging</groupId>
+        <artifactId>commons-logging</artifactId>
+        <version>${commons-logging.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>
+      <dependency>
+        <groupId>commons-io</groupId>
+        <artifactId>commons-io</artifactId>
+        <version>${commons-io.version}</version>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.apache.httpcomponents</groupId>
+        <artifactId>httpclient-cache</artifactId>
+        <version>${httpclient.version}</version>
+        <type>test-jar</type>
+        <scope>test</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+
+  <modules>
+    <module>httpasyncclient</module>
+    <module>httpasyncclient-cache</module>
+    <module>httpasyncclient-osgi</module>
+  </modules>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestEntries>
+              <Specification-Title>HttpComponents ${project.name}</Specification-Title>
+              <Specification-Version>${project.version}</Specification-Version>
+              <Specification-Vendor>The Apache Software Foundation</Specification-Vendor>
+              <Implementation-Title>HttpComponents ${project.name}</Implementation-Title>
+              <Implementation-Version>${project.version}</Implementation-Version>
+              <Implementation-Vendor>The Apache Software Foundation</Implementation-Vendor>
+              <Implementation-Vendor-Id>org.apache</Implementation-Vendor-Id>
+              <url>${project.url}</url>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
+      <plugin>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-javadoc-plugin</artifactId>
+        <configuration>
+          <source>${maven.compiler.source}</source>
+          <links>
+            <link>http://docs.oracle.com/javase/6/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>
+      </plugin>
+      <plugin>
+        <artifactId>maven-site-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <version>2.9.1</version>
+        <dependencies>
+          <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>hc-stylecheck</artifactId>
+            <version>${hc.stylecheck.version}</version>
+          </dependency>
+        </dependencies>
+        <configuration>
+          <encoding>UTF-8</encoding>
+        </configuration>
+        <executions>
+          <execution>
+            <id>validate-main</id>
+            <phase>validate</phase>
+            <configuration>
+              <configLocation>hc-stylecheck/default.xml</configLocation>
+              <headerLocation>hc-stylecheck/asl2.header</headerLocation>
+              <consoleOutput>true</consoleOutput>
+              <failsOnError>true</failsOnError>
+              <linkXRef>false</linkXRef>
+              <sourceDirectory>${basedir}/src/main</sourceDirectory>
+            </configuration>
+            <goals>
+              <goal>checkstyle</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>validate-test</id>
+            <phase>validate</phase>
+            <configuration>
+              <configLocation>hc-stylecheck/default.xml</configLocation>
+              <headerLocation>hc-stylecheck/asl2.header</headerLocation>
+              <consoleOutput>true</consoleOutput>
+              <failsOnError>true</failsOnError>
+              <linkXRef>false</linkXRef>
+              <sourceDirectory>${basedir}/src/test</sourceDirectory>
+            </configuration>
+            <goals>
+              <goal>checkstyle</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>validate-examples</id>
+            <phase>validate</phase>
+            <configuration>
+              <configLocation>hc-stylecheck/minimal.xml</configLocation>
+              <headerLocation>hc-stylecheck/asl2.header</headerLocation>
+              <consoleOutput>true</consoleOutput>
+              <failsOnError>true</failsOnError>
+              <linkXRef>false</linkXRef>
+              <sourceDirectory>${basedir}/src/examples</sourceDirectory>
+            </configuration>
+            <goals>
+              <goal>checkstyle</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>clirr-maven-plugin</artifactId>
+        <version>${hc.clirr.version}</version>
+        <configuration>
+          <comparisonVersion>${api.comparison.version}</comparisonVersion>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+        <version>0.12</version>
+        <executions>
+          <execution>
+            <phase>verify</phase>
+            <goals>
+              <goal>check</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <excludes>
+            <exclude>src/test/resources/*.truststore</exclude>
+            <!-- Eclipse file not in the source repository -->
+            <exclude>**/.checkstyle</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <reporting>
+    <plugins>
+
+      <plugin>
+        <artifactId>maven-project-info-reports-plugin</artifactId>
+        <version>${hc.project-info.version}</version>
+        <inherited>false</inherited>
+        <reportSets>
+          <reportSet>
+            <reports>
+              <report>dependency-management</report>
+              <report>issue-tracking</report>
+              <report>license</report>
+              <report>scm</report>
+              <report>summary</report>
+            </reports>
+          </reportSet>
+        </reportSets>
+      </plugin>
+
+    </plugins>
+  </reporting>
+
+</project>