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>